Merge "Revert "Infra: Revert LUCI changes for Mac-arm64""
diff --git a/Android.bp b/Android.bp
index fbf6d49..f3ebaa9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -925,7 +925,7 @@
         ":perfetto_src_ipc_client",
         ":perfetto_src_ipc_common",
         ":perfetto_src_perfetto_cmd_perfetto_cmd",
-        ":perfetto_src_perfetto_cmd_protos_gen",
+        ":perfetto_src_perfetto_cmd_protos_cpp_gen",
         ":perfetto_src_perfetto_cmd_trigger_producer",
         ":perfetto_src_protozero_protozero",
         ":perfetto_src_tracing_common",
@@ -986,7 +986,7 @@
         "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
         "perfetto_src_perfetto_cmd_gen_cc_config_descriptor",
-        "perfetto_src_perfetto_cmd_protos_gen_headers",
+        "perfetto_src_perfetto_cmd_protos_cpp_gen_headers",
     ],
     defaults: [
         "perfetto_defaults",
@@ -2921,9 +2921,9 @@
     ],
 }
 
-// GN: //protos/perfetto/config:perfetto_config_descriptor
+// GN: //protos/perfetto/config:merged_config_descriptor
 genrule {
-    name: "perfetto_protos_perfetto_config_perfetto_config_descriptor",
+    name: "perfetto_protos_perfetto_config_merged_config_descriptor",
     srcs: [
         "protos/perfetto/config/perfetto_config.proto",
     ],
@@ -2932,7 +2932,7 @@
     ],
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
     out: [
-        "perfetto_protos_perfetto_config_perfetto_config_descriptor.bin",
+        "perfetto_protos_perfetto_config_merged_config_descriptor.bin",
     ],
 }
 
@@ -4143,6 +4143,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -4364,6 +4365,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -4419,6 +4421,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/clk.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",
         "external/perfetto/protos/perfetto/trace/ftrace/dmabuf_heap.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/dpu.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ext4.gen.cc",
@@ -4474,6 +4477,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -4529,6 +4533,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/clk.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",
         "external/perfetto/protos/perfetto/trace/ftrace/dmabuf_heap.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/dpu.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ext4.gen.h",
@@ -4588,6 +4593,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -4642,6 +4648,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/clk.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",
         "external/perfetto/protos/perfetto/trace/ftrace/dmabuf_heap.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/dpu.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ext4.pb.cc",
@@ -4697,6 +4704,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -4751,6 +4759,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/clk.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",
         "external/perfetto/protos/perfetto/trace/ftrace/dmabuf_heap.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/dpu.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ext4.pb.h",
@@ -4810,6 +4819,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -4865,6 +4875,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/clk.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",
         "external/perfetto/protos/perfetto/trace/ftrace/dmabuf_heap.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/dpu.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ext4.pbzero.cc",
@@ -4920,6 +4931,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -4975,6 +4987,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/clk.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",
         "external/perfetto/protos/perfetto/trace/ftrace/dmabuf_heap.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/dpu.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ext4.pbzero.h",
@@ -6536,6 +6549,44 @@
     ],
 }
 
+// GN: //protos/perfetto/trace/track_event:descriptor
+genrule {
+    name: "perfetto_protos_perfetto_trace_track_event_descriptor",
+    srcs: [
+        "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",
+        "protos/perfetto/trace/track_event/chrome_frame_reporter.proto",
+        "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
+        "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+        "protos/perfetto/trace/track_event/chrome_latency_info.proto",
+        "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
+        "protos/perfetto/trace/track_event/chrome_message_pump.proto",
+        "protos/perfetto/trace/track_event/chrome_mojo_event_info.proto",
+        "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
+        "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto",
+        "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
+        "protos/perfetto/trace/track_event/chrome_user_event.proto",
+        "protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto",
+        "protos/perfetto/trace/track_event/counter_descriptor.proto",
+        "protos/perfetto/trace/track_event/debug_annotation.proto",
+        "protos/perfetto/trace/track_event/log_message.proto",
+        "protos/perfetto/trace/track_event/process_descriptor.proto",
+        "protos/perfetto/trace/track_event/source_location.proto",
+        "protos/perfetto/trace/track_event/task_execution.proto",
+        "protos/perfetto/trace/track_event/thread_descriptor.proto",
+        "protos/perfetto/trace/track_event/track_descriptor.proto",
+        "protos/perfetto/trace/track_event/track_event.proto",
+    ],
+    tools: [
+        "aprotoc",
+    ],
+    cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
+    out: [
+        "perfetto_protos_perfetto_trace_track_event_descriptor.bin",
+    ],
+}
+
 // GN: //protos/perfetto/trace/track_event:lite
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_lite_gen",
@@ -6662,44 +6713,6 @@
     ],
 }
 
-// GN: //protos/perfetto/trace/track_event:track_event_descriptor
-genrule {
-    name: "perfetto_protos_perfetto_trace_track_event_track_event_descriptor",
-    srcs: [
-        "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",
-        "protos/perfetto/trace/track_event/chrome_frame_reporter.proto",
-        "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
-        "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
-        "protos/perfetto/trace/track_event/chrome_latency_info.proto",
-        "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
-        "protos/perfetto/trace/track_event/chrome_message_pump.proto",
-        "protos/perfetto/trace/track_event/chrome_mojo_event_info.proto",
-        "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
-        "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto",
-        "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
-        "protos/perfetto/trace/track_event/chrome_user_event.proto",
-        "protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto",
-        "protos/perfetto/trace/track_event/counter_descriptor.proto",
-        "protos/perfetto/trace/track_event/debug_annotation.proto",
-        "protos/perfetto/trace/track_event/log_message.proto",
-        "protos/perfetto/trace/track_event/process_descriptor.proto",
-        "protos/perfetto/trace/track_event/source_location.proto",
-        "protos/perfetto/trace/track_event/task_execution.proto",
-        "protos/perfetto/trace/track_event/thread_descriptor.proto",
-        "protos/perfetto/trace/track_event/track_descriptor.proto",
-        "protos/perfetto/trace/track_event/track_event.proto",
-    ],
-    tools: [
-        "aprotoc",
-    ],
-    cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
-    out: [
-        "perfetto_protos_perfetto_trace_track_event_track_event_descriptor.bin",
-    ],
-}
-
 // GN: //protos/perfetto/trace/track_event:zero
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_zero_gen",
@@ -6828,9 +6841,9 @@
     ],
 }
 
-// GN: //protos/third_party/chromium:chrome_track_event_descriptor
+// GN: //protos/third_party/chromium:descriptor
 genrule {
-    name: "perfetto_protos_third_party_chromium_chrome_track_event_descriptor",
+    name: "perfetto_protos_third_party_chromium_descriptor",
     srcs: [
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
@@ -6863,7 +6876,7 @@
     ],
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
     out: [
-        "perfetto_protos_third_party_chromium_chrome_track_event_descriptor.bin",
+        "perfetto_protos_third_party_chromium_descriptor.bin",
     ],
 }
 
@@ -7208,7 +7221,7 @@
 genrule {
     name: "perfetto_src_perfetto_cmd_gen_cc_config_descriptor",
     srcs: [
-        ":perfetto_protos_perfetto_config_perfetto_config_descriptor",
+        ":perfetto_protos_perfetto_config_merged_config_descriptor",
     ],
     cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
     out: [
@@ -7232,9 +7245,9 @@
     ],
 }
 
-// GN: //src/perfetto_cmd:protos
+// GN: //src/perfetto_cmd:protos_cpp
 genrule {
-    name: "perfetto_src_perfetto_cmd_protos_gen",
+    name: "perfetto_src_perfetto_cmd_protos_cpp_gen",
     srcs: [
         "src/perfetto_cmd/perfetto_cmd_state.proto",
     ],
@@ -7248,9 +7261,9 @@
     ],
 }
 
-// GN: //src/perfetto_cmd:protos
+// GN: //src/perfetto_cmd:protos_cpp
 genrule {
-    name: "perfetto_src_perfetto_cmd_protos_gen_headers",
+    name: "perfetto_src_perfetto_cmd_protos_cpp_gen_headers",
     srcs: [
         "src/perfetto_cmd/perfetto_cmd_state.proto",
     ],
@@ -7989,7 +8002,7 @@
 genrule {
     name: "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
     srcs: [
-        ":perfetto_protos_third_party_chromium_chrome_track_event_descriptor",
+        ":perfetto_protos_third_party_chromium_descriptor",
     ],
     cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
     out: [
@@ -8019,7 +8032,7 @@
 genrule {
     name: "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
     srcs: [
-        ":perfetto_protos_perfetto_trace_track_event_track_event_descriptor",
+        ":perfetto_protos_perfetto_trace_track_event_descriptor",
     ],
     cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
     out: [
@@ -8146,6 +8159,7 @@
         "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_thread_time_in_state.sql",
         "src/trace_processor/metrics/sql/android/android_trace_quality.sql",
@@ -8332,6 +8346,7 @@
         "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",
@@ -8417,6 +8432,7 @@
         "src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc",
         "src/trace_processor/dynamic/thread_state_generator_unittest.cc",
         "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/fuchsia/fuchsia_trace_utils_unittest.cc",
         "src/trace_processor/importers/memory_tracker/graph_processor_unittest.cc",
@@ -8477,6 +8493,7 @@
     name: "perfetto_src_trace_processor_util_unittests",
     srcs: [
         "src/trace_processor/util/debug_annotation_parser_unittest.cc",
+        "src/trace_processor/util/gzip_utils_unittest.cc",
         "src/trace_processor/util/proto_to_args_parser_unittest.cc",
         "src/trace_processor/util/protozero_to_text_unittests.cc",
     ],
@@ -9166,7 +9183,6 @@
     name: "perfetto_tools_trace_to_text_full",
     srcs: [
         "tools/trace_to_text/proto_full_utils.cc",
-        "tools/trace_to_text/trace_to_text.cc",
     ],
 }
 
@@ -9193,6 +9209,22 @@
     ],
 }
 
+// GN: //tools/trace_to_text:trace_to_text_lib
+filegroup {
+    name: "perfetto_tools_trace_to_text_trace_to_text_lib",
+    srcs: [
+        "tools/trace_to_text/trace_to_text.cc",
+    ],
+}
+
+// GN: //tools/trace_to_text:unittests
+filegroup {
+    name: "perfetto_tools_trace_to_text_unittests",
+    srcs: [
+        "tools/trace_to_text/trace_to_text_unittest.cc",
+    ],
+}
+
 // GN: //tools/trace_to_text:utils
 filegroup {
     name: "perfetto_tools_trace_to_text_utils",
@@ -9438,7 +9470,7 @@
         ":perfetto_src_kallsyms_kallsyms",
         ":perfetto_src_kallsyms_unittests",
         ":perfetto_src_perfetto_cmd_perfetto_cmd",
-        ":perfetto_src_perfetto_cmd_protos_gen",
+        ":perfetto_src_perfetto_cmd_protos_cpp_gen",
         ":perfetto_src_perfetto_cmd_trigger_producer",
         ":perfetto_src_perfetto_cmd_unittests",
         ":perfetto_src_profiling_common_callstack_trie",
@@ -9463,6 +9495,7 @@
         ":perfetto_src_profiling_perf_producer_unittests",
         ":perfetto_src_profiling_perf_regs_parsing",
         ":perfetto_src_profiling_perf_unwinding",
+        ":perfetto_src_profiling_symbolizer_symbolize_database",
         ":perfetto_src_profiling_symbolizer_symbolizer",
         ":perfetto_src_profiling_symbolizer_unittests",
         ":perfetto_src_profiling_unittests",
@@ -9563,6 +9596,9 @@
         ":perfetto_src_tracing_test_tracing_integration_test",
         ":perfetto_src_tracing_unittests",
         ":perfetto_tools_sanitizers_unittests_sanitizers_unittests",
+        ":perfetto_tools_trace_to_text_trace_to_text_lib",
+        ":perfetto_tools_trace_to_text_unittests",
+        ":perfetto_tools_trace_to_text_utils",
     ],
     shared_libs: [
         "libandroidicu",
@@ -9674,7 +9710,7 @@
         "perfetto_src_ipc_test_messages_cpp_gen_headers",
         "perfetto_src_ipc_test_messages_ipc_gen_headers",
         "perfetto_src_perfetto_cmd_gen_cc_config_descriptor",
-        "perfetto_src_perfetto_cmd_protos_gen_headers",
+        "perfetto_src_perfetto_cmd_protos_cpp_gen_headers",
         "perfetto_src_protozero_testing_messages_cpp_gen_headers",
         "perfetto_src_protozero_testing_messages_lite_gen_headers",
         "perfetto_src_protozero_testing_messages_zero_gen_headers",
@@ -9688,6 +9724,7 @@
         "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",
+        "perfetto_tools_trace_to_text_gen_cc_trace_descriptor",
     ],
     defaults: [
         "perfetto_defaults",
@@ -9964,6 +10001,7 @@
         ":perfetto_tools_trace_to_text_common",
         ":perfetto_tools_trace_to_text_full",
         ":perfetto_tools_trace_to_text_pprofbuilder",
+        ":perfetto_tools_trace_to_text_trace_to_text_lib",
         ":perfetto_tools_trace_to_text_utils",
     ],
     static_libs: [
@@ -10287,7 +10325,7 @@
         ":perfetto_src_base_unix_socket",
         ":perfetto_src_ipc_client",
         ":perfetto_src_ipc_common",
-        ":perfetto_src_perfetto_cmd_protos_gen",
+        ":perfetto_src_perfetto_cmd_protos_cpp_gen",
         ":perfetto_src_perfetto_cmd_trigger_perfetto_cmd",
         ":perfetto_src_perfetto_cmd_trigger_producer",
         ":perfetto_src_protozero_protozero",
@@ -10346,7 +10384,7 @@
         "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
-        "perfetto_src_perfetto_cmd_protos_gen_headers",
+        "perfetto_src_perfetto_cmd_protos_cpp_gen_headers",
     ],
     defaults: [
         "perfetto_defaults",
diff --git a/BUILD b/BUILD
index ab6cc4c..283d9ce 100644
--- a/BUILD
+++ b/BUILD
@@ -787,7 +787,7 @@
 perfetto_cc_proto_descriptor(
     name = "src_perfetto_cmd_gen_cc_config_descriptor",
     deps = [
-        ":protos_perfetto_config_perfetto_config_descriptor",
+        ":protos_perfetto_config_merged_config_descriptor",
     ],
     outs = [
         "src/perfetto_cmd/perfetto_config.descriptor.h",
@@ -1005,7 +1005,7 @@
 perfetto_cc_proto_descriptor(
     name = "src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
     deps = [
-        ":protos_third_party_chromium_chrome_track_event_descriptor",
+        ":protos_third_party_chromium_descriptor",
     ],
     outs = [
         "src/trace_processor/importers/chrome_track_event.descriptor.h",
@@ -1025,7 +1025,7 @@
 perfetto_cc_proto_descriptor(
     name = "src_trace_processor_importers_gen_cc_track_event_descriptor",
     deps = [
-        ":protos_perfetto_trace_track_event_track_event_descriptor",
+        ":protos_perfetto_trace_track_event_descriptor",
     ],
     outs = [
         "src/trace_processor/importers/track_event.descriptor.h",
@@ -1067,6 +1067,7 @@
         "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_thread_time_in_state.sql",
         "src/trace_processor/metrics/sql/android/android_trace_quality.sql",
@@ -1475,6 +1476,8 @@
         "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",
@@ -1927,7 +1930,6 @@
     srcs = [
         "tools/trace_to_text/proto_full_utils.cc",
         "tools/trace_to_text/proto_full_utils.h",
-        "tools/trace_to_text/trace_to_text.cc",
     ],
 )
 
@@ -1949,6 +1951,16 @@
     ],
 )
 
+# GN target: //tools/trace_to_text:trace_to_text_lib
+perfetto_filegroup(
+    name = "tools_trace_to_text_trace_to_text_lib",
+    srcs = [
+        "tools/trace_to_text/proto_full_utils.h",
+        "tools/trace_to_text/trace_to_text.cc",
+        "tools/trace_to_text/trace_to_text.h",
+    ],
+)
+
 # GN target: //tools/trace_to_text:utils
 perfetto_filegroup(
     name = "tools_trace_to_text_utils",
@@ -1978,7 +1990,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/common:zero
+# GN target: //protos/perfetto/common:source_set
 perfetto_proto_library(
     name = "protos_perfetto_common_protos",
     srcs = [
@@ -2028,7 +2040,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/android:zero
+# GN target: //protos/perfetto/config/android:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_android_protos",
     srcs = [
@@ -2099,7 +2111,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/ftrace:zero
+# GN target: //protos/perfetto/config/ftrace:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_ftrace_protos",
     srcs = [
@@ -2134,7 +2146,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/gpu:zero
+# GN target: //protos/perfetto/config/gpu:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_gpu_protos",
     srcs = [
@@ -2170,7 +2182,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/inode_file:zero
+# GN target: //protos/perfetto/config/inode_file:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_inode_file_protos",
     srcs = [
@@ -2206,7 +2218,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/interceptors:zero
+# GN target: //protos/perfetto/config/interceptors:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_interceptors_protos",
     srcs = [
@@ -2237,15 +2249,18 @@
     ],
 )
 
-# GN target: //protos/perfetto/config:merged_config
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_merged_config",
+# GN target: //protos/perfetto/config:merged_config_descriptor
+perfetto_proto_descriptor(
+    name = "protos_perfetto_config_merged_config_descriptor",
     deps = [
         ":protos_perfetto_config_merged_config_protos",
     ],
+    outs = [
+        "protos_perfetto_config_merged_config_descriptor.bin",
+    ],
 )
 
-# GN target: //protos/perfetto/config:merged_config
+# GN target: //protos/perfetto/config:merged_config_source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_merged_config_protos",
     srcs = [
@@ -2254,28 +2269,6 @@
     visibility = PERFETTO_CONFIG.public_visibility,
 )
 
-# GN target: //protos/perfetto/config:perfetto_config_descriptor
-perfetto_proto_descriptor(
-    name = "protos_perfetto_config_perfetto_config_descriptor",
-    deps = [
-        ":protos_perfetto_config_perfetto_config_protos",
-    ],
-    outs = [
-        "protos_perfetto_config_perfetto_config_descriptor.bin",
-    ],
-)
-
-# GN target: //protos/perfetto/config:perfetto_config_descriptor
-perfetto_proto_library(
-    name = "protos_perfetto_config_perfetto_config_protos",
-    srcs = [
-        "protos/perfetto/config/perfetto_config.proto",
-    ],
-    visibility = [
-        PERFETTO_CONFIG.proto_library_visibility,
-    ],
-)
-
 # GN target: //protos/perfetto/config/power:cpp
 perfetto_cc_protocpp_library(
     name = "protos_perfetto_config_power_cpp",
@@ -2292,7 +2285,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/power:zero
+# GN target: //protos/perfetto/config/power:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_power_protos",
     srcs = [
@@ -2327,7 +2320,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/process_stats:zero
+# GN target: //protos/perfetto/config/process_stats:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_process_stats_protos",
     srcs = [
@@ -2363,7 +2356,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/profiling:zero
+# GN target: //protos/perfetto/config/profiling:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_profiling_protos",
     srcs = [
@@ -2388,7 +2381,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config:zero
+# GN target: //protos/perfetto/config:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_protos",
     srcs = [
@@ -2432,7 +2425,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/sys_stats:zero
+# GN target: //protos/perfetto/config/sys_stats:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_sys_stats_protos",
     srcs = [
@@ -2471,7 +2464,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/track_event:zero
+# GN target: //protos/perfetto/config/track_event:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_track_event_protos",
     srcs = [
@@ -2550,7 +2543,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/ipc:ipc
+# GN target: //protos/perfetto/ipc:source_set
 perfetto_proto_library(
     name = "protos_perfetto_ipc_protos",
     srcs = [
@@ -2584,7 +2577,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/ipc:wire_protocol_cpp
+# GN target: //protos/perfetto/ipc:wire_protocol_source_set
 perfetto_proto_library(
     name = "protos_perfetto_ipc_wire_protocol_protos",
     srcs = [
@@ -2735,7 +2728,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/android:zero
+# GN target: //protos/perfetto/trace/android:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_android_protos",
     srcs = [
@@ -2772,7 +2765,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/chrome:zero
+# GN target: //protos/perfetto/trace/chrome:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_chrome_protos",
     srcs = [
@@ -2797,7 +2790,7 @@
 perfetto_proto_descriptor(
     name = "protos_perfetto_trace_descriptor",
     deps = [
-        ":protos_perfetto_trace_protos",
+        ":protos_perfetto_trace_non_minimal_protos",
     ],
     outs = [
         "protos_perfetto_trace_descriptor.bin",
@@ -2812,7 +2805,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/filesystem:zero
+# GN target: //protos/perfetto/trace/filesystem:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_filesystem_protos",
     srcs = [
@@ -2839,7 +2832,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/ftrace:zero
+# GN target: //protos/perfetto/trace/ftrace:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_ftrace_protos",
     srcs = [
@@ -2849,6 +2842,7 @@
         "protos/perfetto/trace/ftrace/clk.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
+        "protos/perfetto/trace/ftrace/cros_ec.proto",
         "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
         "protos/perfetto/trace/ftrace/dpu.proto",
         "protos/perfetto/trace/ftrace/ext4.proto",
@@ -2913,7 +2907,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/gpu:zero
+# GN target: //protos/perfetto/trace/gpu:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_gpu_protos",
     srcs = [
@@ -2948,7 +2942,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/interned_data:zero
+# GN target: //protos/perfetto/trace/interned_data:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_interned_data_protos",
     srcs = [
@@ -2977,15 +2971,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace:merged_trace
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_merged_trace",
-    deps = [
-        ":protos_perfetto_trace_merged_trace_protos",
-    ],
-)
-
-# GN target: //protos/perfetto/trace:merged_trace
+# GN target: //protos/perfetto/trace:merged_trace_source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_merged_trace_protos",
     srcs = [
@@ -3002,7 +2988,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace:minimal_zero
+# GN target: //protos/perfetto/trace:minimal_source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_minimal_protos",
     srcs = [
@@ -3057,7 +3043,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace:non_minimal_zero
+# GN target: //protos/perfetto/trace:non_minimal_source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_non_minimal_protos",
     srcs = [
@@ -3099,6 +3085,9 @@
         ":protos_perfetto_trace_system_info_protos",
         ":protos_perfetto_trace_track_event_protos",
     ],
+    exports = [
+        ":protos_perfetto_trace_track_event_protos",
+    ],
 )
 
 # GN target: //protos/perfetto/trace:non_minimal_zero
@@ -3143,7 +3132,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/perfetto:zero
+# GN target: //protos/perfetto/trace/perfetto:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_perfetto_protos",
     srcs = [
@@ -3171,7 +3160,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/power:zero
+# GN target: //protos/perfetto/trace/power:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_power_protos",
     srcs = [
@@ -3196,7 +3185,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace_processor:metrics_impl_zero
+# GN target: //protos/perfetto/trace_processor:metrics_impl_source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_processor_metrics_impl_protos",
     srcs = [
@@ -3215,7 +3204,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace_processor:zero
+# GN target: //protos/perfetto/trace_processor:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_processor_protos",
     srcs = [
@@ -3246,7 +3235,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/profiling:zero
+# GN target: //protos/perfetto/trace/profiling:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_profiling_protos",
     srcs = [
@@ -3273,46 +3262,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace:descriptor
-perfetto_proto_library(
-    name = "protos_perfetto_trace_protos",
-    srcs = [
-        "protos/perfetto/trace/trace.proto",
-    ],
-    visibility = [
-        PERFETTO_CONFIG.proto_library_visibility,
-    ],
-    deps = [
-        ":protos_perfetto_common_protos",
-        ":protos_perfetto_config_android_protos",
-        ":protos_perfetto_config_ftrace_protos",
-        ":protos_perfetto_config_gpu_protos",
-        ":protos_perfetto_config_inode_file_protos",
-        ":protos_perfetto_config_interceptors_protos",
-        ":protos_perfetto_config_power_protos",
-        ":protos_perfetto_config_process_stats_protos",
-        ":protos_perfetto_config_profiling_protos",
-        ":protos_perfetto_config_protos",
-        ":protos_perfetto_config_sys_stats_protos",
-        ":protos_perfetto_config_track_event_protos",
-        ":protos_perfetto_trace_android_protos",
-        ":protos_perfetto_trace_chrome_protos",
-        ":protos_perfetto_trace_filesystem_protos",
-        ":protos_perfetto_trace_ftrace_protos",
-        ":protos_perfetto_trace_gpu_protos",
-        ":protos_perfetto_trace_interned_data_protos",
-        ":protos_perfetto_trace_minimal_protos",
-        ":protos_perfetto_trace_non_minimal_protos",
-        ":protos_perfetto_trace_perfetto_protos",
-        ":protos_perfetto_trace_power_protos",
-        ":protos_perfetto_trace_profiling_protos",
-        ":protos_perfetto_trace_ps_protos",
-        ":protos_perfetto_trace_sys_stats_protos",
-        ":protos_perfetto_trace_system_info_protos",
-        ":protos_perfetto_trace_track_event_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/ps:lite
 perfetto_cc_proto_library(
     name = "protos_perfetto_trace_ps_lite",
@@ -3321,7 +3270,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/ps:zero
+# GN target: //protos/perfetto/trace/ps:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_ps_protos",
     srcs = [
@@ -3349,7 +3298,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/sys_stats:zero
+# GN target: //protos/perfetto/trace/sys_stats:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_sys_stats_protos",
     srcs = [
@@ -3380,7 +3329,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/system_info:zero
+# GN target: //protos/perfetto/trace/system_info:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_system_info_protos",
     srcs = [
@@ -3407,6 +3356,17 @@
     ],
 )
 
+# GN target: //protos/perfetto/trace/track_event:descriptor
+perfetto_proto_descriptor(
+    name = "protos_perfetto_trace_track_event_descriptor",
+    deps = [
+        ":protos_perfetto_trace_track_event_protos",
+    ],
+    outs = [
+        "protos_perfetto_trace_track_event_descriptor.bin",
+    ],
+)
+
 # GN target: //protos/perfetto/trace/track_event:lite
 perfetto_cc_proto_library(
     name = "protos_perfetto_trace_track_event_lite",
@@ -3415,7 +3375,7 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/track_event:zero
+# GN target: //protos/perfetto/trace/track_event:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_track_event_protos",
     srcs = [
@@ -3449,31 +3409,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/track_event:track_event_descriptor
-perfetto_proto_descriptor(
-    name = "protos_perfetto_trace_track_event_track_event_descriptor",
-    deps = [
-        ":protos_perfetto_trace_track_event_track_event_protos",
-    ],
-    outs = [
-        "protos_perfetto_trace_track_event_track_event_descriptor.bin",
-    ],
-)
-
-# GN target: //protos/perfetto/trace/track_event:track_event_descriptor
-perfetto_proto_library(
-    name = "protos_perfetto_trace_track_event_track_event_protos",
-    srcs = [
-        "protos/perfetto/trace/track_event/track_event.proto",
-    ],
-    visibility = [
-        PERFETTO_CONFIG.proto_library_visibility,
-    ],
-    deps = [
-        ":protos_perfetto_trace_track_event_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/track_event:zero
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_track_event_zero",
@@ -3482,28 +3417,14 @@
     ],
 )
 
-# GN target: //protos/third_party/chromium:chrome_track_event_descriptor
+# GN target: //protos/third_party/chromium:descriptor
 perfetto_proto_descriptor(
-    name = "protos_third_party_chromium_chrome_track_event_descriptor",
+    name = "protos_third_party_chromium_descriptor",
     deps = [
-        ":protos_third_party_chromium_chrome_track_event_protos",
+        ":protos_third_party_chromium_protos",
     ],
     outs = [
-        "protos_third_party_chromium_chrome_track_event_descriptor.bin",
-    ],
-)
-
-# GN target: //protos/third_party/chromium:chrome_track_event_descriptor
-perfetto_proto_library(
-    name = "protos_third_party_chromium_chrome_track_event_protos",
-    srcs = [
-        "protos/third_party/chromium/chrome_track_event.proto",
-    ],
-    visibility = [
-        PERFETTO_CONFIG.proto_library_visibility,
-    ],
-    deps = [
-        ":protos_perfetto_trace_track_event_protos",
+        "protos_third_party_chromium_descriptor.bin",
     ],
 )
 
@@ -3515,7 +3436,7 @@
     ],
 )
 
-# GN target: //protos/third_party/chromium:lite
+# GN target: //protos/third_party/chromium:source_set
 perfetto_proto_library(
     name = "protos_third_party_chromium_protos",
     srcs = [
@@ -3525,9 +3446,12 @@
     deps = [
         ":protos_perfetto_trace_track_event_protos",
     ],
+    exports = [
+        ":protos_perfetto_trace_track_event_protos",
+    ],
 )
 
-# GN target: //protos/third_party/pprof:zero
+# GN target: //protos/third_party/pprof:source_set
 perfetto_proto_library(
     name = "protos_third_party_pprof_protos",
     srcs = [
@@ -3546,15 +3470,15 @@
     ],
 )
 
-# GN target: //src/perfetto_cmd:protos
+# GN target: //src/perfetto_cmd:protos_cpp
 perfetto_cc_protocpp_library(
-    name = "src_perfetto_cmd_protos",
+    name = "src_perfetto_cmd_protos_cpp",
     deps = [
         ":src_perfetto_cmd_protos_protos",
     ],
 )
 
-# GN target: //src/perfetto_cmd:protos
+# GN target: //src/perfetto_cmd:protos_source_set
 perfetto_proto_library(
     name = "src_perfetto_cmd_protos_protos",
     srcs = [
@@ -3732,7 +3656,7 @@
         ":protozero",
         ":src_base_base",
         ":src_perfetto_cmd_gen_cc_config_descriptor",
-        ":src_perfetto_cmd_protos",
+        ":src_perfetto_cmd_protos_cpp",
     ] + PERFETTO_CONFIG.deps.zlib,
 )
 
@@ -4047,6 +3971,7 @@
         ":tools_trace_to_text_common",
         ":tools_trace_to_text_full",
         ":tools_trace_to_text_pprofbuilder",
+        ":tools_trace_to_text_trace_to_text_lib",
         ":tools_trace_to_text_utils",
     ],
     visibility = [
diff --git a/BUILD.gn b/BUILD.gn
index 2e07e14..53b8d9a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -77,6 +77,9 @@
 if (enable_perfetto_unittests) {
   import("gn/perfetto_unittests.gni")
   test("perfetto_unittests") {
+    if (is_fuchsia) {
+      use_cfv2 = false
+    }
     deps = perfetto_unittests_targets
   }
   all_targets += [ ":perfetto_unittests" ]
@@ -130,9 +133,9 @@
   all_targets += [
     "test/configs",
 
-    # For syntax-checking the proto.
-    "protos/perfetto/config:merged_config",
-    "protos/perfetto/trace:merged_trace",  # For syntax-checking the proto.
+    # For syntax-checking the protos.
+    "protos/perfetto/config:merged_config_lite",
+    "protos/perfetto/trace:merged_trace_lite",
 
     # For checking all generated xxx.gen.{cc,h} files without waiting for
     # embedders to try to use them and fail.
diff --git a/CHANGELOG b/CHANGELOG
index 95103e5..ebad664 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,23 +1,63 @@
 Unreleased:
   Tracing service and probes:
-    * Changed output format of perfetto --query. Made the output more compact
-      and added a summary of ongoing tracing sessions for the caller UID.
-    * Added "cpufreq_period_ms" in data source "linux.sys_stats" to poll
-      /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq periodically for
-      current frequency of CPUs in kHz.
+    *
   Trace Processor:
-    * Add support for multiple SQL statements to be passed to
-      TraceProcessor::ExecuteQuery. All queries will be executed fully, with
-      the returned iterator yielding rows for the final statement.
-    * Add support for multi-line SQL comments; previously only single line
-      comments were supported.
+    *
   UI:
-    * Changed HTTP+RPC to use the /websocket endpoint available in newer
-      versions of trace_processor --httpd.
+    * Added flow arrows between binder transaction pairs (request/reply
+      and async send/async recv).
   SDK:
     *
 
 
+v24.2 - 2022-02-10:
+  SDK:
+    * Revert of incremental timestamps, introduced in v24.0.
+      Some clients were depending on non-incremental timestamps.
+      Future releases will re-enable this but offer an opt-out.
+
+
+v24.1 - 2022-02-09:
+  Tracing service and probes:
+    * Fixed build failures on Windows.
+  Trace Processor:
+    * Fixed build failures on Windows.
+
+
+v24.0 - 2022-02-08:
+  Tracing service and probes:
+    * Added "cpufreq_period_ms" in data source "linux.sys_stats" to poll
+      /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq periodically.
+    * Added support for Trusty TEE workqueue events.
+    * Added support for more PMU events in traced_perf.
+    * Changed output format of perfetto --query. Made the output more compact
+      and added a summary of ongoing tracing sessions for the caller UID.
+    * Changed timeout for traced stall detection from 2s to 4s.
+    * Changed internal buffer management to split trace filtering in smaller
+      tasks and avoid too large memory allocation when filtering.
+    * Fixed a bug that could cause producers to see Flush() requests after an
+      OnStop() and mis-behave if the tracing session is extremely short.
+  Trace Processor:
+    * Added support for passing multiple SQL statements to ExecuteQuery(). All
+      queries will be executed fully, with the returned iterator yielding rows
+      for the final statement.
+    * Added support for multi-line SQL comments; previously only single line
+      comments were supported.
+  UI:
+    * Added support for parsing instant events from legacy systrace formats.
+    * Added ingestion and visualization for inet_sock_set_state and
+      tcp_retransmit_skb events, showing TCP connections on dedicated tracks.
+    * Changed HTTP+RPC to use the /websocket endpoint available in newer
+      versions of trace_processor --httpd.
+    * Changed text selection/copy: now allowed by default for DOM elements.
+    * Changed search to also lookup slices by ID when the term is a number.
+    * Changed postMessage() API, suppressed confirmation dialog when the opener
+      is in the same origin, for cases when the UI is self-hosted.
+  SDK:
+    * Changed timestamps emitted by the SDK to be incremental by default, using
+      ClockSnapshot + TracePacketDefaults.
+
+
 v23.0 - 2022-01-11:
   Tracing service and probes:
     * Added workaround for a kernel ftrace bug causing some "comm" fields to be
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index c970450..a755b8c 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -289,7 +289,8 @@
   # Enables Zlib support. This is used both by the "perfetto" cmdline client
   # (for compressing traces) and by trace processor (for compressed traces).
   enable_perfetto_zlib =
-      enable_perfetto_trace_processor || enable_perfetto_platform_services
+      (enable_perfetto_trace_processor && !build_with_chromium) ||
+      enable_perfetto_platform_services
 }
 
 declare_args() {
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index 1a2b482..dbfa0fa 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -80,6 +80,7 @@
 
 if (enable_perfetto_trace_processor) {
   perfetto_unittests_targets += [ "src/trace_processor:unittests" ]
+  perfetto_unittests_targets += [ "tools/trace_to_text:unittests" ]
 
   if (enable_perfetto_trace_processor_sqlite) {
     perfetto_unittests_targets += [ "src/trace_processor/metrics:unittests" ]
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index 5b87753..d1794d5 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -254,6 +254,16 @@
       }
     }
 
+    # The distinction between deps and public_deps does not matter for GN
+    # but Bazel cares about the difference so we distinguish between the two.
+    public_deps_ = []
+    if (defined(invoker.public_deps)) {
+      foreach(dep, invoker.public_deps) {
+        public_deps_ = [ string_replace(dep, expansion_token, gen_type) ]
+      }
+    }
+    deps_ += public_deps_
+
     if (gen_type == "zero") {
       protozero_library(target_name_) {
         proto_in_dir = proto_path
@@ -328,6 +338,7 @@
         metadata = {
           proto_library_sources = invoker.sources
           import_dirs = import_dirs_
+          exports = get_path_info(public_deps_, "abspath")
         }
         forward_variables_from(invoker, vars_to_forward)
       }
diff --git a/include/perfetto/ext/base/hash.h b/include/perfetto/ext/base/hash.h
index 50669f9..158a886 100644
--- a/include/perfetto/ext/base/hash.h
+++ b/include/perfetto/ext/base/hash.h
@@ -45,11 +45,14 @@
   void Update(const char* data, size_t size) {
     for (size_t i = 0; i < size; i++) {
       result_ ^= static_cast<uint8_t>(data[i]);
+      // Note: Arithmetic overflow of unsigned integers is well defined in C++
+      // standard unlike signed integers.
+      // https://stackoverflow.com/a/41280273
       result_ *= kFnv1a64Prime;
     }
   }
 
-  uint64_t digest() { return result_; }
+  uint64_t digest() const { return result_; }
 
  private:
   static constexpr uint64_t kFnv1a64OffsetBasis = 0xcbf29ce484222325;
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 3a3db7e..9229153 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -35,16 +35,14 @@
 
 namespace perfetto {
 
-// A function for converting an abstract timestamp into a
-// perfetto::TraceTimestamp struct. By specialising this template and defining
+// This template provides a way to convert an abstract timestamp into the trace
+// clock timebase in nanoseconds. By specialising this template and defining
 // static ConvertTimestampToTraceTimeNs function in it the user can register
-// additional timestamp types. The return value should specify the
-// clock domain used by the timestamp as well as its value.
+// additional timestamp types. The return value should specify the clock used by
+// the timestamp as well as its value in nanoseconds.
 //
-// The supported clock domains are the ones described in
-// perfetto.protos.ClockSnapshot. However, custom clock IDs (>=64) are
-// reserved for internal use by the SDK for the time being.
-// The timestamp value should be in nanoseconds regardless of the clock domain.
+// The users should see the specialisation for uint64_t below as an example.
+// Note that the specialisation should be defined in perfetto namespace.
 template <typename T>
 struct TraceTimestampTraits;
 
@@ -241,7 +239,7 @@
                                Arguments&&... args) PERFETTO_NO_INLINE {
     TraceForCategoryImpl(instances, category, event_name, type,
                          TrackEventInternal::kDefaultTrack,
-                         TrackEventInternal::GetTraceTime(),
+                         TrackEventInternal::GetTimeNs(),
                          std::forward<Arguments>(args)...);
   }
 
@@ -263,7 +261,7 @@
                                Arguments&&... args) PERFETTO_NO_INLINE {
     TraceForCategoryImpl(
         instances, category, event_name, type, std::forward<TrackType>(track),
-        TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...);
+        TrackEventInternal::GetTimeNs(), std::forward<Arguments>(args)...);
   }
 
   // Trace point which takes a timestamp, but not track.
@@ -316,7 +314,7 @@
                                ValueType value) PERFETTO_ALWAYS_INLINE {
     PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
     TraceForCategory(instances, category, /*name=*/nullptr, type, track,
-                     TrackEventInternal::GetTraceTime(), value);
+                     TrackEventInternal::GetTimeNs(), value);
   }
 
   // Trace point with with a timestamp and a counter sample.
@@ -362,8 +360,7 @@
     TrackRegistry::Get()->UpdateTrack(track, desc.SerializeAsString());
     Base::template Trace([&](typename Base::TraceContext ctx) {
       TrackEventInternal::WriteTrackDescriptor(
-          track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
-          TrackEventInternal::GetTraceTime());
+          track, ctx.tls_inst_->trace_writer.get());
     });
   }
 
@@ -455,14 +452,14 @@
           TrackEventIncrementalState* incr_state = ctx.GetIncrementalState();
           if (incr_state->was_cleared) {
             incr_state->was_cleared = false;
-            TrackEventInternal::ResetIncrementalState(trace_writer, incr_state,
+            TrackEventInternal::ResetIncrementalState(trace_writer,
                                                       trace_timestamp);
           }
 
           // Write the track descriptor before any event on the track.
           if (track) {
             TrackEventInternal::WriteTrackDescriptorIfNeeded(
-                track, trace_writer, incr_state, trace_timestamp);
+                track, trace_writer, incr_state);
           }
 
           // Write the event itself.
@@ -516,8 +513,7 @@
     TrackRegistry::Get()->UpdateTrack(track, std::move(callback));
     Base::template Trace([&](typename Base::TraceContext ctx) {
       TrackEventInternal::WriteTrackDescriptor(
-          track, ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
-          TrackEventInternal::GetTraceTime());
+          track, ctx.tls_inst_->trace_writer.get());
     });
   }
 
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index 6fed09e..037dc09 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -35,24 +35,13 @@
 
 // Represents a point in time for the clock specified by |clock_id|.
 struct TraceTimestamp {
-  // Clock IDs have the following semantic:
-  // [1, 63]:    Builtin types, see BuiltinClock from
-  //             ../common/builtin_clock.proto.
-  // [64, 127]:  User-defined clocks. These clocks are sequence-scoped. They
-  //             are only valid within the same |trusted_packet_sequence_id|
-  //             (i.e. only for TracePacket(s) emitted by the same TraceWriter
-  //             that emitted the clock snapshot).
-  // [128, MAX]: Reserved for future use. The idea is to allow global clock
-  //             IDs and setting this ID to hash(full_clock_name) & ~127.
-  // Learn more: `clock_snapshot.proto`
-  uint32_t clock_id;
-  uint64_t value;
+  protos::pbzero::BuiltinClock clock_id;
+  uint64_t nanoseconds;
 };
 
 class EventContext;
 class TrackEventSessionObserver;
 struct Category;
-struct TraceTimestamp;
 namespace protos {
 namespace gen {
 class TrackEventConfig;
@@ -96,12 +85,6 @@
 struct TrackEventIncrementalState {
   static constexpr size_t kMaxInternedDataFields = 32;
 
-  // Packet-sequence-scoped clock that encodes microsecond timestamps in the
-  // domain of the clock returned by GetClockId() as delta values - see
-  // Clock::is_incremental in perfetto/trace/clock_snapshot.proto.
-  // Default unit: nanoseconds.
-  static constexpr uint32_t kClockIdIncremental = 64;
-
   bool was_cleared = true;
 
   // A heap-allocated message for storing newly seen interned data while we are
@@ -133,11 +116,6 @@
   // this tracing session. The value in the map indicates whether the category
   // is enabled or disabled.
   std::unordered_map<std::string, bool> dynamic_categories;
-
-  // The latest reference timestamp that was used in a TracePacket or in a
-  // ClockSnapshot. The increment between this timestamp and the current trace
-  // time (GetTimeNs) is a value in kClockIdIncremental's domain.
-  uint64_t last_timestamp_ns = 0;
 };
 
 // The backend portion of the track event trace point implemention. Outlined to
@@ -168,11 +146,9 @@
       const Category* category,
       const char* name,
       perfetto::protos::pbzero::TrackEvent::Type,
-      const TraceTimestamp& timestamp);
+      TraceTimestamp timestamp = {GetClockId(), GetTimeNs()});
 
-  static void ResetIncrementalState(TraceWriterBase* trace_writer,
-                                    TrackEventIncrementalState* incr_state,
-                                    const TraceTimestamp& timestamp);
+  static void ResetIncrementalState(TraceWriterBase*, TraceTimestamp);
 
   // TODO(altimin): Remove this method once Chrome uses
   // EventContext::AddDebugAnnotation directly.
@@ -192,29 +168,24 @@
   static void WriteTrackDescriptorIfNeeded(
       const TrackType& track,
       TraceWriterBase* trace_writer,
-      TrackEventIncrementalState* incr_state,
-      const TraceTimestamp& timestamp) {
+      TrackEventIncrementalState* incr_state) {
     auto it_and_inserted = incr_state->seen_tracks.insert(track.uuid);
     if (PERFETTO_LIKELY(!it_and_inserted.second))
       return;
-    WriteTrackDescriptor(track, trace_writer, incr_state, timestamp);
+    WriteTrackDescriptor(track, trace_writer);
   }
 
   // Unconditionally write a track descriptor into the trace.
   template <typename TrackType>
   static void WriteTrackDescriptor(const TrackType& track,
-                                   TraceWriterBase* trace_writer,
-                                   TrackEventIncrementalState* incr_state,
-                                   const TraceTimestamp& timestamp) {
+                                   TraceWriterBase* trace_writer) {
     TrackRegistry::Get()->SerializeTrack(
-        track, NewTracePacket(trace_writer, incr_state, timestamp));
+        track, NewTracePacket(trace_writer, {GetClockId(), GetTimeNs()}));
   }
 
   // Get the current time in nanoseconds in the trace clock timebase.
   static uint64_t GetTimeNs();
 
-  static TraceTimestamp GetTraceTime();
-
   // Get the clock used by GetTimeNs().
   static constexpr protos::pbzero::BuiltinClock GetClockId() {
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
@@ -233,8 +204,7 @@
  private:
   static protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
       TraceWriterBase*,
-      TrackEventIncrementalState*,
-      const TraceTimestamp&,
+      TraceTimestamp,
       uint32_t seq_flags =
           protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
   static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
diff --git a/include/perfetto/tracing/track_event_state_tracker.h b/include/perfetto/tracing/track_event_state_tracker.h
index e97f035..7b2437e 100644
--- a/include/perfetto/tracing/track_event_state_tracker.h
+++ b/include/perfetto/tracing/track_event_state_tracker.h
@@ -80,10 +80,6 @@
     std::map<uint64_t /*iid*/, std::string> event_names;
     std::map<uint64_t /*iid*/, std::string> event_categories;
     std::map<uint64_t /*iid*/, std::string> debug_annotation_names;
-    // Current absolute timestamp of the incremental clock.
-    uint64_t most_recent_absolute_time_ns = 0;
-    // default_clock_id == 0 means, no default clock_id is set.
-    uint32_t default_clock_id = 0;
   };
 
   // State for the entire tracing session. Shared by all trace writer sequences
diff --git a/infra/luci/generated/cr-buildbucket.cfg b/infra/luci/generated/cr-buildbucket.cfg
index 18a7c5a..a835dad 100644
--- a/infra/luci/generated/cr-buildbucket.cfg
+++ b/infra/luci/generated/cr-buildbucket.cfg
@@ -29,12 +29,11 @@
         cipd_version: "refs/heads/master"
         cmd: "luciexe"
       }
-      properties: "{\"recipe\":\"perfetto\"}"
+      properties:
+        '{'
+        '  "recipe": "perfetto"'
+        '}'
       service_account: "perfetto-luci-official-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "perfetto-official-builder-linux"
@@ -47,12 +46,11 @@
         cipd_version: "refs/heads/master"
         cmd: "luciexe"
       }
-      properties: "{\"recipe\":\"perfetto\"}"
+      properties:
+        '{'
+        '  "recipe": "perfetto"'
+        '}'
       service_account: "perfetto-luci-official-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "perfetto-official-builder-mac"
@@ -65,12 +63,15 @@
         cipd_version: "refs/heads/master"
         cmd: "luciexe"
       }
-      properties: "{\"recipe\":\"perfetto\"}"
-      service_account: "perfetto-luci-official-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
+      properties:
+        '{'
+        '  "recipe": "perfetto"'
+        '}'
+      caches {
+        name: "macos_sdk"
+        path: "macos_sdk"
       }
+      service_account: "perfetto-luci-official-builder@chops-service-accounts.iam.gserviceaccount.com"
     }
     builders {
       name: "perfetto-official-builder-windows"
@@ -83,12 +84,15 @@
         cipd_version: "refs/heads/master"
         cmd: "luciexe"
       }
-      properties: "{\"recipe\":\"perfetto\"}"
-      service_account: "perfetto-luci-official-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
+      properties:
+        '{'
+        '  "recipe": "perfetto"'
+        '}'
+      caches {
+        name: "windows_sdk"
+        path: "windows_sdk"
       }
+      service_account: "perfetto-luci-official-builder@chops-service-accounts.iam.gserviceaccount.com"
     }
   }
 }
diff --git a/infra/luci/generated/luci-scheduler.cfg b/infra/luci/generated/luci-scheduler.cfg
index 89ae71b..dd7f20b 100644
--- a/infra/luci/generated/luci-scheduler.cfg
+++ b/infra/luci/generated/luci-scheduler.cfg
@@ -10,7 +10,7 @@
   acl_sets: "official"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.perfetto.official"
+    bucket: "official"
     builder: "perfetto-official-builder-android"
   }
 }
@@ -20,7 +20,7 @@
   acl_sets: "official"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.perfetto.official"
+    bucket: "official"
     builder: "perfetto-official-builder-linux"
   }
 }
@@ -30,7 +30,7 @@
   acl_sets: "official"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.perfetto.official"
+    bucket: "official"
     builder: "perfetto-official-builder-mac"
   }
 }
@@ -40,7 +40,7 @@
   acl_sets: "official"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.perfetto.official"
+    bucket: "official"
     builder: "perfetto-official-builder-windows"
   }
 }
diff --git a/infra/luci/generated/project.cfg b/infra/luci/generated/project.cfg
index fbf2a59..2aad4e1 100644
--- a/infra/luci/generated/project.cfg
+++ b/infra/luci/generated/project.cfg
@@ -6,3 +6,10 @@
 
 name: "perfetto"
 access: "group:all"
+lucicfg {
+  version: "1.30.9"
+  package_dir: ".."
+  config_dir: "generated"
+  entry_point: "main.star"
+  experiments: "crbug.com/1182002"
+}
diff --git a/infra/luci/main.star b/infra/luci/main.star
index 4119e2d..316a505 100755
--- a/infra/luci/main.star
+++ b/infra/luci/main.star
@@ -13,11 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-lucicfg.check_version("1.23.3", "Please update depot_tools")
+lucicfg.check_version("1.30.9", "Please update depot_tools")
 
-# Enable LUCI Realms support and launch all builds in realms-aware mode.
-lucicfg.enable_experiment("crbug.com/1085650")
-luci.builder.defaults.experiments.set({"luci.use_realms": 100})
+# Use LUCI Scheduler BBv2 names and add Scheduler realms configs.
+lucicfg.enable_experiment("crbug.com/1182002")
 
 # Enable bbagent.
 luci.recipe.defaults.use_bbagent.set(True)
@@ -75,7 +74,7 @@
     ],
 )
 
-def official_builder(name, os):
+def official_builder(name, os, caches=[]):
     luci.builder(
         name = name,
         bucket = "official",
@@ -98,9 +97,13 @@
                 refs = ["refs/tags/v.+"],
             ),
         ],
+        caches = [
+            swarming.cache(cache, name = cache)
+            for cache in caches
+        ]
     )
 
 official_builder("perfetto-official-builder-linux", "Linux")
-official_builder("perfetto-official-builder-mac", "Mac")
-official_builder("perfetto-official-builder-windows", "Windows")
+official_builder("perfetto-official-builder-mac", "Mac", ["macos_sdk"])
+official_builder("perfetto-official-builder-windows", "Windows", ["windows_sdk"])
 official_builder("perfetto-official-builder-android", "Linux")
diff --git a/protos/perfetto/common/trace_stats.proto b/protos/perfetto/common/trace_stats.proto
index c6a9fbf..f1afc2a 100644
--- a/protos/perfetto/common/trace_stats.proto
+++ b/protos/perfetto/common/trace_stats.proto
@@ -20,7 +20,7 @@
 
 // Statistics for the internals of the tracing service.
 //
-// Next id: 11.
+// Next id: 16.
 message TraceStats {
   // From TraceBuffer::Stats.
   //
@@ -167,4 +167,23 @@
     optional uint64 errors = 4;
   }
   optional FilterStats filter_stats = 11;
+
+  // Count of Flush() requests (either from the Consumer, or self-induced
+  // periodic flushes). The final Flush() is also included in the count.
+  optional uint64 flushes_requested = 12;
+
+  // The count of the Flush() requests that were completed succesfully.
+  // In a well behaving trace this should always be == `flush_requests`.
+  optional uint64 flushes_succeeded = 13;
+
+  // The count of the Flush() requests that failed (in most timed out).
+  // In a well behaving trace this should always be == 0.
+  optional uint64 flushes_failed = 14;
+
+  enum FinalFlushOutcome {
+    FINAL_FLUSH_UNSPECIFIED = 0;
+    FINAL_FLUSH_SUCCEEDED = 1;
+    FINAL_FLUSH_FAILED = 2;
+  }
+  optional FinalFlushOutcome final_flush_outcome = 15;
 }
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index de9c1f5..471bfbe 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -43,13 +43,6 @@
   ]
 }
 
-# This target is not used in the tree and is built only to guarantee that the
-# autogenerated merged proto has a valid syntax.
-perfetto_proto_library("merged_config") {
-  proto_generators = [ "lite" ]
-  sources = [ "perfetto_config.proto" ]
-}
-
 perfetto_proto_library("descriptor") {
   proto_generators = [ "descriptor" ]
   generate_descriptor = "config.descriptor"
@@ -57,8 +50,19 @@
   sources = [ "trace_config.proto" ]
 }
 
-perfetto_proto_library("perfetto_config_descriptor") {
+# This target is not used in the tree and is built only to guarantee that the
+# autogenerated merged proto has a valid syntax.
+perfetto_proto_library("merged_config_@TYPE@") {
+  proto_generators = [
+    "lite",
+    "source_set",
+  ]
+  sources = [ "perfetto_config.proto" ]
+}
+
+perfetto_proto_library("merged_config_descriptor") {
   proto_generators = [ "descriptor" ]
   generate_descriptor = "perfetto_config.descriptor"
   sources = [ "perfetto_config.proto" ]
+  deps = [ ":merged_config_source_set" ]
 }
diff --git a/protos/perfetto/ipc/BUILD.gn b/protos/perfetto/ipc/BUILD.gn
index 49c077e..fb9bfcf 100644
--- a/protos/perfetto/ipc/BUILD.gn
+++ b/protos/perfetto/ipc/BUILD.gn
@@ -21,6 +21,7 @@
   proto_generators = [
     "ipc",
     "cpp",
+    "source_set",
   ]
   sources = [
     "consumer_port.proto",
@@ -39,6 +40,7 @@
   proto_generators = [
     "zero",
     "cpp",
+    "source_set",
   ]
   sources = [ "wire_protocol.proto" ]
 }
diff --git a/protos/perfetto/metrics/BUILD.gn b/protos/perfetto/metrics/BUILD.gn
index a0aec35..751ed94 100644
--- a/protos/perfetto/metrics/BUILD.gn
+++ b/protos/perfetto/metrics/BUILD.gn
@@ -33,6 +33,6 @@
 perfetto_proto_library("descriptor") {
   proto_generators = [ "descriptor" ]
   generate_descriptor = "metrics.descriptor"
-  deps = [ "android:source_set" ]
+  deps = [ ":source_set" ]
   sources = [ "metrics.proto" ]
 }
diff --git a/protos/perfetto/metrics/android/network_metric.proto b/protos/perfetto/metrics/android/network_metric.proto
index fea1d1d..393e76f 100644
--- a/protos/perfetto/metrics/android/network_metric.proto
+++ b/protos/perfetto/metrics/android/network_metric.proto
@@ -50,13 +50,16 @@
 
     // Per core packets statistic.
     repeated CorePacketStatistic core = 2;
+
+    // GRO aggregation ratio.
+    optional string gro_aggregation_ratio = 3;
   }
 
   message Tx {
-    // Total packets statistic
+    // Total packets statistic.
     optional PacketStatistic total = 1;
 
-    // Per core packets statistic
+    // Per core packets statistic.
     repeated CorePacketStatistic core = 2;
   }
 
@@ -110,4 +113,7 @@
 
   // SoftIrq NET_RX action metrics.
   optional NetRxAction net_rx_action = 2;
+
+  // Packet retransmission rate.
+  optional double retransmission_rate = 3;
 }
diff --git a/protos/perfetto/metrics/chrome/scroll_jank.proto b/protos/perfetto/metrics/chrome/scroll_jank.proto
index 6fea1d8..3fe397d 100644
--- a/protos/perfetto/metrics/chrome/scroll_jank.proto
+++ b/protos/perfetto/metrics/chrome/scroll_jank.proto
@@ -30,4 +30,5 @@
   optional int64 num_scroll_update_count = 5 [(unit) = "count_biggerIsBetter"];
   optional int64 num_scroll_update_jank_count = 6
       [(unit) = "count_smallerIsBetter"];
+  optional double scroll_jank_budget_ms = 7 [(unit) = "ms_smallerIsBetter"];
 }
diff --git a/protos/perfetto/metrics/chrome/touch_jank.proto b/protos/perfetto/metrics/chrome/touch_jank.proto
index beebfb5..20318b8 100644
--- a/protos/perfetto/metrics/chrome/touch_jank.proto
+++ b/protos/perfetto/metrics/chrome/touch_jank.proto
@@ -31,4 +31,5 @@
   optional int64 num_touch_update_count = 5 [(unit) = "count_biggerIsBetter"];
   optional int64 num_touch_update_jank_count = 6
       [(unit) = "count_smallerIsBetter"];
+  optional double touch_jank_budget_ms = 7 [(unit) = "ms_smallerIsBetter"];
 }
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 03f6690..afcb6d7 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -907,13 +907,16 @@
 
     // Per core packets statistic.
     repeated CorePacketStatistic core = 2;
+
+    // GRO aggregation ratio.
+    optional string gro_aggregation_ratio = 3;
   }
 
   message Tx {
-    // Total packets statistic
+    // Total packets statistic.
     optional PacketStatistic total = 1;
 
-    // Per core packets statistic
+    // Per core packets statistic.
     repeated CorePacketStatistic core = 2;
   }
 
@@ -967,6 +970,9 @@
 
   // SoftIrq NET_RX action metrics.
   optional NetRxAction net_rx_action = 2;
+
+  // Packet retransmission rate.
+  optional double retransmission_rate = 3;
 }
 
 // End of protos/perfetto/metrics/android/network_metric.proto
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index a807061..cfcca49 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -71,6 +71,15 @@
   ]
 }
 
+# This is only for Bazel build generation.
+group("source_set") {
+  testonly = true
+  public_deps = [
+    ":minimal_source_set",
+    ":non_minimal_source_set",
+  ]
+}
+
 perfetto_proto_library("non_minimal_@TYPE@") {
   proto_generators = [
     "cpp",
@@ -96,6 +105,7 @@
     "track_event:@TYPE@",
   ]
   sources = proto_sources_non_minimal
+  public_deps = [ "track_event:@TYPE@" ]
 }
 
 perfetto_proto_library("minimal_@TYPE@") {
@@ -103,13 +113,6 @@
   sources = proto_sources_minimal
 }
 
-# This target is not used in the tree and is built only to guarantee that the
-# autogenerated merged proto has a valid syntax.
-perfetto_proto_library("merged_trace") {
-  proto_generators = [ "lite" ]
-  sources = [ "perfetto_trace.proto" ]
-}
-
 perfetto_proto_library("descriptor") {
   proto_generators = [ "descriptor" ]
   generate_descriptor = "trace.descriptor"
@@ -130,3 +133,13 @@
   sources = [ "test_extensions.proto" ]
   deps = [ "track_event:source_set" ]
 }
+
+# This target are not used in the tree and is built only to guarantee that the
+# autogenerated merged proto has a valid syntax.
+perfetto_proto_library("merged_trace_@TYPE@") {
+  proto_generators = [
+    "lite",
+    "source_set",
+  ]
+  sources = [ "perfetto_trace.proto" ]
+}
diff --git a/protos/perfetto/trace/chrome/BUILD.gn b/protos/perfetto/trace/chrome/BUILD.gn
index dc4ecb2..2cad830 100644
--- a/protos/perfetto/trace/chrome/BUILD.gn
+++ b/protos/perfetto/trace/chrome/BUILD.gn
@@ -22,7 +22,7 @@
   ]
 }
 
-perfetto_proto_library("minimal_complete_lite") {
+perfetto_proto_library("minimal_complete_@TYPE@") {
   proto_generators = [ "lite" ]
   deps = [
     ":@TYPE@",
diff --git a/protos/perfetto/trace/ftrace/BUILD.gn b/protos/perfetto/trace/ftrace/BUILD.gn
index 7edf7fe..2b6182d 100644
--- a/protos/perfetto/trace/ftrace/BUILD.gn
+++ b/protos/perfetto/trace/ftrace/BUILD.gn
@@ -17,12 +17,6 @@
 import("all_protos.gni")
 
 perfetto_proto_library("@TYPE@") {
-  proto_generators = [
-    "cpp",
-    "zero",
-    "lite",
-    "source_set",
-  ]
   sources = ftrace_proto_names
 }
 
diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index ae4e3d0..7d43696 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -26,6 +26,7 @@
   "clk.proto",
   "compaction.proto",
   "cpuhp.proto",
+  "cros_ec.proto",
   "dmabuf_heap.proto",
   "dpu.proto",
   "ext4.proto",
diff --git a/protos/perfetto/trace/ftrace/cros_ec.proto b/protos/perfetto/trace/ftrace/cros_ec.proto
new file mode 100644
index 0000000..b3ba4fb
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/cros_ec.proto
@@ -0,0 +1,15 @@
+// Autogenerated by:
+// ../../tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message CrosEcSensorhubDataFtraceEvent {
+  optional int64 current_time = 1;
+  optional int64 current_timestamp = 2;
+  optional int64 delta = 3;
+  optional uint32 ec_fifo_timestamp = 4;
+  optional uint32 ec_sensor_num = 5;
+  optional int64 fifo_timestamp = 6;
+}
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index 5b42169..13b3acb 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -26,6 +26,7 @@
 import "protos/perfetto/trace/ftrace/clk.proto";
 import "protos/perfetto/trace/ftrace/compaction.proto";
 import "protos/perfetto/trace/ftrace/cpuhp.proto";
+import "protos/perfetto/trace/ftrace/cros_ec.proto";
 import "protos/perfetto/trace/ftrace/dmabuf_heap.proto";
 import "protos/perfetto/trace/ftrace/dpu.proto";
 import "protos/perfetto/trace/ftrace/ext4.proto";
@@ -456,5 +457,8 @@
     NetDevXmitFtraceEvent net_dev_xmit = 361;
     InetSockSetStateFtraceEvent inet_sock_set_state = 362;
     TcpRetransmitSkbFtraceEvent tcp_retransmit_skb = 363;
+    CrosEcSensorhubDataFtraceEvent cros_ec_sensorhub_data = 364;
+    NapiGroReceiveEntryFtraceEvent napi_gro_receive_entry = 365;
+    NapiGroReceiveExitFtraceEvent napi_gro_receive_exit = 366;
   }
 }
diff --git a/protos/perfetto/trace/ftrace/net.proto b/protos/perfetto/trace/ftrace/net.proto
index e5ffbfb..866a599 100644
--- a/protos/perfetto/trace/ftrace/net.proto
+++ b/protos/perfetto/trace/ftrace/net.proto
@@ -16,3 +16,27 @@
   optional int32 rc = 3;
   optional uint64 skbaddr = 4;
 }
+message NapiGroReceiveEntryFtraceEvent {
+  optional uint32 data_len = 1;
+  optional uint32 gso_size = 2;
+  optional uint32 gso_type = 3;
+  optional uint32 hash = 4;
+  optional uint32 ip_summed = 5;
+  optional uint32 l4_hash = 6;
+  optional uint32 len = 7;
+  optional int32 mac_header = 8;
+  optional uint32 mac_header_valid = 9;
+  optional string name = 10;
+  optional uint32 napi_id = 11;
+  optional uint32 nr_frags = 12;
+  optional uint32 protocol = 13;
+  optional uint32 queue_mapping = 14;
+  optional uint64 skbaddr = 15;
+  optional uint32 truesize = 16;
+  optional uint32 vlan_proto = 17;
+  optional uint32 vlan_tagged = 18;
+  optional uint32 vlan_tci = 19;
+}
+message NapiGroReceiveExitFtraceEvent {
+  optional int32 ret = 1;
+}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 8e6b639..7e7747c 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -2109,7 +2109,7 @@
 
 // Statistics for the internals of the tracing service.
 //
-// Next id: 11.
+// Next id: 16.
 message TraceStats {
   // From TraceBuffer::Stats.
   //
@@ -2256,6 +2256,25 @@
     optional uint64 errors = 4;
   }
   optional FilterStats filter_stats = 11;
+
+  // Count of Flush() requests (either from the Consumer, or self-induced
+  // periodic flushes). The final Flush() is also included in the count.
+  optional uint64 flushes_requested = 12;
+
+  // The count of the Flush() requests that were completed succesfully.
+  // In a well behaving trace this should always be == `flush_requests`.
+  optional uint64 flushes_succeeded = 13;
+
+  // The count of the Flush() requests that failed (in most timed out).
+  // In a well behaving trace this should always be == 0.
+  optional uint64 flushes_failed = 14;
+
+  enum FinalFlushOutcome {
+    FINAL_FLUSH_UNSPECIFIED = 0;
+    FINAL_FLUSH_SUCCEEDED = 1;
+    FINAL_FLUSH_FAILED = 2;
+  }
+  optional FinalFlushOutcome final_flush_outcome = 15;
 }
 
 // End of protos/perfetto/common/trace_stats.proto
@@ -3544,6 +3563,19 @@
 
 // End of protos/perfetto/trace/ftrace/cpuhp.proto
 
+// Begin of protos/perfetto/trace/ftrace/cros_ec.proto
+
+message CrosEcSensorhubDataFtraceEvent {
+  optional int64 current_time = 1;
+  optional int64 current_timestamp = 2;
+  optional int64 delta = 3;
+  optional uint32 ec_fifo_timestamp = 4;
+  optional uint32 ec_sensor_num = 5;
+  optional int64 fifo_timestamp = 6;
+}
+
+// End of protos/perfetto/trace/ftrace/cros_ec.proto
+
 // Begin of protos/perfetto/trace/ftrace/dmabuf_heap.proto
 
 message DmaHeapStatFtraceEvent {
@@ -5132,6 +5164,30 @@
   optional int32 rc = 3;
   optional uint64 skbaddr = 4;
 }
+message NapiGroReceiveEntryFtraceEvent {
+  optional uint32 data_len = 1;
+  optional uint32 gso_size = 2;
+  optional uint32 gso_type = 3;
+  optional uint32 hash = 4;
+  optional uint32 ip_summed = 5;
+  optional uint32 l4_hash = 6;
+  optional uint32 len = 7;
+  optional int32 mac_header = 8;
+  optional uint32 mac_header_valid = 9;
+  optional string name = 10;
+  optional uint32 napi_id = 11;
+  optional uint32 nr_frags = 12;
+  optional uint32 protocol = 13;
+  optional uint32 queue_mapping = 14;
+  optional uint64 skbaddr = 15;
+  optional uint32 truesize = 16;
+  optional uint32 vlan_proto = 17;
+  optional uint32 vlan_tagged = 18;
+  optional uint32 vlan_tci = 19;
+}
+message NapiGroReceiveExitFtraceEvent {
+  optional int32 ret = 1;
+}
 
 // End of protos/perfetto/trace/ftrace/net.proto
 
@@ -5932,6 +5988,9 @@
     NetDevXmitFtraceEvent net_dev_xmit = 361;
     InetSockSetStateFtraceEvent inet_sock_set_state = 362;
     TcpRetransmitSkbFtraceEvent tcp_retransmit_skb = 363;
+    CrosEcSensorhubDataFtraceEvent cros_ec_sensorhub_data = 364;
+    NapiGroReceiveEntryFtraceEvent napi_gro_receive_entry = 365;
+    NapiGroReceiveExitFtraceEvent napi_gro_receive_exit = 366;
   }
 }
 
diff --git a/protos/perfetto/trace/track_event/BUILD.gn b/protos/perfetto/trace/track_event/BUILD.gn
index d1a8177..ab9d273 100644
--- a/protos/perfetto/trace/track_event/BUILD.gn
+++ b/protos/perfetto/trace/track_event/BUILD.gn
@@ -43,7 +43,7 @@
   ]
 }
 
-perfetto_proto_library("track_event_@TYPE@") {
+perfetto_proto_library("@TYPE@") {
   proto_generators = [ "descriptor" ]
   generate_descriptor = "track_event.descriptor"
   sources = [ "track_event.proto" ]
diff --git a/protos/perfetto/trace_processor/BUILD.gn b/protos/perfetto/trace_processor/BUILD.gn
index ee1c795..c8671b1 100644
--- a/protos/perfetto/trace_processor/BUILD.gn
+++ b/protos/perfetto/trace_processor/BUILD.gn
@@ -16,7 +16,10 @@
 import("proto_files.gni")
 
 perfetto_proto_library("@TYPE@") {
-  proto_generators = [ "zero" ]
+  proto_generators = [
+    "zero",
+    "source_set",
+  ]
   deps = [ "../common:zero" ]  # ../common:zero needed for descriptor.proto.
   sources = []
   foreach(source, trace_processor_protos) {
@@ -24,7 +27,10 @@
   }
 }
 
-perfetto_proto_library("metrics_impl_zero") {
-  proto_generators = [ "zero" ]
+perfetto_proto_library("metrics_impl_@TYPE@") {
+  proto_generators = [
+    "zero",
+    "source_set",
+  ]
   sources = [ "metrics_impl.proto" ]
 }
diff --git a/protos/third_party/chromium/BUILD.gn b/protos/third_party/chromium/BUILD.gn
index 83228a6..b0f4f77 100644
--- a/protos/third_party/chromium/BUILD.gn
+++ b/protos/third_party/chromium/BUILD.gn
@@ -4,14 +4,14 @@
 
 perfetto_proto_library("@TYPE@") {
   sources = chrome_track_event_sources
-  deps = [ "../../perfetto/trace/track_event:@TYPE@" ]
+  public_deps = [ "../../perfetto/trace/track_event:@TYPE@" ]
 }
 
-perfetto_proto_library("chrome_track_event_@TYPE@") {
+perfetto_proto_library("@TYPE@") {
   proto_generators = [ "descriptor" ]
   sources = chrome_track_event_sources
   generate_descriptor = "chrome_track_event.descriptor"
-  deps = [ "../../perfetto/trace/track_event:source_set" ]
+  deps = [ ":source_set" ]
 
   # When rolled into Chrome, extension descriptor is going to be linked into
   # binary, therefore increasing its size. Including imports means that the
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index d856269..1984ebc 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -130,6 +130,7 @@
   SHOULD_SWAP_BROWSING_INSTANCE_NO_HAS_NOT_COMMITTED_ANY_NAVIGATION = 20;
   SHOULD_SWAP_BROWSING_INSTANCE_NO_UNLOAD_HANDLER_EXISTS_ON_SAME_SITE_NAVIGATION =
       21;
+  SHOULD_SWAP_BROWSING_INSTANCE_NO_NOT_PRIMARY_MAIN_FRAME = 22;
 }
 
 message ShouldSwapBrowsingInstancesResult {
@@ -615,9 +616,76 @@
   optional FrameType frame_type = 4;
 }
 
+message EventLatency {
+  enum EventType {
+    EVENT_TYPE_UNSPECIFIED = 0;
+    MOUSE_PRESSED = 1;
+    MOUSE_RELEASED = 2;
+    MOUSE_WHEEL = 3;
+    KEY_PRESSED = 4;
+    KEY_RELEASED = 5;
+    TOUCH_PRESSED = 6;
+    TOUCH_RELEASED = 7;
+    TOUCH_MOVED = 8;
+    GESTURE_SCROLL_BEGIN = 9;
+    GESTURE_SCROLL_UPDATE = 10;
+    GESTURE_SCROLL_END = 11;
+    GESTURE_DOUBLE_TAP = 12;
+    GESTURE_LONG_PRESS = 13;
+    GESTURE_LONG_TAP = 14;
+    GESTURE_SHOW_PRESS = 15;
+    GESTURE_TAP = 16;
+    GESTURE_TAP_CANCEL = 17;
+    GESTURE_TAP_DOWN = 18;
+    GESTURE_TAP_UNCONFIRMED = 19;
+    GESTURE_TWO_FINGER_TAP = 20;
+    FIRST_GESTURE_SCROLL_UPDATE = 21;
+    MOUSE_DRAGGED = 22;
+    GESTURE_PINCH_BEGIN = 23;
+    GESTURE_PINCH_END = 24;
+    GESTURE_PINCH_UPDATE = 25;
+    INERTIAL_GESTURE_SCROLL_UPDATE = 26;
+  }
+
+  optional EventType event_type = 1;
+}
+
+message ProcessSingleton {
+  enum RemoteProcessInteractionResult {
+    INTERACTION_RESULT_UNSPECIFIED = 0;
+    TERMINATE_FAILED = 1;
+    REMOTE_PROCESS_NOT_FOUND = 2;
+    TERMINATE_WAIT_TIMEOUT = 3;
+    RUNNING_PROCESS_NOTIFY_ERROR = 4;
+    TERMINATE_NOT_ENOUGH_PERMISSIONS = 5;
+    REMOTE_PROCESS_SHUTTING_DOWN = 6;
+    PROFILE_UNLOCKED = 7;
+    PROFILE_UNLOCKED_BEFORE_KILL = 8;
+    SAME_BROWSER_INSTANCE = 9;
+    SAME_BROWSER_INSTANCE_BEFORE_KILL = 10;
+    FAILED_TO_EXTRACT_PID = 11;
+    INVALID_LOCK_FILE = 12;
+    ORPHANED_LOCK_FILE = 13;
+    USER_REFUSED_TERMINATION = 14;
+    TERMINATE_SUCCEEDED = 100;
+  }
+
+  enum RemoteHungProcessTerminateReason {
+    TERMINATE_REASON_UNSPECIFIED = 0;
+    USER_ACCEPTED_TERMINATION = 1;
+    NO_VISIBLE_WINDOW_FOUND = 2;
+    NOTIFY_ATTEMPTS_EXCEEDED = 3;
+    SOCKET_WRITE_FAILED = 4;
+    SOCKET_READ_FAILED = 5;
+  }
+
+  optional RemoteProcessInteractionResult remote_process_interaction_result = 1;
+  optional RemoteHungProcessTerminateReason remote_process_terminate_reason = 2;
+}
+
 message ChromeTrackEvent {
   // Extension range for Chrome: 1000-1999
-  // Next ID: 1032
+  // Next ID: 1034
   extend TrackEvent {
     optional ChromeAppState chrome_app_state = 1000;
 
@@ -687,5 +755,9 @@
 
     optional RendererMainThreadTaskExecution
         renderer_main_thread_task_execution = 1031;
+
+    optional EventLatency event_latency = 1032;
+
+    optional ProcessSingleton process_singleton = 1033;
   }
 }
diff --git a/protos/third_party/pprof/BUILD.gn b/protos/third_party/pprof/BUILD.gn
index 553d0ac..adbdf95 100644
--- a/protos/third_party/pprof/BUILD.gn
+++ b/protos/third_party/pprof/BUILD.gn
@@ -15,6 +15,9 @@
 import("../../../gn/proto_library.gni")
 
 perfetto_proto_library("@TYPE@") {
-  proto_generators = [ "zero" ]
+  proto_generators = [
+    "zero",
+    "source_set",
+  ]
   sources = [ "profile.proto" ]
 }
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index 6c95b59..1b9135c 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/platform.py b/python/perfetto/trace_processor/platform.py
index e271012..6508058 100644
--- a/python/perfetto/trace_processor/platform.py
+++ b/python/perfetto/trace_processor/platform.py
@@ -46,7 +46,8 @@
       req = request.Request(SHELL_URL)
       with request.urlopen(req) as req:
         file.write(req.read())
-    subprocess.check_output(['chmod', '+x', file.name])
+    if os.name != 'nt':
+      subprocess.check_output(['chmod', '+x', file.name])
     return file.name
 
   def get_bind_addr(self, port: int) -> Tuple[str, int]:
diff --git a/python/perfetto/trace_processor/shell.py b/python/perfetto/trace_processor/shell.py
index b0e3950..a7c997d 100644
--- a/python/perfetto/trace_processor/shell.py
+++ b/python/perfetto/trace_processor/shell.py
@@ -13,7 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
 import subprocess
+import sys
 import time
 from urllib import request, error
 
@@ -30,10 +32,15 @@
   url = f'{addr}:{str(port)}'
 
   shell_path = platform_delegate.get_shell_path(bin_path=bin_path)
-  p = subprocess.Popen([shell_path, '-D', '--http-port',
-                        str(port)],
-                       stdout=subprocess.DEVNULL,
-                       stderr=None if verbose else subprocess.DEVNULL)
+  if os.name == 'nt' and not shell_path.endswith('.exe'):
+    tp_exec = [sys.executable, shell_path]
+  else:
+    tp_exec = [shell_path]
+
+  p = subprocess.Popen(
+      tp_exec + ['-D', '--http-port', str(port)],
+      stdout=subprocess.DEVNULL,
+      stderr=None if verbose else subprocess.DEVNULL)
 
   while True:
     try:
diff --git a/python/perfetto/trace_uri_resolver/__init__.py b/python/perfetto/trace_uri_resolver/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/perfetto/trace_uri_resolver/__init__.py
diff --git a/python/perfetto/trace_uri_resolver/util.py b/python/perfetto/trace_uri_resolver/util.py
index 8cec762..673d688 100644
--- a/python/perfetto/trace_uri_resolver/util.py
+++ b/python/perfetto/trace_uri_resolver/util.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
 from typing import BinaryIO, Dict, Optional, Tuple
 
 # Limit parsing file to 32MB to maintain parity with the UI
@@ -45,4 +46,12 @@
   if idx == -1:
     return None, uri
 
+  # If there is only a single character before the colon
+  # this is likely a Windows path; throw an error on other platforms
+  # to prevent single character names causing issues on Windows.
+  if idx == 1:
+    if os.name != 'nt':
+      raise Exception('Single character resolvers are not allowed')
+    return None, uri
+
   return (uri[:idx], uri[idx + 1:])
diff --git a/python/setup.py b/python/setup.py
index 3c1598e..19d30dd 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -2,10 +2,13 @@
 
 setup(
     name='perfetto',
-    packages=['perfetto', 'perfetto.trace_processor'],
+    packages=[
+        'perfetto', 'perfetto.batch_trace_processor',
+        'perfetto.trace_processor', 'perfetto.trace_uri_resolver'
+    ],
     package_data={'perfetto.trace_processor': ['*.descriptor']},
     include_package_data=True,
-    version='0.3.0',
+    version='0.4.0',
     license='apache-2.0',
     description='Python API for Perfetto\'s Trace Processor',
     author='Perfetto',
diff --git a/src/android_stats/perfetto_atoms.h b/src/android_stats/perfetto_atoms.h
index 2f48055..75cbbd8 100644
--- a/src/android_stats/perfetto_atoms.h
+++ b/src/android_stats/perfetto_atoms.h
@@ -73,13 +73,20 @@
 
   // Checkpoints inside perfetto_cmd after tracing has finished.
   kOnTracingDisabled = 4,
-  kUploadIncidentBegin = 8,
   kFinalizeTraceAndExit = 11,
+  kCmdFwReportBegin = 49,
+  // Will be removed once incidentd is no longer used.
+  kUploadIncidentBegin = 8,
   kNotUploadingEmptyTrace = 17,
 
   // Guardrails inside perfetto_cmd after tracing has finished.
+  kCmdFwReportEmptyTrace = 50,
+  // Will be removed once incidentd is no longer used.
   kUploadIncidentFailure = 10,
 
+  // "Successful" terminal states inside perfetto_cmd.
+  kCmdFwReportHandoff = 51,
+
   // Deprecated as "success" is misleading; it simply means we were
   // able to communicate with incidentd. Will be removed once
   // incidentd is no longer used.
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 637436f..9cfe232 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -45,7 +45,7 @@
 # is shared both by the executable and tests.
 source_set("perfetto_cmd") {
   public_deps = [
-    ":protos",
+    ":protos_cpp",
     "../../include/perfetto/ext/traced",
   ]
   deps = [
@@ -83,12 +83,12 @@
 
 perfetto_cc_proto_descriptor("gen_cc_config_descriptor") {
   descriptor_name = "perfetto_config.descriptor"
-  descriptor_target = "../../protos/perfetto/config:perfetto_config_descriptor"
+  descriptor_target = "../../protos/perfetto/config:merged_config_descriptor"
 }
 
 source_set("trigger_perfetto_cmd") {
   public_deps = [
-    ":protos",
+    ":protos_cpp",
     "../../include/perfetto/ext/traced",
   ]
   deps = [
@@ -113,8 +113,11 @@
   ]
 }
 
-perfetto_proto_library("protos") {
-  proto_generators = [ "cpp" ]
+perfetto_proto_library("protos_@TYPE@") {
+  proto_generators = [
+    "cpp",
+    "source_set",
+  ]
   sources = [ "perfetto_cmd_state.proto" ]
   proto_path = perfetto_root_path
 }
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index ed57c99..e104296 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -1240,8 +1240,8 @@
 
 TRACING SESSIONS:
 
-ID      UID     STATE      NAME         BUF (#) KB   DUR (s)   #DS  STARTED
-===     ===     =====      ====         ==========   =======   ===  =======
+ID      UID     STATE      BUF (#) KB   DUR (s)   #DS  STARTED  NAME
+===     ===     =====      ==========   =======   ===  =======  ====
 )");
     for (const auto& sess : svc_state.tracing_sessions()) {
       uint32_t buf_tot_kb = 0;
@@ -1252,14 +1252,22 @@
       int h = sec / 3600;
       int m = (sec - (h * 3600)) / 60;
       int s = (sec - h * 3600 - m * 60);
-      printf("%-7" PRIu64
-             " %-7d %-10s %-12s (%d) %-8u %-9u %-4u %02d:%02d:%02d\n",
+      printf("%-7" PRIu64 " %-7d %-10s (%d) %-8u %-9u %-4u %02d:%02d:%02d %s\n",
              sess.id(), sess.consumer_uid(), sess.state().c_str(),
-             sess.unique_session_name().c_str(), sess.buffer_size_kb_size(),
-             buf_tot_kb, sess.duration_ms() / 1000, sess.num_data_sources(), h,
-             m, s);
+             sess.buffer_size_kb_size(), buf_tot_kb, sess.duration_ms() / 1000,
+             sess.num_data_sources(), h, m, s,
+             sess.unique_session_name().c_str());
     }  // for tracing_sessions()
-  }    // if (supports_tracing_sessions)
+
+    int sessions_listed = static_cast<int>(svc_state.tracing_sessions().size());
+    if (sessions_listed != svc_state.num_sessions() && geteuid() != 0) {
+      printf(
+          "\n"
+          "NOTE: Some tracing sessions are not reported in the list above.\n"
+          "This is likely because they are owned by a different UID.\n"
+          "If you want to list all session, run again this command as root.\n");
+    }
+  }  // if (supports_tracing_sessions)
 }
 
 void PerfettoCmd::OnObservableEvents(
diff --git a/src/perfetto_cmd/perfetto_cmd_android.cc b/src/perfetto_cmd/perfetto_cmd_android.cc
index 6a2d949..c1fa07b 100644
--- a/src/perfetto_cmd/perfetto_cmd_android.cc
+++ b/src/perfetto_cmd/perfetto_cmd_android.cc
@@ -80,14 +80,12 @@
   PERFETTO_CHECK(!cfg.skip_report());
 
   if (bytes_written_ == 0) {
-    // TODO(lalitm): change this to a dedicated atom decoupled from
-    // incidentd.
-    LogUploadEvent(PerfettoStatsdAtom::kNotUploadingEmptyTrace);
+    LogUploadEvent(PerfettoStatsdAtom::kCmdFwReportEmptyTrace);
     PERFETTO_LOG("Skipping reporting trace to Android. Empty trace.");
     return;
   }
 
-  // TODO(lalitm): add atom for beginning report here.
+  LogUploadEvent(PerfettoStatsdAtom::kCmdFwReportBegin);
   base::StackString<128> self_fd("/proc/self/fd/%d",
                                  fileno(*trace_out_stream_));
   base::ScopedFile fd(base::OpenFile(self_fd.c_str(), O_RDONLY | O_CLOEXEC));
@@ -110,6 +108,7 @@
                  uuid.ToPrettyString().c_str(),
                  trace_config_->unique_session_name().c_str(), bytes_written_);
   }
+  LogUploadEvent(PerfettoStatsdAtom::kCmdFwReportHandoff);
 }
 
 // Open a staging file (unlinking the previous instance), copy the trace
diff --git a/src/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index bcacb37..1fe4dbd 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -423,8 +423,8 @@
   }
 
   if (base::StartsWith(filename, kApkPrefix)) {
-    symbol_file =
-        root_str + "/" + dirname + "/" + filename.substr(sizeof(kApkPrefix));
+    symbol_file = root_str + "/" + dirname + "/" +
+                  filename.substr(sizeof(kApkPrefix) - 1);
     result = IsCorrectFile(symbol_file, build_id);
     if (result) {
       return result;
@@ -438,7 +438,7 @@
   }
 
   if (base::StartsWith(filename, kApkPrefix)) {
-    symbol_file = root_str + "/" + filename.substr(sizeof(kApkPrefix));
+    symbol_file = root_str + "/" + filename.substr(sizeof(kApkPrefix) - 1);
     result = IsCorrectFile(symbol_file, build_id);
     if (result) {
       return result;
diff --git a/src/profiling/symbolizer/local_symbolizer_unittest.cc b/src/profiling/symbolizer/local_symbolizer_unittest.cc
index 921c878..cb0c11d 100644
--- a/src/profiling/symbolizer/local_symbolizer_unittest.cc
+++ b/src/profiling/symbolizer/local_symbolizer_unittest.cc
@@ -162,6 +162,79 @@
   EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
 }
 
+TEST(LocalBinaryFinderTest, AbsolutePath) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("root");
+  tmp.AddDir("root/dir");
+  tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+
+  LocalBinaryFinder finder({tmp.path() + "/root"});
+
+  base::Optional<FoundBinary> bin1 =
+      finder.FindBinary("/dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
+}
+
+TEST(LocalBinaryFinderTest, AbsolutePathWithoutBaseApk) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("root");
+  tmp.AddDir("root/dir");
+  tmp.AddFile("root/dir/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+
+  LocalBinaryFinder finder({tmp.path() + "/root"});
+
+  base::Optional<FoundBinary> bin1 =
+      finder.FindBinary("/dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
+}
+
+TEST(LocalBinaryFinderTest, OnlyFilename) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("root");
+  tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+
+  LocalBinaryFinder finder({tmp.path() + "/root"});
+
+  base::Optional<FoundBinary> bin1 =
+      finder.FindBinary("/ignored_dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
+}
+
+TEST(LocalBinaryFinderTest, OnlyFilenameWithoutBaseApk) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("root");
+  tmp.AddFile("root/elf1.so", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+
+  LocalBinaryFinder finder({tmp.path() + "/root"});
+
+  base::Optional<FoundBinary> bin1 = finder.FindBinary(
+      "/ignored_dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
+}
+
+TEST(LocalBinaryFinderTest, BuildIdSubdir) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("root");
+  tmp.AddDir("root/.build-id");
+  tmp.AddDir("root/.build-id/41");
+  tmp.AddFile("root/.build-id/41/41414141414141414141414141414141414141.debug",
+              CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+
+  LocalBinaryFinder finder({tmp.path() + "/root"});
+
+  base::Optional<FoundBinary> bin1 =
+      finder.FindBinary("/ignored_dir/ignored_name.so", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(
+      bin1.value().file_name,
+      tmp.path() +
+          "/root/.build-id/41/41414141414141414141414141414141414141.debug");
+}
+
 }  // namespace
 }  // namespace profiling
 }  // namespace perfetto
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 4ce7cec..3cbfca5 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -80,6 +80,8 @@
     "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",
@@ -392,6 +394,7 @@
   testonly = true
   sources = [
     "forwarding_trace_parser_unittest.cc",
+    "importers/ftrace/binder_tracker_unittest.cc",
     "importers/ftrace/sched_event_tracker_unittest.cc",
     "importers/fuchsia/fuchsia_trace_utils_unittest.cc",
     "importers/memory_tracker/graph_processor_unittest.cc",
@@ -429,6 +432,7 @@
     "../base",
     "../protozero",
     "../protozero:testing_messages_zero",
+    "containers",
     "containers:unittests",
     "db:unittests",
     "importers/common",
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index 00496b3..3339c82 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -69,10 +69,19 @@
       PERFETTO_CHECK(nullable_vector<StringPool::Id>().IsDense() == IsDense());
       break;
     case ColumnType::kId:
+    case ColumnType::kDummy:
       break;
   }
 }
 
+Column Column::DummyColumn(const char* name,
+                           Table* table,
+                           uint32_t col_idx_in_table) {
+  return Column(name, ColumnType::kDummy, Flag::kNoFlag, table,
+                col_idx_in_table, std::numeric_limits<uint32_t>::max(), nullptr,
+                nullptr);
+}
+
 Column Column::IdColumn(Table* table, uint32_t col_idx, uint32_t row_map_idx) {
   return Column("id", ColumnType::kId, kIdFlags, table, col_idx, row_map_idx,
                 nullptr, nullptr);
@@ -128,6 +137,8 @@
       FilterIntoIdSlow(op, value, rm);
       break;
     }
+    case ColumnType::kDummy:
+      PERFETTO_FATAL("FilterIntoSlow not allowed on dummy column");
   }
 }
 
@@ -443,6 +454,9 @@
         int res = compare::Numeric(a_idx, b_idx);
         return desc ? res > 0 : res < 0;
       });
+      break;
+    case ColumnType::kDummy:
+      PERFETTO_FATAL("StableSort not allowed on dummy column");
   }
 }
 
@@ -469,6 +483,7 @@
 }
 
 const RowMap& Column::row_map() const {
+  PERFETTO_DCHECK(type_ != ColumnType::kDummy);
   return table_->row_maps_[row_map_idx_];
 }
 
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index dacbe60..ba0a981 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -69,11 +69,6 @@
   bool desc;
 };
 
-// Represents a column which is to be joined on.
-struct JoinKey {
-  uint32_t col_idx;
-};
-
 class Table;
 
 // Represents a named, strongly typed list of data.
@@ -201,6 +196,11 @@
                   row_map_idx, ptr, std::move(storage));
   }
 
+  // Creates a Column which does not have any data backing it.
+  static Column DummyColumn(const char* name,
+                            Table* table,
+                            uint32_t col_idx_in_table);
+
   // Creates a Column which returns the index as the value of the row.
   static Column IdColumn(Table* table,
                          uint32_t col_idx_in_table,
@@ -231,6 +231,8 @@
           return base::nullopt;
         return row_map().RowOf(static_cast<uint32_t>(value.long_value));
       }
+      case ColumnType::kDummy:
+        PERFETTO_FATAL("IndexOf not allowed on dummy column");
     }
     PERFETTO_FATAL("For GCC");
   }
@@ -265,6 +267,8 @@
       case ColumnType::kId: {
         PERFETTO_FATAL("Cannot set value on a id column");
       }
+      case ColumnType::kDummy:
+        PERFETTO_FATAL("Set not allowed on dummy column");
     }
   }
 
@@ -330,6 +334,9 @@
   // Returns true if this column is considered an id column.
   bool IsId() const { return type_ == ColumnType::kId; }
 
+  // Returns true if this column is a dummy column.
+  bool IsDummy() const { return type_ == ColumnType::kDummy; }
+
   // Returns true if this column is a nullable column.
   bool IsNullable() const { return (flags_ & Flag::kNonNull) == 0; }
 
@@ -389,9 +396,6 @@
   Order ascending() const { return Order{col_idx_in_table_, false}; }
   Order descending() const { return Order{col_idx_in_table_, true}; }
 
-  // Returns the JoinKey for this Column.
-  JoinKey join_key() const { return JoinKey{col_idx_in_table_}; }
-
   // Returns an iterator to the first entry in this column.
   Iterator begin() const { return Iterator(this, 0); }
 
@@ -434,6 +438,9 @@
 
     // Types generated on the fly.
     kId,
+
+    // Types which don't have any data backing them.
+    kDummy,
   };
 
   friend class Table;
@@ -476,6 +483,8 @@
       }
       case ColumnType::kId:
         return SqlValue::Long(idx);
+      case ColumnType::kDummy:
+        PERFETTO_FATAL("GetAtIdx not allowed on dummy column");
     }
     PERFETTO_FATAL("For GCC");
   }
@@ -586,6 +595,8 @@
         return SqlValue::Type::kDouble;
       case ColumnType::kString:
         return SqlValue::Type::kString;
+      case ColumnType::kDummy:
+        PERFETTO_FATAL("ToSqlValueType not allowed on dummy column");
     }
     PERFETTO_FATAL("For GCC");
   }
diff --git a/src/trace_processor/db/table.cc b/src/trace_processor/db/table.cc
index 940d8b1..019df20 100644
--- a/src/trace_processor/db/table.cc
+++ b/src/trace_processor/db/table.cc
@@ -144,57 +144,5 @@
   return table;
 }
 
-Table Table::LookupJoin(JoinKey left, const Table& other, JoinKey right) {
-  // The join table will have the same size and RowMaps as the left (this)
-  // table because the left column is indexing the right table.
-  Table table(string_pool_, nullptr);
-  table.row_count_ = row_count_;
-  for (const RowMap& rm : row_maps_) {
-    table.row_maps_.emplace_back(rm.Copy());
-  }
-
-  for (const Column& col : columns_) {
-    // We skip id columns as they are misleading on join tables.
-    if (col.IsId())
-      continue;
-    table.columns_.emplace_back(col, &table, table.columns_.size(),
-                                col.row_map_idx_);
-  }
-
-  const Column& left_col = columns_[left.col_idx];
-  const Column& right_col = other.columns_[right.col_idx];
-
-  // For each index in the left column, retrieve the index of the row inside
-  // the RowMap of the right column. By getting the index of the row rather
-  // than the row number itself, we can call |Apply| on the other RowMaps
-  // in the right table.
-  std::vector<uint32_t> indices(row_count_);
-  for (uint32_t i = 0; i < row_count_; ++i) {
-    SqlValue val = left_col.Get(i);
-    PERFETTO_CHECK(val.type != SqlValue::Type::kNull);
-    indices[i] = right_col.IndexOf(val).value();
-  }
-
-  // Apply the computed RowMap to each of the right RowMaps, adding it to the
-  // join table as we go.
-  RowMap rm(std::move(indices));
-  for (const RowMap& ot : other.row_maps_) {
-    table.row_maps_.emplace_back(ot.SelectRows(rm));
-  }
-
-  uint32_t left_row_maps_size = static_cast<uint32_t>(row_maps_.size());
-  for (const Column& col : other.columns_) {
-    // We skip id columns as they are misleading on join tables.
-    if (col.IsId())
-      continue;
-
-    // Ensure that we offset the RowMap index by the number of RowMaps in the
-    // left table.
-    table.columns_.emplace_back(col, &table, table.columns_.size(),
-                                col.row_map_idx_ + left_row_maps_size);
-  }
-  return table;
-}
-
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/db/table.h b/src/trace_processor/db/table.h
index e13ad58..cb54d53 100644
--- a/src/trace_processor/db/table.h
+++ b/src/trace_processor/db/table.h
@@ -38,7 +38,7 @@
   // Iterator over the rows of the table.
   class Iterator {
    public:
-    Iterator(const Table* table) : table_(table) {
+    explicit Iterator(const Table* table) : table_(table) {
       for (const auto& rm : table->row_maps()) {
         its_.emplace_back(rm.IterateRows());
       }
@@ -134,22 +134,6 @@
   // Sorts the Table using the specified order by constraints.
   Table Sort(const std::vector<Order>& od) const;
 
-  // Joins |this| table with the |other| table using the values of column |left|
-  // of |this| table to lookup the row in |right| column of the |other| table.
-  //
-  // Concretely, for each row in the returned table we lookup the value of
-  // |left| in |right|. The found row is used as the values for |other|'s
-  // columns in the returned table.
-  //
-  // This means we obtain the following invariants:
-  //  1. this->size() == ret->size()
-  //  2. this->Rows()[i].Get(j) == ret->Rows()[i].Get(j)
-  //
-  // It also means there are few restrictions on the data in |left| and |right|:
-  //  * |left| is not allowed to have any nulls.
-  //  * |left|'s values must exist in |right|
-  Table LookupJoin(JoinKey left, const Table& other, JoinKey right);
-
   // Extends the table with a new column called |name| with data |sv|.
   template <typename T>
   Table ExtendWithColumn(const char* name,
diff --git a/src/trace_processor/dynamic/ancestor_generator.cc b/src/trace_processor/dynamic/ancestor_generator.cc
index f603d67..c126ced 100644
--- a/src/trace_processor/dynamic/ancestor_generator.cc
+++ b/src/trace_processor/dynamic/ancestor_generator.cc
@@ -111,7 +111,8 @@
 
 std::unique_ptr<Table> AncestorGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   uint32_t column = GetConstraintColumnIndex(type_, context_);
   auto it = std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
     return c.col_idx == column && c.op == FilterOp::kEq;
diff --git a/src/trace_processor/dynamic/ancestor_generator.h b/src/trace_processor/dynamic/ancestor_generator.h
index 2e1f8f0..48947ac 100644
--- a/src/trace_processor/dynamic/ancestor_generator.h
+++ b/src/trace_processor/dynamic/ancestor_generator.h
@@ -48,7 +48,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
   // Returns a RowMap of slice IDs which are ancestors of |slice_id|. Returns
   // NULL if an invalid |slice_id| is given. This is used by
diff --git a/src/trace_processor/dynamic/connected_flow_generator.cc b/src/trace_processor/dynamic/connected_flow_generator.cc
index d3cf976..1032fd7 100644
--- a/src/trace_processor/dynamic/connected_flow_generator.cc
+++ b/src/trace_processor/dynamic/connected_flow_generator.cc
@@ -198,7 +198,8 @@
 
 std::unique_ptr<Table> ConnectedFlowGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   const auto& flow = context_->storage->flow_table();
   const auto& slice = context_->storage->slice_table();
 
diff --git a/src/trace_processor/dynamic/connected_flow_generator.h b/src/trace_processor/dynamic/connected_flow_generator.h
index e6d01b3..b2117af 100644
--- a/src/trace_processor/dynamic/connected_flow_generator.h
+++ b/src/trace_processor/dynamic/connected_flow_generator.h
@@ -55,7 +55,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
  private:
   Mode mode_;
diff --git a/src/trace_processor/dynamic/descendant_generator.cc b/src/trace_processor/dynamic/descendant_generator.cc
index cc33bf1..ebec3f3 100644
--- a/src/trace_processor/dynamic/descendant_generator.cc
+++ b/src/trace_processor/dynamic/descendant_generator.cc
@@ -96,7 +96,8 @@
 
 std::unique_ptr<Table> DescendantGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   const auto& slices = context_->storage->slice_table();
 
   uint32_t column = GetConstraintColumnIndex(context_);
diff --git a/src/trace_processor/dynamic/descendant_generator.h b/src/trace_processor/dynamic/descendant_generator.h
index 6e87a61..216b7e1 100644
--- a/src/trace_processor/dynamic/descendant_generator.h
+++ b/src/trace_processor/dynamic/descendant_generator.h
@@ -43,7 +43,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
   // Returns a RowMap of slice IDs which are descendants of |slice_id|. Returns
   // NULL if an invalid |slice_id| is given. This is used by
diff --git a/src/trace_processor/dynamic/describe_slice_generator.cc b/src/trace_processor/dynamic/describe_slice_generator.cc
index edc610d..500d561 100644
--- a/src/trace_processor/dynamic/describe_slice_generator.cc
+++ b/src/trace_processor/dynamic/describe_slice_generator.cc
@@ -70,7 +70,8 @@
 
 std::unique_ptr<Table> DescribeSliceGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   auto input = GetDescribeSliceInputValues(cs);
   const auto& slices = context_->storage->slice_table();
 
diff --git a/src/trace_processor/dynamic/describe_slice_generator.h b/src/trace_processor/dynamic/describe_slice_generator.h
index af59529..fc7d991 100644
--- a/src/trace_processor/dynamic/describe_slice_generator.h
+++ b/src/trace_processor/dynamic/describe_slice_generator.h
@@ -43,7 +43,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
  private:
   TraceProcessorContext* context_ = nullptr;
diff --git a/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc b/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
index 1af5ad1..617c658 100644
--- a/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
+++ b/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
@@ -119,7 +119,8 @@
 
 std::unique_ptr<Table> ExperimentalAnnotatedStackGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   const auto& cs_table = context_->storage->stack_profile_callsite_table();
   const auto& f_table = context_->storage->stack_profile_frame_table();
   const auto& m_table = context_->storage->stack_profile_mapping_table();
diff --git a/src/trace_processor/dynamic/experimental_annotated_stack_generator.h b/src/trace_processor/dynamic/experimental_annotated_stack_generator.h
index 7e6a62d..f61c166 100644
--- a/src/trace_processor/dynamic/experimental_annotated_stack_generator.h
+++ b/src/trace_processor/dynamic/experimental_annotated_stack_generator.h
@@ -40,7 +40,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
  private:
   TraceProcessorContext* context_ = nullptr;
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator.cc b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
index 88d167f..530fe39 100644
--- a/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
@@ -50,7 +50,8 @@
 
 std::unique_ptr<Table> ExperimentalCounterDurGenerator::ComputeTable(
     const std::vector<Constraint>&,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   if (!dur_column_) {
     dur_column_.reset(
         new NullableVector<int64_t>(ComputeDurColumn(*counter_table_)));
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator.h b/src/trace_processor/dynamic/experimental_counter_dur_generator.h
index 38f9773..4ec9f24 100644
--- a/src/trace_processor/dynamic/experimental_counter_dur_generator.h
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator.h
@@ -35,7 +35,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
-                                      const std::vector<Order>&) override;
+                                      const std::vector<Order>&,
+                                      const BitVector& cols_used) override;
 
   // public + static for testing
   static NullableVector<int64_t> ComputeDurColumn(const Table& table);
diff --git a/src/trace_processor/dynamic/experimental_flamegraph_generator.cc b/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
index 559e3ca..1475eab 100644
--- a/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
+++ b/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
@@ -324,7 +324,8 @@
 
 std::unique_ptr<Table> ExperimentalFlamegraphGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   // Get the input column values and compute the flamegraph using them.
   auto values = GetFlamegraphInputValues(cs);
 
diff --git a/src/trace_processor/dynamic/experimental_flamegraph_generator.h b/src/trace_processor/dynamic/experimental_flamegraph_generator.h
index 652193c..c8531ad 100644
--- a/src/trace_processor/dynamic/experimental_flamegraph_generator.h
+++ b/src/trace_processor/dynamic/experimental_flamegraph_generator.h
@@ -49,7 +49,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
  private:
   TraceProcessorContext* context_ = nullptr;
diff --git a/src/trace_processor/dynamic/experimental_flat_slice_generator.cc b/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
index a05cb81..4e8d697 100644
--- a/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
+++ b/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
@@ -46,7 +46,8 @@
 
 std::unique_ptr<Table> ExperimentalFlatSliceGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   using CI = tables::ExperimentalFlatSliceTable::ColumnIndex;
   auto start_it = std::find_if(cs.begin(), cs.end(), [](const Constraint& c) {
     return c.col_idx == static_cast<uint32_t>(CI::start_bound) &&
diff --git a/src/trace_processor/dynamic/experimental_flat_slice_generator.h b/src/trace_processor/dynamic/experimental_flat_slice_generator.h
index b0838c4..480e477 100644
--- a/src/trace_processor/dynamic/experimental_flat_slice_generator.h
+++ b/src/trace_processor/dynamic/experimental_flat_slice_generator.h
@@ -62,7 +62,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
   // Visibile for testing.
   static std::unique_ptr<tables::ExperimentalFlatSliceTable>
diff --git a/src/trace_processor/dynamic/experimental_sched_upid_generator.cc b/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
index 1906918..60d325f 100644
--- a/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
+++ b/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
@@ -48,7 +48,8 @@
 
 std::unique_ptr<Table> ExperimentalSchedUpidGenerator::ComputeTable(
     const std::vector<Constraint>&,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   if (!upid_column_) {
     upid_column_.reset(new NullableVector<uint32_t>(ComputeUpidColumn()));
   }
diff --git a/src/trace_processor/dynamic/experimental_sched_upid_generator.h b/src/trace_processor/dynamic/experimental_sched_upid_generator.h
index 09e0dfa..e144eea 100644
--- a/src/trace_processor/dynamic/experimental_sched_upid_generator.h
+++ b/src/trace_processor/dynamic/experimental_sched_upid_generator.h
@@ -37,7 +37,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
-                                      const std::vector<Order>&) override;
+                                      const std::vector<Order>&,
+                                      const BitVector& cols_used) override;
 
  private:
   NullableVector<uint32_t> ComputeUpidColumn();
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator.cc b/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
index 5d0f2db..9b842bb 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
@@ -75,7 +75,8 @@
 
 std::unique_ptr<Table> ExperimentalSliceLayoutGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   std::set<TrackId> selected_tracks;
   std::string filter_string = "";
   for (const auto& c : cs) {
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator.h b/src/trace_processor/dynamic/experimental_slice_layout_generator.h
index 4b033f7..b2d731e 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator.h
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator.h
@@ -40,7 +40,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
-                                      const std::vector<Order>&) override;
+                                      const std::vector<Order>&,
+                                      const BitVector& cols_used) override;
 
  private:
   Table ComputeLayoutTable(const Table& table, StringPool::Id filter_id);
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 0f2945a..efb8ae8 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 
+#include "src/trace_processor/containers/bit_vector.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -104,7 +105,8 @@
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
   std::unique_ptr<Table> table = gen.ComputeTable(
-      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {},
+      BitVector());
   ExpectOutput(*table, R"(
  #####
 )");
@@ -122,7 +124,8 @@
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
   std::unique_ptr<Table> table = gen.ComputeTable(
-      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {},
+      BitVector());
   ExpectOutput(*table, R"(
  #####
  #####
@@ -145,7 +148,8 @@
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
   std::unique_ptr<Table> table = gen.ComputeTable(
-      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {},
+      BitVector());
   ExpectOutput(*table, R"(
  #####
  ####
@@ -175,7 +179,8 @@
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
   std::unique_ptr<Table> table = gen.ComputeTable(
-      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {},
+      BitVector());
   ExpectOutput(*table, R"(
  ####
  ##
@@ -210,7 +215,8 @@
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
   std::unique_ptr<Table> table = gen.ComputeTable(
-      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {},
+      BitVector());
   ExpectOutput(*table, R"(
 #### ####
 ##   ##
@@ -242,7 +248,8 @@
 
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
   std::unique_ptr<Table> table = gen.ComputeTable(
-      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {},
+      BitVector());
   ExpectOutput(*table, R"(
 ####
 ##
diff --git a/src/trace_processor/dynamic/thread_state_generator.cc b/src/trace_processor/dynamic/thread_state_generator.cc
index 74115cd..f68f127 100644
--- a/src/trace_processor/dynamic/thread_state_generator.cc
+++ b/src/trace_processor/dynamic/thread_state_generator.cc
@@ -38,7 +38,8 @@
 
 std::unique_ptr<Table> ThreadStateGenerator::ComputeTable(
     const std::vector<Constraint>&,
-    const std::vector<Order>&) {
+    const std::vector<Order>&,
+    const BitVector&) {
   if (!unsorted_thread_state_table_) {
     int64_t trace_end_ts =
         context_->storage->GetTraceTimestampBoundsNs().second;
diff --git a/src/trace_processor/dynamic/thread_state_generator.h b/src/trace_processor/dynamic/thread_state_generator.h
index 439d4bb..db97925 100644
--- a/src/trace_processor/dynamic/thread_state_generator.h
+++ b/src/trace_processor/dynamic/thread_state_generator.h
@@ -40,7 +40,8 @@
   uint32_t EstimateRowCount() override;
   util::Status ValidateConstraints(const QueryConstraints&) override;
   std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob) override;
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used) override;
 
   // Visible for testing.
   std::unique_ptr<tables::ThreadStateTable> ComputeThreadStateTable(
diff --git a/src/trace_processor/importers/BUILD.gn b/src/trace_processor/importers/BUILD.gn
index fceb53f..908ae8d 100644
--- a/src/trace_processor/importers/BUILD.gn
+++ b/src/trace_processor/importers/BUILD.gn
@@ -21,12 +21,10 @@
 
 perfetto_cc_proto_descriptor("gen_cc_chrome_track_event_descriptor") {
   descriptor_name = "chrome_track_event.descriptor"
-  descriptor_target =
-      "../../../protos/third_party/chromium: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:track_event_descriptor"
+  descriptor_target = "../../../protos/perfetto/trace/track_event:descriptor"
 }
diff --git a/src/trace_processor/importers/common/track_tracker.h b/src/trace_processor/importers/common/track_tracker.h
index e38cc66..d51d41a 100644
--- a/src/trace_processor/importers/common/track_tracker.h
+++ b/src/trace_processor/importers/common/track_tracker.h
@@ -95,7 +95,7 @@
                                 StringId description = StringId::Null(),
                                 StringId unit = StringId::Null());
 
-  // Creaates a counter track for values within perf samples.
+  // Creates a counter track for values within perf samples.
   // The tracks themselves are managed by PerfSampleTracker.
   TrackId CreatePerfCounterTrack(StringId name,
                                  uint32_t perf_session_id,
diff --git a/src/trace_processor/importers/default_modules.cc b/src/trace_processor/importers/default_modules.cc
index 19345cf..b14f357 100644
--- a/src/trace_processor/importers/default_modules.cc
+++ b/src/trace_processor/importers/default_modules.cc
@@ -16,6 +16,7 @@
 
 #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"
@@ -38,6 +39,7 @@
   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
diff --git a/src/trace_processor/importers/ftrace/binder_tracker.cc b/src/trace_processor/importers/ftrace/binder_tracker.cc
index 64eee90..fd229f1 100644
--- a/src/trace_processor/importers/ftrace/binder_tracker.cc
+++ b/src/trace_processor/importers/ftrace/binder_tracker.cc
@@ -17,9 +17,11 @@
 #include "src/trace_processor/importers/ftrace/binder_tracker.h"
 #include "perfetto/base/compiler.h"
 #include "perfetto/ext/base/string_utils.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/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
@@ -74,7 +76,6 @@
       flags_(context->storage->InternString("flags")),
       code_(context->storage->InternString("code")),
       calling_tid_(context->storage->InternString("calling tid")),
-      dest_slice_id_(context->storage->InternString("destination slice id")),
       data_size_(context->storage->InternString("data size")),
       offsets_size_(context->storage->InternString("offsets size")) {}
 
@@ -84,8 +85,8 @@
                                 uint32_t tid,
                                 int32_t transaction_id,
                                 int32_t dest_node,
-                                int32_t dest_tgid,
-                                int32_t dest_tid,
+                                uint32_t dest_tgid,
+                                uint32_t dest_tid,
                                 bool is_reply,
                                 uint32_t flags,
                                 StringId code) {
@@ -105,95 +106,95 @@
                                  base::StringView(flag_str))));
     inserter->AddArg(code_, Variadic::String(code));
     inserter->AddArg(calling_tid_, Variadic::UnsignedInteger(tid));
-    // TODO(hjd): The legacy UI included the calling pid in the args,
-    // is this necessary? It's complicated in our case because process
-    // association might not happen until after the binder transaction slices
-    // have been parsed. We would need to backfill the arg.
   };
 
-  if (is_reply) {
-    // Reply slices have accurate dest information, so we can add it.
-    const auto& thread_table = context_->storage->thread_table();
-    UniqueTid utid = context_->process_tracker->GetOrCreateThread(
-        static_cast<uint32_t>(dest_tid));
-    StringId dest_thread_name = thread_table.name()[utid];
-    auto dest_args_inserter = [this, dest_tid, &dest_thread_name](
-                                  ArgsTracker::BoundInserter* inserter) {
-      inserter->AddArg(dest_thread_, Variadic::Integer(dest_tid));
-      inserter->AddArg(dest_name_, Variadic::String(dest_thread_name));
-    };
-    context_->slice_tracker->AddArgs(track_id, binder_category_id_, reply_id_,
-                                     dest_args_inserter);
-    context_->slice_tracker->End(ts, track_id, kNullStringId, kNullStringId,
-                                 args_inserter);
-    awaiting_rcv_for_reply_.insert(transaction_id);
-    return;
-  }
+  bool is_oneway = (flags & kOneWay) == kOneWay;
+  auto insert_slice = [&]() {
+    if (is_reply) {
+      UniqueTid utid = context_->process_tracker->GetOrCreateThread(
+          static_cast<uint32_t>(dest_tid));
+      StringId dest_thread_name =
+          context_->storage->thread_table().name()[utid];
+      auto dest_args_inserter = [this, dest_tid, &dest_thread_name](
+                                    ArgsTracker::BoundInserter* inserter) {
+        inserter->AddArg(dest_thread_, Variadic::Integer(dest_tid));
+        inserter->AddArg(dest_name_, Variadic::String(dest_thread_name));
+      };
+      context_->slice_tracker->AddArgs(track_id, binder_category_id_, reply_id_,
+                                       dest_args_inserter);
+      return context_->slice_tracker->End(ts, track_id, kNullStringId,
+                                          kNullStringId, args_inserter);
+    }
+    if (is_oneway) {
+      return context_->slice_tracker->Scoped(ts, track_id, binder_category_id_,
+                                             transaction_async_id_, 0,
+                                             args_inserter);
+    }
+    return context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
+                                          transaction_slice_id_, args_inserter);
+  };
 
-  bool expects_reply = !is_reply && ((flags & kOneWay) == 0);
-
-  if (expects_reply) {
-    context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
-                                   transaction_slice_id_, args_inserter);
-    transaction_await_rcv[transaction_id] = track_id;
-  } else {
-    context_->slice_tracker->Scoped(ts, track_id, binder_category_id_,
-                                    transaction_async_id_, 0, args_inserter);
-    awaiting_async_rcv_[transaction_id] = args_inserter;
-  }
+  OutstandingTransaction transaction;
+  transaction.is_reply = is_reply;
+  transaction.is_oneway = is_oneway;
+  transaction.args_inserter = args_inserter;
+  transaction.send_track_id = track_id;
+  transaction.send_slice_id = insert_slice();
+  outstanding_transactions_.Insert(transaction_id, std::move(transaction));
 }
 
 void BinderTracker::TransactionReceived(int64_t ts,
                                         uint32_t pid,
                                         int32_t transaction_id) {
+  const OutstandingTransaction* opt_transaction =
+      outstanding_transactions_.Find(transaction_id);
+  if (!opt_transaction) {
+    // If we don't know what type of transaction it is, we don't know how to
+    // insert the slice.
+    // TODO(lalitm): maybe we should insert a dummy slice anyway - seems like
+    // a questionable idea to just ignore these completely.
+    return;
+  }
+
+  OutstandingTransaction transaction(std::move(*opt_transaction));
+  outstanding_transactions_.Erase(transaction_id);
+
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
-  const auto& thread_table = context_->storage->thread_table();
-  StringId thread_name = thread_table.name()[utid];
   TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-  if (awaiting_rcv_for_reply_.count(transaction_id) > 0) {
+
+  if (transaction.is_reply) {
+    // Simply end the slice started back when the first |expects_reply|
+    // transaction was sent.
     context_->slice_tracker->End(ts, track_id);
-    awaiting_rcv_for_reply_.erase(transaction_id);
     return;
   }
 
-  TrackId* rcv_track_id = transaction_await_rcv.Find(transaction_id);
-  if (rcv_track_id) {
-    // First begin the reply slice to get its slice id.
-    auto reply_slice_id = context_->slice_tracker->Begin(
+  base::Optional<SliceId> recv_slice_id;
+  if (transaction.is_oneway) {
+    recv_slice_id = context_->slice_tracker->Scoped(
+        ts, track_id, binder_category_id_, async_rcv_id_, 0,
+        std::move(transaction.args_inserter));
+  } else {
+    if (transaction.send_track_id) {
+      auto args_inserter = [this, utid,
+                            pid](ArgsTracker::BoundInserter* inserter) {
+        inserter->AddArg(dest_thread_, Variadic::UnsignedInteger(pid));
+        inserter->AddArg(
+            dest_name_,
+            Variadic::String(context_->storage->thread_table().name()[utid]));
+      };
+      context_->slice_tracker->AddArgs(*transaction.send_track_id,
+                                       binder_category_id_,
+                                       transaction_slice_id_, args_inserter);
+    }
+    recv_slice_id = context_->slice_tracker->Begin(
         ts, track_id, binder_category_id_, reply_id_);
-    // Add accurate dest info to the binder transaction slice.
-    auto args_inserter = [this, pid, &thread_name, &reply_slice_id](
-                             ArgsTracker::BoundInserter* inserter) {
-      inserter->AddArg(dest_thread_, Variadic::UnsignedInteger(pid));
-      inserter->AddArg(dest_name_, Variadic::String(thread_name));
-      if (reply_slice_id.has_value())
-        inserter->AddArg(dest_slice_id_,
-                         Variadic::UnsignedInteger(reply_slice_id->value));
-    };
-    // Add the dest args to the current transaction slice and get the slice id.
-    auto transaction_slice_id =
-        context_->slice_tracker->AddArgs(*rcv_track_id, binder_category_id_,
-                                         transaction_slice_id_, args_inserter);
-
-    // Add the dest slice id to the reply slice that has just begun.
-    auto reply_dest_inserter =
-        [this, &transaction_slice_id](ArgsTracker::BoundInserter* inserter) {
-          if (transaction_slice_id.has_value())
-            inserter->AddArg(dest_slice_id_, Variadic::UnsignedInteger(
-                                                 transaction_slice_id.value()));
-        };
-    context_->slice_tracker->AddArgs(track_id, binder_category_id_, reply_id_,
-                                     reply_dest_inserter);
-    transaction_await_rcv.Erase(transaction_id);
-    return;
   }
 
-  SetArgsCallback* args = awaiting_async_rcv_.Find(transaction_id);
-  if (args) {
-    context_->slice_tracker->Scoped(ts, track_id, binder_category_id_,
-                                    async_rcv_id_, 0, *args);
-    awaiting_async_rcv_.Erase(transaction_id);
-    return;
+  // Create a flow between the sending slice and this slice.
+  if (transaction.send_slice_id && recv_slice_id) {
+    context_->flow_tracker->InsertFlow(*transaction.send_slice_id,
+                                       *recv_slice_id);
   }
 }
 
diff --git a/src/trace_processor/importers/ftrace/binder_tracker.h b/src/trace_processor/importers/ftrace/binder_tracker.h
index a9482de..aa710e3 100644
--- a/src/trace_processor/importers/ftrace/binder_tracker.h
+++ b/src/trace_processor/importers/ftrace/binder_tracker.h
@@ -21,6 +21,7 @@
 
 #include "perfetto/base/flat_set.h"
 #include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/optional.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"
@@ -50,8 +51,8 @@
                    uint32_t tid,
                    int32_t transaction_id,
                    int32_t dest_node,
-                   int32_t dest_tgid,
-                   int32_t dest_tid,
+                   uint32_t dest_tgid,
+                   uint32_t dest_tid,
                    bool is_reply,
                    uint32_t flags,
                    StringId code);
@@ -67,14 +68,19 @@
                            uint64_t offsets_size);
 
  private:
-  TraceProcessorContext* const context_;
-  base::FlatSet<int32_t> awaiting_rcv_for_reply_;
+  struct OutstandingTransaction {
+    bool is_reply = false;
+    bool is_oneway = false;
+    SetArgsCallback args_inserter;
+    base::Optional<TrackId> send_track_id;
+    base::Optional<SliceId> send_slice_id;
+  };
 
-  base::FlatHashMap<int32_t, TrackId> transaction_await_rcv;
-  base::FlatHashMap<int32_t, SetArgsCallback> awaiting_async_rcv_;
+  TraceProcessorContext* const context_;
+
+  base::FlatHashMap<int32_t, OutstandingTransaction> outstanding_transactions_;
 
   base::FlatHashMap<uint32_t, int64_t> attempt_lock_;
-
   base::FlatHashMap<uint32_t, int64_t> lock_acquired_;
 
   const StringId binder_category_id_;
@@ -93,7 +99,6 @@
   const StringId flags_;
   const StringId code_;
   const StringId calling_tid_;
-  const StringId dest_slice_id_;
   const StringId data_size_;
   const StringId offsets_size_;
 };
diff --git a/src/trace_processor/importers/ftrace/binder_tracker_unittest.cc b/src/trace_processor/importers/ftrace/binder_tracker_unittest.cc
new file mode 100644
index 0000000..7f62c06
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/binder_tracker_unittest.cc
@@ -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.
+ */
+
+#include "src/trace_processor/importers/ftrace/binder_tracker.h"
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/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/storage/trace_storage.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+constexpr int kOneWay = 0x01;
+
+class BinderTrackerTest : public ::testing::Test {
+ public:
+  BinderTrackerTest() {
+    context.storage.reset(new TraceStorage());
+    context.global_args_tracker.reset(new GlobalArgsTracker(&context));
+    context.args_tracker.reset(new ArgsTracker(&context));
+    context.slice_tracker.reset(new SliceTracker(&context));
+    context.process_tracker.reset(new ProcessTracker(&context));
+    context.track_tracker.reset(new TrackTracker(&context));
+    context.flow_tracker.reset(new FlowTracker(&context));
+    binder_tracker = BinderTracker::GetOrCreate(&context);
+  }
+
+ protected:
+  TraceProcessorContext context;
+  BinderTracker* binder_tracker;
+};
+
+TEST_F(BinderTrackerTest, RequestReply) {
+  int64_t req_ts = 100;
+  int64_t req_recv_ts = 105;
+  int64_t rep_ts = 150;
+  int64_t rep_recv_ts = 155;
+
+  uint32_t req_tid = 5;
+  uint32_t rep_tid = 10;
+
+  int32_t req_transaction_id = 1234;
+  int32_t rep_transaction_id = 5678;
+
+  binder_tracker->Transaction(req_ts, req_tid, req_transaction_id, 9, rep_tid,
+                              rep_tid, false, 0, kNullStringId);
+  binder_tracker->TransactionReceived(req_recv_ts, rep_tid, req_transaction_id);
+
+  binder_tracker->Transaction(rep_ts, rep_tid, rep_transaction_id, 99, req_tid,
+                              req_tid, true, 0, kNullStringId);
+  binder_tracker->TransactionReceived(rep_recv_ts, req_tid, rep_transaction_id);
+
+  const auto& thread = context.storage->thread_table();
+  const auto& track = context.storage->thread_track_table();
+  const auto& slice = context.storage->slice_table();
+  const auto& flow = context.storage->flow_table();
+  ASSERT_EQ(slice.row_count(), 2u);
+
+  auto tid_for_slice = [&](uint32_t row) {
+    TrackId track_id = slice.track_id()[row];
+    UniqueTid utid = track.utid()[*track.id().IndexOf(track_id)];
+    return thread.tid()[utid];
+  };
+
+  ASSERT_EQ(slice.ts()[0], req_ts);
+  ASSERT_EQ(slice.dur()[0], rep_recv_ts - req_ts);
+  ASSERT_EQ(tid_for_slice(0), req_tid);
+
+  ASSERT_EQ(slice.ts()[1], req_recv_ts);
+  ASSERT_EQ(slice.dur()[1], rep_ts - req_recv_ts);
+  ASSERT_EQ(tid_for_slice(1), rep_tid);
+
+  ASSERT_EQ(flow.row_count(), 1u);
+  ASSERT_EQ(flow.slice_out()[0], slice.id()[0]);
+  ASSERT_EQ(flow.slice_in()[0], slice.id()[1]);
+}
+
+TEST_F(BinderTrackerTest, Oneway) {
+  int64_t sen_ts = 100;
+  int64_t rec_ts = 150;
+
+  uint32_t sen_tid = 5;
+  uint32_t rec_tid = 10;
+
+  int32_t transaction_id = 1234;
+
+  binder_tracker->Transaction(sen_ts, sen_tid, transaction_id, 9, rec_tid,
+                              rec_tid, false, kOneWay, kNullStringId);
+  binder_tracker->TransactionReceived(rec_ts, rec_tid, transaction_id);
+
+  const auto& thread = context.storage->thread_table();
+  const auto& track = context.storage->thread_track_table();
+  const auto& slice = context.storage->slice_table();
+  const auto& flow = context.storage->flow_table();
+  ASSERT_EQ(slice.row_count(), 2u);
+
+  auto tid_for_slice = [&](uint32_t row) {
+    TrackId track_id = slice.track_id()[row];
+    UniqueTid utid = track.utid()[*track.id().IndexOf(track_id)];
+    return thread.tid()[utid];
+  };
+
+  ASSERT_EQ(slice.ts()[0], sen_ts);
+  ASSERT_EQ(slice.dur()[0], 0);
+  ASSERT_EQ(tid_for_slice(0), sen_tid);
+
+  ASSERT_EQ(slice.ts()[1], rec_ts);
+  ASSERT_EQ(slice.dur()[1], 0);
+  ASSERT_EQ(tid_for_slice(1), rec_tid);
+
+  ASSERT_EQ(flow.row_count(), 1u);
+  ASSERT_EQ(flow.slice_out()[0], slice.id()[0]);
+  ASSERT_EQ(flow.slice_in()[0], slice.id()[1]);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index 4782a3b..e397165 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
 namespace trace_processor {
 namespace {
 
-std::array<MessageDescriptor, 364> descriptors{{
+std::array<MessageDescriptor, 367> descriptors{{
     {nullptr, 0, {}},
     {nullptr, 0, {}},
     {nullptr, 0, {}},
@@ -3876,6 +3876,22 @@
         },
     },
     {
+        "inet_sock_set_state",
+        9,
+        {
+            {},
+            {"daddr", ProtoSchemaType::kUint32},
+            {"dport", ProtoSchemaType::kUint32},
+            {"family", ProtoSchemaType::kUint32},
+            {"newstate", ProtoSchemaType::kInt32},
+            {"oldstate", ProtoSchemaType::kInt32},
+            {"protocol", ProtoSchemaType::kUint32},
+            {"saddr", ProtoSchemaType::kUint32},
+            {"skaddr", ProtoSchemaType::kUint64},
+            {"sport", ProtoSchemaType::kUint32},
+        },
+    },
+    {
         "tcp_retransmit_skb",
         7,
         {
@@ -3890,19 +3906,50 @@
         },
     },
     {
-        "inet_sock_set_state",
-        9,
+        "cros_ec_sensorhub_data",
+        6,
         {
             {},
-            {"daddr", ProtoSchemaType::kUint32},
-            {"dport", ProtoSchemaType::kUint32},
-            {"family", ProtoSchemaType::kUint32},
-            {"newstate", ProtoSchemaType::kInt32},
-            {"oldstate", ProtoSchemaType::kInt32},
+            {"current_time", ProtoSchemaType::kInt64},
+            {"current_timestamp", ProtoSchemaType::kInt64},
+            {"delta", ProtoSchemaType::kInt64},
+            {"ec_fifo_timestamp", ProtoSchemaType::kUint32},
+            {"ec_sensor_num", ProtoSchemaType::kUint32},
+            {"fifo_timestamp", ProtoSchemaType::kInt64},
+        },
+    },
+    {
+        "napi_gro_receive_entry",
+        19,
+        {
+            {},
+            {"data_len", ProtoSchemaType::kUint32},
+            {"gso_size", ProtoSchemaType::kUint32},
+            {"gso_type", ProtoSchemaType::kUint32},
+            {"hash", ProtoSchemaType::kUint32},
+            {"ip_summed", ProtoSchemaType::kUint32},
+            {"l4_hash", ProtoSchemaType::kUint32},
+            {"len", ProtoSchemaType::kUint32},
+            {"mac_header", ProtoSchemaType::kInt32},
+            {"mac_header_valid", ProtoSchemaType::kUint32},
+            {"name", ProtoSchemaType::kString},
+            {"napi_id", ProtoSchemaType::kUint32},
+            {"nr_frags", ProtoSchemaType::kUint32},
             {"protocol", ProtoSchemaType::kUint32},
-            {"saddr", ProtoSchemaType::kUint32},
-            {"skaddr", ProtoSchemaType::kUint64},
-            {"sport", ProtoSchemaType::kUint32},
+            {"queue_mapping", ProtoSchemaType::kUint32},
+            {"skbaddr", ProtoSchemaType::kUint64},
+            {"truesize", ProtoSchemaType::kUint32},
+            {"vlan_proto", ProtoSchemaType::kUint32},
+            {"vlan_tagged", ProtoSchemaType::kUint32},
+            {"vlan_tci", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "napi_gro_receive_exit",
+        1,
+        {
+            {},
+            {"ret", ProtoSchemaType::kInt32},
         },
     },
 }};
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index e9bdddb..9d5e382 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -132,9 +132,11 @@
       irq_id_(context_->storage->InternString("irq")),
       tcp_state_id_(context_->storage->InternString("tcp_state")),
       tcp_event_id_(context_->storage->InternString("tcp_event")),
+      napi_gro_id_(context_->storage->InternString("napi_gro")),
       tcp_retransmited_name_id_(
           context_->storage->InternString("TCP Retransmit Skb")),
       ret_arg_id_(context_->storage->InternString("ret")),
+      len_arg_id_(context->storage->InternString("len")),
       direct_reclaim_nr_reclaimed_id_(
           context->storage->InternString("direct_reclaim_nr_reclaimed")),
       direct_reclaim_order_id_(
@@ -666,6 +668,14 @@
         ParseTcpRetransmitSkb(ts, data);
         break;
       }
+      case FtraceEvent::kNapiGroReceiveEntryFieldNumber: {
+        ParseNapiGroReceiveEntry(cpu, ts, data);
+        break;
+      }
+      case FtraceEvent::kNapiGroReceiveExitFieldNumber: {
+        ParseNapiGroReceiveExit(cpu, ts, data);
+        break;
+      }
       default:
         break;
     }
@@ -1279,8 +1289,8 @@
   protos::pbzero::BinderTransactionFtraceEvent::Decoder evt(blob.data,
                                                             blob.size);
   int32_t dest_node = static_cast<int32_t>(evt.target_node());
-  int32_t dest_tgid = static_cast<int32_t>(evt.to_proc());
-  int32_t dest_tid = static_cast<int32_t>(evt.to_thread());
+  uint32_t dest_tgid = static_cast<uint32_t>(evt.to_proc());
+  uint32_t dest_tid = static_cast<uint32_t>(evt.to_thread());
   int32_t transaction_id = static_cast<int32_t>(evt.debug_id());
   bool is_reply = static_cast<int32_t>(evt.reply()) == 1;
   uint32_t flags = static_cast<uint32_t>(evt.flags());
@@ -1724,12 +1734,10 @@
   if (!id) {
     return;
   }
-  // Store cpu & len as args for metrics computation
-  StringId cpu_key = context_->storage->InternString("cpu");
-  StringId len_key = context_->storage->InternString("len");
+  // Store cpu & len as args for metrics computation.
   context_->args_tracker->AddArgsTo(*id)
-      .AddArg(cpu_key, Variadic::UnsignedInteger(cpu))
-      .AddArg(len_key, Variadic::UnsignedInteger(evt.len()));
+      .AddArg(cpu_id_, Variadic::UnsignedInteger(cpu))
+      .AddArg(len_arg_id_, Variadic::UnsignedInteger(evt.len()));
 }
 
 void FtraceParser::ParseInetSockSetState(int64_t timestamp,
@@ -1809,5 +1817,41 @@
                                   slice_name_id, 0);
 }
 
+void FtraceParser::ParseNapiGroReceiveEntry(uint32_t cpu,
+                                            int64_t timestamp,
+                                            protozero::ConstBytes blob) {
+  protos::pbzero::NapiGroReceiveEntryFtraceEvent::Decoder evt(blob.data,
+                                                              blob.size);
+  base::StackString<255> track_name("Napi Gro Cpu %d", cpu);
+  StringId track_name_id =
+      context_->storage->InternString(track_name.string_view());
+  base::StringView net_device = evt.name();
+  StringId slice_name_id = context_->storage->InternString(net_device);
+  TrackId track = context_->track_tracker->InternCpuTrack(track_name_id, cpu);
+  auto len = evt.len();
+  auto args_inserter = [this, len](ArgsTracker::BoundInserter* inserter) {
+    inserter->AddArg(len_arg_id_, Variadic::Integer(len));
+  };
+  context_->slice_tracker->Begin(timestamp, track, napi_gro_id_, slice_name_id,
+                                 args_inserter);
+}
+
+void FtraceParser::ParseNapiGroReceiveExit(uint32_t cpu,
+                                           int64_t timestamp,
+                                           protozero::ConstBytes blob) {
+  protos::pbzero::NapiGroReceiveExitFtraceEvent::Decoder evt(blob.data,
+                                                             blob.size);
+  base::StackString<255> track_name("Napi Gro Cpu %d", cpu);
+  StringId track_name_id =
+      context_->storage->InternString(track_name.string_view());
+  TrackId track = context_->track_tracker->InternCpuTrack(track_name_id, cpu);
+  auto ret = evt.ret();
+  auto args_inserter = [this, ret](ArgsTracker::BoundInserter* inserter) {
+    inserter->AddArg(ret_arg_id_, Variadic::Integer(ret));
+  };
+  context_->slice_tracker->End(timestamp, track, napi_gro_id_, {},
+                               args_inserter);
+}
+
 }  // 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 6863cac..cb040de 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -161,6 +161,12 @@
                              uint32_t pid,
                              protozero::ConstBytes);
   void ParseTcpRetransmitSkb(int64_t timestamp, protozero::ConstBytes);
+  void ParseNapiGroReceiveEntry(uint32_t cpu,
+                                int64_t timestamp,
+                                protozero::ConstBytes);
+  void ParseNapiGroReceiveExit(uint32_t cpu,
+                               int64_t timestamp,
+                               protozero::ConstBytes);
 
   TraceProcessorContext* context_;
   RssStatTracker rss_stat_tracker_;
@@ -190,8 +196,10 @@
   const StringId irq_id_;
   const StringId tcp_state_id_;
   const StringId tcp_event_id_;
+  const StringId napi_gro_id_;
   const StringId tcp_retransmited_name_id_;
   const StringId ret_arg_id_;
+  const StringId len_arg_id_;
   const StringId direct_reclaim_nr_reclaimed_id_;
   const StringId direct_reclaim_order_id_;
   const StringId direct_reclaim_may_writepage_id_;
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
index 5e61475..74de318 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
@@ -262,14 +262,8 @@
           UniqueTid utid =
               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
                                   static_cast<uint32_t>(tinfo.pid));
-          InstantId id = context_->event_tracker->PushInstant(
-              ts, name, utid, RefType::kRefUtid);
-          auto inserter = context_->args_tracker->AddArgsTo(id);
-          for (const Arg& arg : args) {
-            inserter.AddArg(
-                arg.name, arg.value.ToStorageVariadic(context_->storage.get()));
-          }
-          context_->args_tracker->Flush();
+          TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+          slices->Scoped(ts, track_id, cat, name, 0, insert_args);
           break;
         }
         case kCounter: {
@@ -379,9 +373,6 @@
           break;
         }
         case kAsyncInstant: {
-          // TODO(eseckler): Consider storing these instants as 0-duration
-          // slices instead, so that they get nested underneath begin/end
-          // slices.
           int64_t correlation_id;
           if (!cursor.ReadInt64(&correlation_id)) {
             context_->storage->IncrementStats(stats::fuchsia_invalid_event);
@@ -391,15 +382,7 @@
               procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
           TrackId track_id = context_->track_tracker->InternFuchsiaAsyncTrack(
               name, upid, correlation_id);
-          InstantId id = context_->event_tracker->PushInstant(
-              ts, name, track_id.value, RefType::kRefTrack);
-          auto inserter = context_->args_tracker->AddArgsTo(id);
-          for (const Arg& arg : args) {
-            inserter.AddArg(
-                arg.name, arg.name,
-                arg.value.ToStorageVariadic(context_->storage.get()));
-          }
-          context_->args_tracker->Flush();
+          slices->Scoped(ts, track_id, cat, name, 0, insert_args);
           break;
         }
         case kAsyncEnd: {
diff --git a/src/trace_processor/importers/gzip/gzip_trace_parser.cc b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
index e8e9494..640f2f1 100644
--- a/src/trace_processor/importers/gzip/gzip_trace_parser.cc
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
@@ -75,7 +75,7 @@
   constexpr size_t kUncompressedBufferSize = 32 * 1024 * 1024;
 
   needs_more_input_ = false;
-  decompressor_.SetInput(start, len);
+  decompressor_.Feed(start, len);
 
   for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
     if (!buffer_) {
@@ -84,10 +84,10 @@
     }
 
     auto result =
-        decompressor_.Decompress(buffer_.get() + bytes_written_,
-                                 kUncompressedBufferSize - bytes_written_);
+        decompressor_.ExtractOutput(buffer_.get() + bytes_written_,
+                                    kUncompressedBufferSize - bytes_written_);
     ret = result.ret;
-    if (ret == ResultCode::kError || ret == ResultCode::kNoProgress)
+    if (ret == ResultCode::kError)
       return util::ErrStatus("Failed to decompress trace chunk");
 
     if (ret == ResultCode::kNeedsMoreInput) {
diff --git a/src/trace_processor/importers/proto/android_camera_event_module.cc b/src/trace_processor/importers/proto/android_camera_event_module.cc
new file mode 100644
index 0000000..7082310
--- /dev/null
+++ b/src/trace_processor/importers/proto/android_camera_event_module.cc
@@ -0,0 +1,92 @@
+/*
+ * 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/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/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"
+#include "src/trace_processor/timestamped_trace_piece.h"
+#include "src/trace_processor/trace_sorter.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using perfetto::protos::pbzero::TracePacket;
+
+AndroidCameraEventModule::AndroidCameraEventModule(
+    TraceProcessorContext* context)
+    : context_(context) {
+  RegisterForField(TracePacket::kAndroidCameraFrameEventFieldNumber, context);
+}
+
+AndroidCameraEventModule::~AndroidCameraEventModule() = default;
+
+ModuleResult AndroidCameraEventModule::TokenizePacket(
+    const protos::pbzero::TracePacket::Decoder& decoder,
+    TraceBlobView* packet,
+    int64_t /*packet_timestamp*/,
+    PacketSequenceState* state,
+    uint32_t field_id) {
+  if (field_id != TracePacket::kAndroidCameraFrameEventFieldNumber) {
+    return ModuleResult::Ignored();
+  }
+  const auto android_camera_frame_event =
+      protos::pbzero::AndroidCameraFrameEvent::Decoder(
+          decoder.android_camera_frame_event());
+  context_->sorter->PushTracePacket(
+      android_camera_frame_event.request_processing_started_ns(), state,
+      std::move(*packet));
+  return ModuleResult::Handled();
+}
+
+void AndroidCameraEventModule::ParsePacket(const TracePacket::Decoder& decoder,
+                                           const TimestampedTracePiece& /*ttp*/,
+                                           uint32_t field_id) {
+  if (field_id != TracePacket::kAndroidCameraFrameEventFieldNumber) {
+    return;
+  }
+  InsertCameraFrameSlice(decoder.android_camera_frame_event());
+}
+
+void AndroidCameraEventModule::InsertCameraFrameSlice(
+    protozero::ConstBytes bytes) {
+  const auto android_camera_frame_event =
+      protos::pbzero::AndroidCameraFrameEvent::Decoder(bytes);
+  StringId track_name = context_->storage->InternString(
+      base::StackString<32>("Camera %d Frames",
+                            android_camera_frame_event.camera_id())
+          .string_view());
+  StringId slice_name = context_->storage->InternString(
+      base::StackString<32>("Frame %" PRId64,
+                            android_camera_frame_event.frame_number())
+          .string_view());
+  int64_t ts = android_camera_frame_event.request_processing_started_ns();
+  int64_t dur = android_camera_frame_event.responses_all_sent_ns() -
+                android_camera_frame_event.request_processing_started_ns();
+  auto track_set_id =
+      context_->async_track_set_tracker->InternGlobalTrackSet(track_name);
+  auto track_id =
+      context_->async_track_set_tracker->Scoped(track_set_id, ts, dur);
+  context_->slice_tracker->Scoped(ts, track_id, /*category=*/kNullStringId,
+                                  slice_name, dur);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/android_camera_event_module.h b/src/trace_processor/importers/proto/android_camera_event_module.h
new file mode 100644
index 0000000..fd117c6
--- /dev/null
+++ b/src/trace_processor/importers/proto/android_camera_event_module.h
@@ -0,0 +1,58 @@
+/*
+ * 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_ANDROID_CAMERA_EVENT_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_CAMERA_EVENT_MODULE_H_
+
+#include <cstdint>
+#include <unordered_map>
+
+#include "perfetto/ext/base/optional.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.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"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class AndroidCameraEventModule : public ProtoImporterModule {
+ public:
+  explicit AndroidCameraEventModule(TraceProcessorContext* context);
+
+  ~AndroidCameraEventModule() override;
+
+  ModuleResult TokenizePacket(const protos::pbzero::TracePacket::Decoder& decoder,
+                              TraceBlobView* packet,
+                              int64_t packet_timestamp,
+                              PacketSequenceState* state,
+                              uint32_t field_id) override;
+
+  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
+                   const TimestampedTracePiece& ttp,
+                   uint32_t field_id) override;
+
+ private:
+  void InsertCameraFrameSlice(protozero::ConstBytes bytes);
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_CAMERA_EVENT_MODULE_H_
diff --git a/src/trace_processor/importers/proto/async_track_set_tracker.cc b/src/trace_processor/importers/proto/async_track_set_tracker.cc
index c45148c..2593a12 100644
--- a/src/trace_processor/importers/proto/async_track_set_tracker.cc
+++ b/src/trace_processor/importers/proto/async_track_set_tracker.cc
@@ -133,8 +133,10 @@
         return state.slice_type == TrackState::SliceType::kTimestamp &&
                state.ts_end <= ts;
       });
-  if (it != set.tracks.end())
+  if (it != set.tracks.end()) {
+    it->ts_end = ts + dur;
     return it->id;
+  }
 
   TrackState state;
   state.slice_type = TrackState::SliceType::kTimestamp;
diff --git a/src/trace_processor/importers/proto/heap_graph_module.cc b/src/trace_processor/importers/proto/heap_graph_module.cc
index 309590a..2172533 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.cc
+++ b/src/trace_processor/importers/proto/heap_graph_module.cc
@@ -138,6 +138,7 @@
                      decoder.heap_graph());
       return;
     case TracePacket::kDeobfuscationMappingFieldNumber:
+      HeapGraphTracker::GetOrCreate(context_)->FinalizeAllProfiles();
       ParseDeobfuscationMapping(decoder.deobfuscation_mapping());
       return;
   }
@@ -288,7 +289,6 @@
   const std::vector<tables::HeapGraphClassTable::Id>* cls_objects =
       heap_graph_tracker->RowsForType(package_name_id,
                                       obfuscated_class_name_id);
-
   if (cls_objects) {
     for (tables::HeapGraphClassTable::Id id : *cls_objects) {
       uint32_t row =
@@ -375,7 +375,7 @@
 
 void HeapGraphModule::NotifyEndOfFile() {
   auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
-  heap_graph_tracker->NotifyEndOfFile();
+  heap_graph_tracker->FinalizeAllProfiles();
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.cc b/src/trace_processor/importers/proto/heap_graph_tracker.cc
index f789511..1224972 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.cc
@@ -643,39 +643,33 @@
     base::StringView normalized_type =
         NormalizeTypeName(context_->storage->GetString(interned_type.name));
 
-    // Annoyingly, some apps have a relative path to base.apk. We take this to
-    // mean the main package, so we treat it as if the location was unknown.
-    bool is_base_apk = false;
+    base::Optional<StringPool::Id> class_package;
     if (location_name) {
-      base::StringView base_apk("base.apk");
-      is_base_apk = context_->storage->GetString(*location_name)
-                        .substr(0, base_apk.size()) == base_apk;
-    }
-
-    if (location_name && !is_base_apk) {
       base::Optional<std::string> package_name =
           PackageFromLocation(context_->storage.get(),
                               context_->storage->GetString(*location_name));
       if (package_name) {
-        class_to_rows_[std::make_pair(
-                           context_->storage->InternString(
-                               base::StringView(*package_name)),
-                           context_->storage->InternString(normalized_type))]
-            .emplace_back(type_id);
+        class_package =
+            context_->storage->InternString(base::StringView(*package_name));
       }
-    } else {
-      // TODO(b/153552977): Remove this workaround.
-      // For profiles collected for old versions of perfetto_hprof, we do not
-      // have any location information. We store them using the nullopt
-      // location, and assume they are all part of the main APK.
-      //
-      // This is to keep ingestion of old profiles working (especially
-      // important for the UI).
-      class_to_rows_[std::make_pair(
-                         base::nullopt,
-                         context_->storage->InternString(normalized_type))]
-          .emplace_back(type_id);
     }
+    if (!class_package) {
+      auto app_id = context_->storage->process_table()
+                        .android_appid()[sequence_state.current_upid];
+      if (app_id) {
+        auto pkg_row =
+            context_->storage->package_list_table().uid().IndexOf(*app_id);
+        if (pkg_row) {
+          class_package =
+              context_->storage->package_list_table().package_name()[*pkg_row];
+        }
+      }
+    }
+
+    class_to_rows_[std::make_pair(
+                       class_package,
+                       context_->storage->InternString(normalized_type))]
+        .emplace_back(type_id);
   }
 
   if (!sequence_state.deferred_size_objects_for_type_.empty() ||
@@ -1057,7 +1051,7 @@
   return tbl;
 }
 
-void HeapGraphTracker::NotifyEndOfFile() {
+void HeapGraphTracker::FinalizeAllProfiles() {
   if (!sequence_state_.empty()) {
     context_->storage->IncrementStats(stats::heap_graph_non_finalized_graph);
     // There might still be valuable data even though the trace is truncated.
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.h b/src/trace_processor/importers/proto/heap_graph_tracker.h
index 7f804ed..5255639 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.h
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.h
@@ -119,10 +119,10 @@
                                uint64_t intern_id,
                                StringPool::Id str);
   void FinalizeProfile(uint32_t seq);
+  void FinalizeAllProfiles();
   void SetPacketIndex(uint32_t seq_id, uint64_t index);
 
   ~HeapGraphTracker() override;
-  void NotifyEndOfFile();
 
   const std::vector<tables::HeapGraphClassTable::Id>* RowsForType(
       base::Optional<StringPool::Id> package_name,
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
index 6e76214..2d01187 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
@@ -16,9 +16,9 @@
 
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
 
-#include "src/trace_processor/importers/proto/profiler_util.h"
-
 #include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/proto/profiler_util.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -53,6 +53,8 @@
 
   TraceProcessorContext context;
   context.storage.reset(new TraceStorage());
+  context.process_tracker.reset(new ProcessTracker(&context));
+  context.process_tracker->GetOrCreateProcess(kPid);
 
   HeapGraphTracker tracker(&context);
 
@@ -193,6 +195,8 @@
 
   TraceProcessorContext context;
   context.storage.reset(new TraceStorage());
+  context.process_tracker.reset(new ProcessTracker(&context));
+  context.process_tracker->GetOrCreateProcess(kPid);
 
   HeapGraphTracker tracker(&context);
 
diff --git a/src/trace_processor/importers/proto/metadata_tracker.cc b/src/trace_processor/importers/proto/metadata_tracker.cc
index e286dea..7f324b9 100644
--- a/src/trace_processor/importers/proto/metadata_tracker.cc
+++ b/src/trace_processor/importers/proto/metadata_tracker.cc
@@ -16,12 +16,20 @@
 
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
 
+#include "perfetto/ext/base/crash_keys.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/storage/metadata.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
 namespace trace_processor {
 
+namespace {
+
+base::CrashKey g_crash_key_uuid("trace_uuid");
+
+}
+
 MetadataTracker::MetadataTracker(TraceProcessorContext* context)
     : context_(context) {
   for (uint32_t i = 0; i < kNumKeys; ++i) {
@@ -37,6 +45,13 @@
   PERFETTO_DCHECK(metadata::kKeyTypes[key] == metadata::KeyType::kSingle);
   PERFETTO_DCHECK(value.type == metadata::kValueTypes[key]);
 
+  // When the trace_uuid is set, store a copy in a crash key, so in case of
+  // a crash in the pipelines we can tell which trace caused the crash.
+  if (key == metadata::trace_uuid && value.type == Variadic::kString) {
+    auto uuid_string_view = context_->storage->GetString(value.string_value);
+    g_crash_key_uuid.Set(uuid_string_view);
+  }
+
   auto* metadata_table = context_->storage->mutable_metadata_table();
   uint32_t key_idx = static_cast<uint32_t>(key);
   base::Optional<uint32_t> opt_row =
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 0c3f315..3ce7b77 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -166,6 +166,22 @@
                     static_cast<int64_t>(evt.chunks_discarded()));
   storage->SetStats(stats::traced_patches_discarded,
                     static_cast<int64_t>(evt.patches_discarded()));
+  storage->SetStats(stats::traced_flushes_requested,
+                    static_cast<int64_t>(evt.flushes_requested()));
+  storage->SetStats(stats::traced_flushes_succeeded,
+                    static_cast<int64_t>(evt.flushes_succeeded()));
+  storage->SetStats(stats::traced_flushes_failed,
+                    static_cast<int64_t>(evt.flushes_failed()));
+  switch (evt.final_flush_outcome()) {
+    case protos::pbzero::TraceStats::FINAL_FLUSH_SUCCEEDED:
+      storage->IncrementStats(stats::traced_final_flush_succeeded, 1);
+      break;
+    case protos::pbzero::TraceStats::FINAL_FLUSH_FAILED:
+      storage->IncrementStats(stats::traced_final_flush_failed, 1);
+      break;
+    case protos::pbzero::TraceStats::FINAL_FLUSH_UNSPECIFIED:
+      break;
+  }
 
   int buf_num = 0;
   for (auto it = evt.buffer_stats(); it; ++it, ++buf_num) {
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
index 1a96191..b6b5937 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
@@ -28,26 +28,21 @@
                                              TraceBlobView* output) {
   PERFETTO_DCHECK(util::IsGzipSupported());
 
-  uint8_t zbuf[4096];
-
   std::vector<uint8_t> data;
   data.reserve(input.length());
 
   // Ensure that the decompressor is able to cope with a new stream of data.
   decompressor_.Reset();
-  decompressor_.SetInput(input.data(), input.length());
-
   using ResultCode = util::GzipDecompressor::ResultCode;
-  for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
-    auto res = decompressor_.Decompress(zbuf, base::ArraySize(zbuf));
-    ret = res.ret;
-    if (ret == ResultCode::kError || ret == ResultCode::kNoProgress ||
-        ret == ResultCode::kNeedsMoreInput) {
-      return util::ErrStatus("Failed to decompress (error code: %d)",
-                             static_cast<int>(ret));
-    }
+  ResultCode ret = decompressor_.FeedAndExtract(
+      input.data(), input.length(),
+      [&data](const uint8_t* buffer, size_t buffer_len) {
+        data.insert(data.end(), buffer, buffer + buffer_len);
+      });
 
-    data.insert(data.end(), zbuf, zbuf + res.bytes_written);
+  if (ret == ResultCode::kError || ret == ResultCode::kNeedsMoreInput) {
+    return util::ErrStatus("Failed to decompress (error code: %d)",
+                           static_cast<int>(ret));
   }
 
   TraceBlob out_blob = TraceBlob::CopyFrom(data.data(), data.size());
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.cc b/src/trace_processor/importers/systrace/systrace_line_parser.cc
index 9120e65..4cfd788 100644
--- a/src/trace_processor/importers/systrace/systrace_line_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.cc
@@ -41,6 +41,7 @@
       rss_stat_tracker_(context_),
       sched_wakeup_name_id_(ctx->storage->InternString("sched_wakeup")),
       sched_waking_name_id_(ctx->storage->InternString("sched_waking")),
+      cpufreq_name_id_(ctx->storage->InternString("cpufreq")),
       cpuidle_name_id_(ctx->storage->InternString("cpuidle")),
       workqueue_name_id_(ctx->storage->InternString("workqueue")),
       sched_blocked_reason_id_(
@@ -123,6 +124,19 @@
     context_->args_tracker->AddArgsTo(instant_id)
         .AddArg(waker_utid_id_, Variadic::UnsignedInteger(utid));
 
+  } else if (line.event_name == "cpu_frequency") {
+    base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
+    base::Optional<double> new_state = base::StringToDouble(args["state"]);
+    if (!event_cpu.has_value()) {
+      return util::Status("Could not convert event cpu");
+    }
+    if (!event_cpu.has_value()) {
+      return util::Status("Could not convert state");
+    }
+
+    TrackId track = context_->track_tracker->InternCpuCounterTrack(
+        cpufreq_name_id_, event_cpu.value());
+    context_->event_tracker->PushCounter(line.ts, new_state.value(), track);
   } else if (line.event_name == "cpu_idle") {
     base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
     base::Optional<double> new_state = base::StringToDouble(args["state"]);
@@ -139,8 +153,8 @@
   } else if (line.event_name == "binder_transaction") {
     auto id = base::StringToInt32(args["transaction"]);
     auto dest_node = base::StringToInt32(args["dest_node"]);
-    auto dest_tgid = base::StringToInt32(args["dest_proc"]);
-    auto dest_tid = base::StringToInt32(args["dest_thread"]);
+    auto dest_tgid = base::StringToUInt32(args["dest_proc"]);
+    auto dest_tid = base::StringToUInt32(args["dest_thread"]);
     auto is_reply = base::StringToInt32(args["reply"]).value() == 1;
     auto flags_str = args["flags"];
     char* end;
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.h b/src/trace_processor/importers/systrace/systrace_line_parser.h
index 24331fe..126fca7 100644
--- a/src/trace_processor/importers/systrace/systrace_line_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.h
@@ -39,6 +39,7 @@
 
   const StringId sched_wakeup_name_id_ = kNullStringId;
   const StringId sched_waking_name_id_ = kNullStringId;
+  const StringId cpufreq_name_id_ = kNullStringId;
   const StringId cpuidle_name_id_ = kNullStringId;
   const StringId workqueue_name_id_ = kNullStringId;
   const StringId sched_blocked_reason_id_ = kNullStringId;
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index 4135e50..f8de2ad 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -69,6 +69,7 @@
   "android/android_multiuser_populator.sql",
   "android/span_view_stats.sql",
   "android/android_sysui_cuj.sql",
+  "android/android_sysui_cuj_surfaceflinger.sql",
   "android/android_sysui_cuj_jank_query.sql",
   "android/process_counter_span_view.sql",
   "android/global_counter_span_view.sql",
diff --git a/src/trace_processor/metrics/sql/android/android_netperf.sql b/src/trace_processor/metrics/sql/android/android_netperf.sql
index 8a67a7d..6471d94 100644
--- a/src/trace_processor/metrics/sql/android/android_netperf.sql
+++ b/src/trace_processor/metrics/sql/android/android_netperf.sql
@@ -26,6 +26,17 @@
   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;
+
 DROP VIEW IF EXISTS tx_packets;
 CREATE VIEW tx_packets AS
   SELECT
@@ -47,6 +58,16 @@
  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";
+
 DROP VIEW IF EXISTS device_per_core_ingress_traffic;
 CREATE VIEW device_per_core_ingress_traffic AS
   SELECT
@@ -128,7 +149,16 @@
                 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
@@ -241,6 +271,11 @@
            runtime/total_packet/1e6
          FROM total_net_rx_action_statistic
        )
+    ),
+    'retransmission_rate', (
+      SELECT
+        (SELECT cnt FROM tcp_retransmitted_count) * 100.0 / COUNT(1)
+      FROM tx_packets
     )
   );
 
diff --git a/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql b/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
index 073c247..48b5b41 100644
--- a/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
+++ b/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
@@ -31,7 +31,8 @@
   JOIN process_metadata USING (upid)
   WHERE
     slice.name GLOB 'J<*>'
-    AND slice.dur > 0
+    -- Filter out CUJs that are <4ms long - assuming CUJ was cancelled.
+    AND slice.dur > 4000000
     AND (
       process.name GLOB 'com.google.android*'
       OR process.name GLOB 'com.android.*')
@@ -57,6 +58,22 @@
 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,
+    MAX(a.dur) as dur_actual
+  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
@@ -83,18 +100,18 @@
 SELECT
   slices.*,
   CASE
-    WHEN fte.ts_actual IS NULL
+    WHEN fte.ts_expected IS NULL
     THEN ts
-    ELSE MAX(COALESCE(slices.ts_prev_frame_end, 0), fte.ts_actual)
+    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_timeline_events fte
-ON slices.vsync = fte.vsync
+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 AND slices.ts <= fte.ts_actual + fte.dur_actual;
+AND slices.ts >= fte.ts_actual AND slices.ts <= (fte.ts_actual + fte.dur_actual);
 
 DROP TABLE IF EXISTS android_sysui_cuj_ts_boundaries;
 CREATE TABLE android_sysui_cuj_ts_boundaries AS
@@ -169,57 +186,47 @@
 DROP TABLE IF EXISTS android_sysui_cuj_frames;
 CREATE TABLE android_sysui_cuj_frames AS
   WITH gcs_to_rt_match AS (
-    -- Match GPU Completion with the last RT slice before it
     SELECT
-      gcs.ts as gcs_ts,
-      gcs.ts_end as gcs_ts_end,
-      gcs.dur as gcs_dur,
-      gcs.idx as idx,
-      MAX(rts.ts) as rts_ts
-    FROM android_sysui_cuj_gpu_completion_slices_in_cuj gcs
-    JOIN android_sysui_cuj_render_thread_slices_in_cuj rts ON rts.ts < gcs.ts
+      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')
-    GROUP BY gcs.ts, gcs.ts_end, gcs.dur, gcs.idx
-  ),
-  frame_boundaries AS (
-    -- Match main thread doFrame with RT DrawFrame and optional GPU Completion
-    SELECT
-      mts.ts_adjusted as mts_ts,
-      mts.ts_end as mts_ts_end,
-      mts.ts_end - mts.ts_adjusted as mts_dur,
-      mts.vsync as vsync,
-      MAX(gcs_rt.gcs_ts) as gcs_ts_start,
-      MAX(gcs_rt.gcs_ts_end) as gcs_ts_end
-    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.rts_ts = rts.ts
-    GROUP BY mts.ts, mts.ts_end, mts.dur
   )
   SELECT
-    ROW_NUMBER() OVER (ORDER BY f.mts_ts) AS frame_number,
-    f.vsync as vsync,
-    f.mts_ts as ts_main_thread_start,
-    f.mts_ts_end as ts_main_thread_end,
-    f.mts_dur AS dur_main_thread,
+    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,
-    MAX(gcs_rt.gcs_ts_end) AS ts_frame_end,
-    MAX(gcs_rt.gcs_ts_end) - f.mts_ts AS dur_frame,
-    SUM(gcs_rt.gcs_ts_end - MAX(COALESCE(hwc.ts_end, 0), gcs_rt.gcs_ts)) as dur_gcs,
-    COUNT(DISTINCT(rts.ts)) as draw_frames,
-    COUNT(DISTINCT(gcs_rt.gcs_ts)) as gpu_completions
-  FROM frame_boundaries f
-  JOIN android_sysui_cuj_draw_frame_slices_in_cuj rts
-    ON f.vsync = rts.vsync
-  LEFT JOIN gcs_to_rt_match gcs_rt
-    ON rts.ts = gcs_rt.rts_ts
-  LEFT JOIN android_sysui_cuj_hwc_release_slices_in_cuj hwc USING (idx)
-  GROUP BY f.mts_ts
-  HAVING gpu_completions >= 1;
+    -- 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
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
new file mode 100644
index 0000000..0d82725
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/android_sysui_cuj_surfaceflinger.sql
@@ -0,0 +1,251 @@
+-- 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.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,
+    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 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
+  FROM slice
+  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
+  WHERE slice.name GLOB 'onMessageInvalidate *'
+  )
+SELECT
+    on_msg.vsync,
+    on_msg.ts,
+    on_msg.ts_end,
+    on_msg.dur,
+    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
+WHERE 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
+  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 = '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 commit_to_composite AS (
+    SELECT
+      commits.vsync,
+      commits.ts AS commit_ts,
+      commits.name AS name,
+      min(composite.ts) AS composite_ts
+      FROM android_sysui_cuj_surfaceflinger_commit_slices_in_cuj commits
+      JOIN android_sysui_cuj_surfaceflinger_composite_slices composite ON composite.ts > commits.ts_end
+      GROUP BY commits.vsync
+  )
+  SELECT
+    vsync,
+    match.commit_ts AS ts,
+    composite.ts_end AS ts_end,
+    composite.ts_end - match.commit_ts AS dur,
+    lag(composite.ts_end) OVER (ORDER BY composite.ts_end ASC) AS ts_prev_frame_end,
+    lead(match.commit_ts) OVER (ORDER BY match.commit_ts ASC) AS ts_next_frame_start
+  FROM commit_to_composite match
+  JOIN android_sysui_cuj_surfaceflinger_composite_slices composite ON match.composite_ts = composite.ts;
+
+-- 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 * FROM android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj
+UNION ALL
+SELECT * 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
+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);
+
+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;
+
+DROP VIEW IF EXISTS android_sysui_cuj_gcs_to_mt_match;
+CREATE VIEW android_sysui_cuj_gcs_to_mt_match AS
+-- Match Mainthread slice with the first GPU Completion that begins during it
+SELECT
+gcs.ts as gcs_ts,
+gcs.ts_end as gcs_ts_end,
+gcs.dur as gcs_dur,
+gcs.idx as idx,
+mtf.ts as mts_ts,
+mtf.vsync as vsync
+FROM android_sysui_cuj_surfaceflinger_gpu_completion_slices_in_cuj gcs
+-- join with all previous render frames but take latest start time
+JOIN android_sysui_cuj_surfaceflinger_main_thread_frames mtf ON gcs.ts > mtf.ts AND gcs.ts < mtf.ts + mtf.dur
+GROUP BY gcs.ts, gcs.ts_end, gcs.dur, gcs.idx;
+
+-- 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,
+  max(mtf.ts_prev_frame_end, frame.ts) AS ts,
+  min(mtf.ts_next_frame_start, frame.ts_end) AS ts_end,
+  min(mtf.ts_next_frame_start, frame.ts_end) - max(mtf.ts_prev_frame_end, frame.ts) AS dur,
+  gcs.gcs_ts,
+  gcs.gcs_ts_end,
+  gcs.gcs_dur
+FROM android_sysui_cuj_sf_actual_frame_timeline_slice frame
+JOIN android_sysui_cuj_surfaceflinger_main_thread_frames mtf ON frame.name = mtf.vsync
+JOIN android_sysui_cuj_gcs_to_mt_match gcs ON gcs.vsync = frame.name
+WHERE frame.jank_type != 'None';
diff --git a/src/trace_processor/metrics/sql/chrome/gesture_jank.sql b/src/trace_processor/metrics/sql/chrome/gesture_jank.sql
index cfbc96b..6e7ea1a 100644
--- a/src/trace_processor/metrics/sql/chrome/gesture_jank.sql
+++ b/src/trace_processor/metrics/sql/chrome/gesture_jank.sql
@@ -221,7 +221,7 @@
 -- rate more than 1 FPS (and therefore VSync interval less than a second), this
 -- ratio should increase with increments more than minimal value in numerator
 -- (1ns) divided by maximum value in denominator, giving 1e-9.
--- Note: Logic is inside the isJankyFrame function found in jank_utilities.sql.
+-- 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
@@ -237,6 +237,15 @@
 
 -- This just uses prev_jank and next_jank to see if each "update" event is a
 -- jank.
+--
+-- JankBudget is the time in ns that we need to reduce the current
+-- gesture (|id|) for this frame not to be considered janky (i.e., how much
+-- faster for IsJankyFrame() to have not returned true).
+--
+-- For JankBudget we use the frames_exact of current, previous and next to find
+-- the jank budget in exact frame count. We then multiply by avg_vsync_internal
+-- to get the jank budget time.
+-- Note: Logic is inside the JankBudget function found in jank_utilities.sql.
 DROP VIEW IF EXISTS {{prefix}}_jank;
 CREATE VIEW {{prefix}}_jank AS
   SELECT
@@ -244,6 +253,8 @@
     (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;
@@ -274,6 +285,9 @@
         SELECT CAST(SUM(dur)/1e6 AS REAL) FROM {{prefix}}_jank WHERE jank
       ),
       'num_{{prefix}}_update_count', COUNT(*),
-      'num_{{prefix}}_update_jank_count', SUM(jank)
+      '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;
diff --git a/src/trace_processor/metrics/sql/chrome/jank_utilities.sql b/src/trace_processor/metrics/sql/chrome/jank_utilities.sql
index 494b88c..44edb42 100644
--- a/src/trace_processor/metrics/sql/chrome/jank_utilities.sql
+++ b/src/trace_processor/metrics/sql/chrome/jank_utilities.sql
@@ -44,3 +44,40 @@
         $cur_frame_exact > $neighbour_frame_exact + 0.5 + 1e-9
     END'
 );
+
+SELECT CREATE_FUNCTION(
+  -- Function : function takes the cur_frame_exact, prev_frame_exact and
+  -- next_frame_exact and returns the value of the jank budget of the current
+  -- frame.
+  --
+  -- 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)',
+  -- Returns the jank budget in percentage (i.e. 0.75) of vsync interval
+  -- percentage.
+  --
+  -- We determine the difference between the frame count of the current frame
+  -- and its consecutive frames by subtracting with the frame_exact values. We
+  -- null check for cases when the neighbor frame count can be null for the
+  -- first and last frames.
+  --
+  -- Since a frame is considered janky, if the difference in the frame count
+  -- with its adjacent frame is greater than 0.5 (half a vsync) which means we
+  -- need to reduce the frame count by a value less than 0.5 of maximum
+  -- difference in frame count for it to be no longer janky. We subtract 1e-9 as
+  -- we want to output minimum amount required.
+  'FLOAT',
+  'SELECT
+    COALESCE(
+      -- Could be null if next or previous is null.
+      MAX(
+        ($cur_frame_exact - $prev_frame_exact),
+        ($cur_frame_exact - $next_frame_exact)
+      ),
+      -- If one of them is null output the first non-null.
+      ($cur_frame_exact - $prev_frame_exact),
+      ($cur_frame_exact - $next_frame_exact)
+      -- Otherwise return null
+    ) - 0.5 - 1e-9'
+);
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 595491d..b73516a 100644
--- a/src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql
+++ b/src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql
@@ -49,6 +49,8 @@
 -- All tracks generated from chrome_dropped_frames_event are
 -- placed under a track group named 'Dropped Frames', whose summary
 -- track is the first track ('All Processes') in chrome_dropped_frames_event.
+-- Note that the 'All Processes' track is generated only when dropped frames
+-- come from more than one origin process.
 DROP VIEW IF EXISTS chrome_dropped_frames_event;
 CREATE VIEW chrome_dropped_frames_event AS
 SELECT
@@ -59,6 +61,8 @@
   'Dropped Frame' AS slice_name,
   'Dropped Frames' AS group_name
 FROM dropped_frames_with_process_info
+WHERE (SELECT COUNT(DISTINCT process_id)
+       FROM dropped_frames_with_process_info) > 1
 GROUP BY ts
 UNION ALL
 SELECT
diff --git a/src/trace_processor/read_trace.cc b/src/trace_processor/read_trace.cc
index a8f1b52..c4cb67a 100644
--- a/src/trace_processor/read_trace.cc
+++ b/src/trace_processor/read_trace.cc
@@ -196,18 +196,13 @@
     // Make sure that to reset the stream between the gzip streams.
     auto bytes = packet.compressed_packets();
     decompressor.Reset();
-    decompressor.SetInput(bytes.data, bytes.size);
-
     using ResultCode = util::GzipDecompressor::ResultCode;
-    uint8_t out[4096];
-    for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
-      auto res = decompressor.Decompress(out, base::ArraySize(out));
-      ret = res.ret;
-      if (ret == ResultCode::kError || ret == ResultCode::kNoProgress ||
-          ret == ResultCode::kNeedsMoreInput) {
-        return util::ErrStatus("Failed while decompressing stream");
-      }
-      output->insert(output->end(), out, out + res.bytes_written);
+    ResultCode ret = decompressor.FeedAndExtract(
+        bytes.data, bytes.size, [&output](const uint8_t* buf, size_t buf_len) {
+          output->insert(output->end(), buf, buf + buf_len);
+        });
+    if (ret == ResultCode::kError || ret == ResultCode::kNeedsMoreInput) {
+      return util::ErrStatus("Failed while decompressing stream");
     }
   }
   return util::OkStatus();
diff --git a/src/trace_processor/read_trace_integrationtest.cc b/src/trace_processor/read_trace_integrationtest.cc
index 390a7ef..439f1fa 100644
--- a/src/trace_processor/read_trace_integrationtest.cc
+++ b/src/trace_processor/read_trace_integrationtest.cc
@@ -103,7 +103,7 @@
   std::vector<uint8_t> decompressed;
   util::Status status = trace_processor::DecompressTrace(
       raw_compressed_trace.data(), raw_compressed_trace.size(), &decompressed);
-  ASSERT_TRUE(status.ok());
+  ASSERT_TRUE(status.ok()) << status.message();
 
   protos::pbzero::Trace::Decoder decoder(decompressed.data(),
                                          decompressed.size());
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index 7863cc5..e5be608 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -51,6 +51,7 @@
       "../../../include/perfetto/trace_processor",
       "../../../protos/perfetto/trace/ftrace:zero",
       "../../base",
+      "../containers",
       "../db",
       "../importers/common",
       "../storage",
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index bc2ee69..b6897e1 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -17,6 +17,7 @@
 #include "src/trace_processor/sqlite/db_sqlite_table.h"
 
 #include "perfetto/ext/base/string_writer.h"
+#include "src/trace_processor/containers/bit_vector.h"
 #include "src/trace_processor/sqlite/query_cache.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 #include "src/trace_processor/tp_metatrace.h"
@@ -83,6 +84,17 @@
   return value;
 }
 
+BitVector ColsUsedBitVector(uint64_t sqlite_cols_used, size_t col_count) {
+  return BitVector::Range(
+      0, static_cast<uint32_t>(col_count), [sqlite_cols_used](uint32_t idx) {
+        // If the lowest bit of |sqlite_cols_used| is set, the first column is
+        // used. The second lowest bit corresponds to the second column etc. If
+        // the most significant bit of |sqlite_cols_used| is set, that means
+        // that any column after the first 63 columns could be used.
+        return sqlite_cols_used & (1ull << std::min(idx, 63u));
+      });
+}
+
 }  // namespace
 
 DbSqliteTable::DbSqliteTable(sqlite3*, Context context)
@@ -111,7 +123,8 @@
 
   // Figure out if the table needs explicit args (in the form of constraints
   // on hidden columns) passed to it in order to make the query valid.
-  util::Status status = generator->ValidateConstraints({});
+  util::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,
@@ -438,8 +451,10 @@
       });
       // If we have a dynamically created table, regenerate the table based on
       // the new constraints.
-      dynamic_table_ =
-          db_sqlite_table_->generator_->ComputeTable(constraints_, orders_);
+      BitVector cols_used_bv = ColsUsedBitVector(
+          qc.cols_used(), db_sqlite_table_->schema_.columns.size());
+      dynamic_table_ = db_sqlite_table_->generator_->ComputeTable(
+          constraints_, orders_, cols_used_bv);
       upstream_table_ = dynamic_table_.get();
       if (!upstream_table_)
         return SQLITE_CONSTRAINT;
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 64231de..1e713b2 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACE_PROCESSOR_SQLITE_DB_SQLITE_TABLE_H_
 #define SRC_TRACE_PROCESSOR_SQLITE_DB_SQLITE_TABLE_H_
 
+#include "src/trace_processor/containers/bit_vector.h"
 #include "src/trace_processor/db/table.h"
 #include "src/trace_processor/sqlite/query_cache.h"
 #include "src/trace_processor/sqlite/sqlite_table.h"
@@ -65,7 +66,8 @@
     // vectors.
     virtual std::unique_ptr<Table> ComputeTable(
         const std::vector<Constraint>& cs,
-        const std::vector<Order>& ob) = 0;
+        const std::vector<Order>& ob,
+        const BitVector& cols_used) = 0;
   };
 
   class Cursor : public SqliteTable::Cursor {
diff --git a/src/trace_processor/sqlite/query_constraints.cc b/src/trace_processor/sqlite/query_constraints.cc
index 942fe47..67f1812 100644
--- a/src/trace_processor/sqlite/query_constraints.cc
+++ b/src/trace_processor/sqlite/query_constraints.cc
@@ -26,10 +26,12 @@
 namespace perfetto {
 namespace trace_processor {
 
-QueryConstraints::QueryConstraints() = default;
+QueryConstraints::QueryConstraints(uint64_t cols_used)
+    : cols_used_(cols_used) {}
 QueryConstraints::~QueryConstraints() = default;
 QueryConstraints::QueryConstraints(QueryConstraints&&) noexcept = default;
-QueryConstraints& QueryConstraints::operator=(QueryConstraints&&) = default;
+QueryConstraints& QueryConstraints::operator=(QueryConstraints&&) noexcept =
+    default;
 
 int QueryConstraints::FreeSqliteString(char* resource) {
   sqlite3_free(resource);
@@ -38,7 +40,8 @@
 
 bool QueryConstraints::operator==(const QueryConstraints& other) const {
   if ((other.constraints().size() != constraints().size()) ||
-      (other.order_by().size() != order_by().size())) {
+      (other.order_by().size() != order_by().size()) ||
+      other.cols_used() != cols_used()) {
     return false;
   }
 
@@ -79,6 +82,8 @@
 QueryConstraints::SqliteString QueryConstraints::ToNewSqlite3String() const {
   std::string str_result;
   str_result.reserve(512);
+
+  // Add all the constraints.
   str_result.append("C");
   str_result.append(std::to_string(constraints_.size()));
   str_result.append(",");
@@ -88,6 +93,9 @@
     str_result.append(std::to_string(cs.op));
     str_result.append(",");
   }
+  str_result.back() = ';';
+
+  // Add all the clauses.
   str_result.append("O");
   str_result.append(std::to_string(order_by_.size()));
   str_result.append(",");
@@ -97,44 +105,67 @@
     str_result.append(std::to_string(ob.desc));
     str_result.append(",");
   }
+  str_result.back() = ';';
 
-  // The last char is a "," so overwriting with the null terminator on purpose.
-  SqliteString result(
-      static_cast<char*>(sqlite3_malloc(static_cast<int>(str_result.size()))));
-  base::StringCopy(result.get(), str_result.c_str(), str_result.size());
+  // Add the columns used.
+  str_result.append("U");
+  str_result.append(std::to_string(cols_used_));
+
+  SqliteString result(static_cast<char*>(
+      sqlite3_malloc(static_cast<int>(str_result.size() + 1))));
+  base::StringCopy(result.get(), str_result.c_str(), str_result.size() + 1);
   return result;
 }
 
 QueryConstraints QueryConstraints::FromString(const char* idxStr) {
   QueryConstraints qc;
 
-  base::StringSplitter splitter(std::string(idxStr), ',');
+  base::StringSplitter outer_splitter(std::string(idxStr), ';');
 
-  PERFETTO_CHECK(splitter.Next() && splitter.cur_token_size() > 1);
-  // The '+ 1' skips the letter 'C' in the first token.
-  long num_constraints = strtol(splitter.cur_token() + 1, nullptr, 10);
-  for (int i = 0; i < num_constraints; ++i) {
-    PERFETTO_CHECK(splitter.Next());
-    int col = static_cast<int>(strtol(splitter.cur_token(), nullptr, 10));
-    PERFETTO_CHECK(splitter.Next());
-    unsigned char op =
-        static_cast<unsigned char>(strtol(splitter.cur_token(), nullptr, 10));
-    qc.AddConstraint(col, op, 0);
+  // Handle the CONSTRAINT section of the string.
+  PERFETTO_CHECK(outer_splitter.Next() && outer_splitter.cur_token_size() > 1);
+  {
+    base::StringSplitter splitter(&outer_splitter, ',');
+    PERFETTO_CHECK(splitter.Next() && splitter.cur_token_size() > 1);
+
+    // The '[1]' skips the letter 'C' in the first token.
+    int64_t num_constraints = *base::CStringToInt64(&splitter.cur_token()[1]);
+    for (int i = 0; i < num_constraints; ++i) {
+      PERFETTO_CHECK(splitter.Next());
+      int col = static_cast<int>(*base::CStringToInt32(splitter.cur_token()));
+      PERFETTO_CHECK(splitter.Next());
+      unsigned char op = static_cast<unsigned char>(
+          *base::CStringToUInt32(splitter.cur_token()));
+      qc.AddConstraint(col, op, 0);
+    }
   }
 
-  PERFETTO_CHECK(splitter.Next() && splitter.cur_token_size() > 1);
-  // The '+ 1' skips the letter 'O' in the current token.
-  long num_order_by = strtol(splitter.cur_token() + 1, nullptr, 10);
-  for (int i = 0; i < num_order_by; ++i) {
-    PERFETTO_CHECK(splitter.Next());
-    int col = static_cast<int>(strtol(splitter.cur_token(), nullptr, 10));
-    PERFETTO_CHECK(splitter.Next());
-    unsigned char desc =
-        static_cast<unsigned char>(strtol(splitter.cur_token(), nullptr, 10));
-    qc.AddOrderBy(col, desc);
+  // Handle the ORDER BY section of the string.
+  PERFETTO_CHECK(outer_splitter.Next() && outer_splitter.cur_token_size() > 1);
+  {
+    base::StringSplitter splitter(&outer_splitter, ',');
+    PERFETTO_CHECK(splitter.Next() && splitter.cur_token_size() > 1);
+
+    // The '[1]' skips the letter 'O' in the current token.
+    int64_t num_order_by = *base::CStringToInt64(&splitter.cur_token()[1]);
+    for (int i = 0; i < num_order_by; ++i) {
+      PERFETTO_CHECK(splitter.Next());
+      int col = static_cast<int>(*base::CStringToInt32(splitter.cur_token()));
+      PERFETTO_CHECK(splitter.Next());
+      unsigned char desc = static_cast<unsigned char>(
+          *base::CStringToUInt32(splitter.cur_token()));
+      qc.AddOrderBy(col, desc);
+    }
   }
 
-  PERFETTO_DCHECK(!splitter.Next());
+  // Handle the COLS USED section of the string.
+  PERFETTO_CHECK(outer_splitter.Next() && outer_splitter.cur_token_size() > 1);
+  {
+    // The '[1]' skips the letter 'U' in the current token.
+    qc.cols_used_ = *base::CStringToUInt64(&outer_splitter.cur_token()[1]);
+  }
+
+  PERFETTO_DCHECK(!outer_splitter.Next());
   return qc;
 }
 
diff --git a/src/trace_processor/sqlite/query_constraints.h b/src/trace_processor/sqlite/query_constraints.h
index 54c1b45..10ee3e6 100644
--- a/src/trace_processor/sqlite/query_constraints.h
+++ b/src/trace_processor/sqlite/query_constraints.h
@@ -19,6 +19,7 @@
 
 #include <sqlite3.h>
 
+#include <limits>
 #include <vector>
 
 #include "perfetto/ext/base/scoped_file.h"
@@ -57,10 +58,11 @@
 
   using SqliteString = base::ScopedResource<char*, FreeSqliteString, nullptr>;
 
-  QueryConstraints();
+  explicit QueryConstraints(
+      uint64_t cols_used = std::numeric_limits<uint64_t>::max());
   ~QueryConstraints();
   QueryConstraints(QueryConstraints&&) noexcept;
-  QueryConstraints& operator=(QueryConstraints&&);
+  QueryConstraints& operator=(QueryConstraints&&) noexcept;
 
   // Two QueryConstraints with the same constraint and orderby vectors
   // are equal.
@@ -89,12 +91,20 @@
 
   std::vector<Constraint>* mutable_constraints() { return &constraints_; }
 
+  uint64_t cols_used() const { return cols_used_; }
+
  private:
   QueryConstraints(const QueryConstraints&) = delete;
   QueryConstraints& operator=(const QueryConstraints&) = delete;
 
   std::vector<OrderBy> order_by_;
   std::vector<Constraint> constraints_;
+
+  // Stores information about which column is used by this query.
+  // If the lowest bit of is set, the first column is used. The second lowest
+  // bit corresponds to the second column etc. If the most significant bit is
+  // set, that means that any column after the first 63 columns could be used.
+  uint64_t cols_used_ = std::numeric_limits<uint64_t>::max();
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/sqlite/query_constraints_unittest.cc b/src/trace_processor/sqlite/query_constraints_unittest.cc
index 402bc70..d4be555 100644
--- a/src/trace_processor/sqlite/query_constraints_unittest.cc
+++ b/src/trace_processor/sqlite/query_constraints_unittest.cc
@@ -35,11 +35,11 @@
 };
 
 TEST_F(QueryConstraintsTest, ConvertToAndFromSqlString) {
-  QueryConstraints qc;
+  QueryConstraints qc(0);
   qc.AddConstraint(12, 0, 0);
 
   QueryConstraints::SqliteString only_constraint = qc.ToNewSqlite3String();
-  ASSERT_TRUE(strcmp(only_constraint.get(), "C1,12,0,O0") == 0);
+  ASSERT_TRUE(strcmp(only_constraint.get(), "C1,12,0;O0;U0") == 0);
 
   QueryConstraints qc_constraint =
       QueryConstraints::FromString(only_constraint.get());
@@ -49,17 +49,17 @@
   qc.AddOrderBy(21, true);
 
   QueryConstraints::SqliteString result = qc.ToNewSqlite3String();
-  ASSERT_TRUE(strcmp(result.get(), "C1,12,0,O2,1,0,21,1") == 0);
+  ASSERT_TRUE(strcmp(result.get(), "C1,12,0;O2,1,0,21,1;U0") == 0);
 
   QueryConstraints qc_result = QueryConstraints::FromString(result.get());
   ASSERT_EQ(qc, qc_result);
 }
 
 TEST_F(QueryConstraintsTest, CheckEmptyConstraints) {
-  QueryConstraints qc;
+  QueryConstraints qc(0);
 
   QueryConstraints::SqliteString string_result = qc.ToNewSqlite3String();
-  ASSERT_TRUE(strcmp(string_result.get(), "C0,O0") == 0);
+  ASSERT_TRUE(strcmp(string_result.get(), "C0;O0;U0") == 0);
 
   QueryConstraints qc_result =
       QueryConstraints::FromString(string_result.get());
@@ -68,17 +68,35 @@
 }
 
 TEST_F(QueryConstraintsTest, OnlyOrderBy) {
-  QueryConstraints qc;
+  QueryConstraints qc(0);
   qc.AddOrderBy(3, true);
 
   QueryConstraints::SqliteString string_result = qc.ToNewSqlite3String();
-  ASSERT_TRUE(strcmp(string_result.get(), "C0,O1,3,1") == 0);
+  ASSERT_TRUE(strcmp(string_result.get(), "C0;O1,3,1;U0") == 0);
 
   QueryConstraints qc_result =
       QueryConstraints::FromString(string_result.get());
   ASSERT_EQ(qc, qc_result);
 }
 
+TEST_F(QueryConstraintsTest, ColsUsed) {
+  ASSERT_EQ(QueryConstraints(0), QueryConstraints::FromString("C0;O0;U0"));
+
+  ASSERT_EQ(QueryConstraints(4), QueryConstraints::FromString("C0;O0;U4"));
+
+  ASSERT_EQ(QueryConstraints(1ull << 63),
+            QueryConstraints::FromString("C0;O0;U9223372036854775808"));
+
+  ASSERT_EQ(QueryConstraints(9223372036854775807ull),
+            QueryConstraints::FromString("C0;O0;U9223372036854775807"));
+
+  ASSERT_EQ(QueryConstraints(),
+            QueryConstraints::FromString("C0;O0;U18446744073709551615"));
+
+  auto str = QueryConstraints(0xFFFFFFFFFFFFFFFF).ToNewSqlite3String();
+  ASSERT_STREQ(str.get(), "C0;O0;U18446744073709551615");
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_table.cc b/src/trace_processor/sqlite/sqlite_table.cc
index d155c98..6fad97b 100644
--- a/src/trace_processor/sqlite/sqlite_table.cc
+++ b/src/trace_processor/sqlite/sqlite_table.cc
@@ -61,7 +61,7 @@
 }
 
 int SqliteTable::BestIndexInternal(sqlite3_index_info* idx) {
-  QueryConstraints qc;
+  QueryConstraints qc(idx->colUsed);
 
   for (int i = 0; i < idx->nConstraint; i++) {
     const auto& cs = idx->aConstraint[i];
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index cfb7d43..0d42ae4 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -90,7 +90,7 @@
       kSame = 1,
     };
 
-    Cursor(SqliteTable* table);
+    explicit Cursor(SqliteTable* table);
     virtual ~Cursor();
 
     // Methods to be implemented by derived table classes.
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 9fb73f8b..4064c80 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -114,6 +114,11 @@
   F(traced_chunks_discarded,            kSingle,  kInfo,     kTrace,    ""),   \
   F(traced_data_sources_registered,     kSingle,  kInfo,     kTrace,    ""),   \
   F(traced_data_sources_seen,           kSingle,  kInfo,     kTrace,    ""),   \
+  F(traced_final_flush_failed,          kSingle,  kDataLoss, kTrace,    ""),   \
+  F(traced_final_flush_succeeded,       kSingle,  kInfo,     kTrace,    ""),   \
+  F(traced_flushes_failed,              kSingle,  kDataLoss, kTrace,    ""),   \
+  F(traced_flushes_requested,           kSingle,  kInfo,     kTrace,    ""),   \
+  F(traced_flushes_succeeded,           kSingle,  kInfo,     kTrace,    ""),   \
   F(traced_patches_discarded,           kSingle,  kInfo,     kTrace,    ""),   \
   F(traced_producers_connected,         kSingle,  kInfo,     kTrace,    ""),   \
   F(traced_producers_seen,              kSingle,  kInfo,     kTrace,    ""),   \
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 78ee878..82b2c7e 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -117,9 +117,13 @@
     "proto_to_args_parser_unittest.cc",
     "protozero_to_text_unittests.cc",
   ]
+  if (enable_perfetto_zlib) {
+    sources += [ "gzip_utils_unittest.cc" ]
+  }
   testonly = true
   deps = [
     ":descriptors",
+    ":gzip",
     ":proto_to_args_parser",
     ":protozero_to_text",
     "..:gen_cc_test_messages_descriptor",
diff --git a/src/trace_processor/util/gzip_utils.cc b/src/trace_processor/util/gzip_utils.cc
index b16e56b..2279b87 100644
--- a/src/trace_processor/util/gzip_utils.cc
+++ b/src/trace_processor/util/gzip_utils.cc
@@ -38,46 +38,33 @@
 #endif
 }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)  // Real Implementation
+
 GzipDecompressor::GzipDecompressor() : z_stream_(new z_stream()) {
   z_stream_->zalloc = nullptr;
   z_stream_->zfree = nullptr;
   z_stream_->opaque = nullptr;
-  inflateInit2(z_stream_.get(), 32 + 15);
+  inflateInit2(z_stream_.get(), 32 + MAX_WBITS);
 }
-#else
-GzipDecompressor::GzipDecompressor() = default;
-#endif
 
 GzipDecompressor::~GzipDecompressor() {
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
-  // Ensure the call to inflateEnd to prevent leaks of internal state.
   inflateEnd(z_stream_.get());
-#endif
 }
 
 void GzipDecompressor::Reset() {
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
   inflateReset(z_stream_.get());
-#endif
 }
 
-void GzipDecompressor::SetInput(const uint8_t* data, size_t size) {
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+void GzipDecompressor::Feed(const uint8_t* data, size_t size) {
   // This const_cast is not harmfull as zlib will not modify the data in this
   // pointer. This is only necessary because of the build flags we use to be
   // compatible with other embedders.
   z_stream_->next_in = const_cast<uint8_t*>(data);
   z_stream_->avail_in = static_cast<uInt>(size);
-#else
-  base::ignore_result(data);
-  base::ignore_result(size);
-#endif
 }
 
-GzipDecompressor::Result GzipDecompressor::Decompress(uint8_t* out,
-                                                      size_t out_size) {
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+GzipDecompressor::Result GzipDecompressor::ExtractOutput(uint8_t* out,
+                                                         size_t out_size) {
   if (z_stream_->avail_in == 0)
     return Result{ResultCode::kNeedsMoreInput, 0};
 
@@ -95,15 +82,34 @@
     case Z_STREAM_END:
       return Result{ResultCode::kEof, out_size - z_stream_->avail_out};
     case Z_BUF_ERROR:
-      return Result{ResultCode::kNoProgress, 0};
+      return Result{ResultCode::kNeedsMoreInput, 0};
     default:
       return Result{ResultCode::kOk, out_size - z_stream_->avail_out};
   }
-#else
-  base::ignore_result(out);
-  base::ignore_result(out_size);
+}
+
+#else  // Dummy Implementation
+
+GzipDecompressor::GzipDecompressor() = default;
+GzipDecompressor::~GzipDecompressor() = default;
+void GzipDecompressor::Reset() {}
+void GzipDecompressor::Feed(const uint8_t*, size_t) {}
+GzipDecompressor::Result GzipDecompressor::ExtractOutput(uint8_t*, size_t) {
   return Result{ResultCode::kError, 0};
-#endif
+}
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
+// static
+std::vector<uint8_t> GzipDecompressor::DecompressFully(const uint8_t* data,
+                                                       size_t len) {
+  std::vector<uint8_t> whole_data;
+  GzipDecompressor decompressor;
+  auto decom_output_consumer = [&](const uint8_t* buf, size_t buf_len) {
+    whole_data.insert(whole_data.end(), buf, buf + buf_len);
+  };
+  decompressor.FeedAndExtract(data, len, decom_output_consumer);
+  return whole_data;
 }
 
 }  // namespace util
diff --git a/src/trace_processor/util/gzip_utils.h b/src/trace_processor/util/gzip_utils.h
index 189cfbd..711541f 100644
--- a/src/trace_processor/util/gzip_utils.h
+++ b/src/trace_processor/util/gzip_utils.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_
 
 #include <memory>
+#include <vector>
 
 struct z_stream_s;
 
@@ -29,13 +30,32 @@
 // build flags.
 bool IsGzipSupported();
 
+// Usage: To decompress in a streaming way, there are two ways of using it:
+// 1. [Commonly used] - Feed the sequence of mem-blocks in 'FeedAndExtract' one
+//    by one. Output will be produced in given output_consumer, which is simply
+//    a callback. On each 'FeedAndExtract', output_consumer could get invoked
+//    any number of times, based on how much partial output is available.
+
+// 2. [Uncommon ; Discouraged] - Feed the sequence of mem-blocks one by one, by
+//    calling 'Feed'. For each time 'Feed' is called, client should call
+//    'ExtractOutput' again and again to extrat the partially available output,
+//    until there in no more output to extract. Also see 'ResultCode' enum.
 class GzipDecompressor {
  public:
   enum class ResultCode {
+    // 'kOk' means nothing bad happened so far, but continue doing what you
+    // were doing.
     kOk,
+    // While calling 'ExtractOutput' repeatedly, if we get 'kEof', it means
+    // we have extracted all the partially available data and we are also
+    // done, i.e. there is no need to feed more input.
     kEof,
+    // Some error. Possibly invalid compressed stream or corrupted data.
     kError,
-    kNoProgress,
+    // While calling 'ExtractOutput' repeatedly, if we get 'kNeedsMoreInput',
+    // it means we have extracted all the partially available data, but we are
+    // not done yet. We need to call the 'Feed' to feed the next input
+    // mem-block and go through the ExtractOutput loop again.
     kNeedsMoreInput,
   };
   struct Result {
@@ -43,22 +63,52 @@
     ResultCode ret;
 
     // The amount of bytes written to output.
-    // Only valid if |ResultCode::kOk|.
+    // Valid in all cases except |ResultCode::kError|.
     size_t bytes_written;
   };
 
   GzipDecompressor();
   ~GzipDecompressor();
+  GzipDecompressor(const GzipDecompressor&) = delete;
+  GzipDecompressor& operator=(const GzipDecompressor&) = delete;
 
-  // Sets the input pointer and size of the gzip stream to inflate.
-  void SetInput(const uint8_t* data, size_t size);
+  // Feed the next mem-block.
+  void Feed(const uint8_t* data, size_t size);
 
-  // Decompresses the input previously provided in |SetInput|.
-  Result Decompress(uint8_t* out, size_t out_size);
+  // Feed the next mem-block and extract output in the callback consumer.
+  // callback can get invoked multiple times if there are multiple
+  // mem-blocks to output.
+  template <typename Callback = void(const uint8_t* ptr, size_t size)>
+  ResultCode FeedAndExtract(const uint8_t* data,
+                            size_t size,
+                            const Callback& output_consumer) {
+    Feed(data, size);
+    uint8_t buffer[4096];
+    Result result;
+    do {
+      result = ExtractOutput(buffer, sizeof(buffer));
+      if (result.ret != ResultCode::kError && result.bytes_written > 0) {
+        output_consumer(buffer, result.bytes_written);
+      }
+    } while (result.ret == ResultCode::kOk);
+    return result.ret;
+  }
+
+  // Extract the newly available partial output. On each 'Feed', this method
+  // should be called repeatedly until there is no more data to output
+  // i.e. (either 'kEof' or 'kNeedsMoreInput').
+  Result ExtractOutput(uint8_t* out, size_t out_capacity);
 
   // Sets the state of the decompressor to reuse with other gzip streams.
+  // This is almost like constructing a new 'GzipDecompressor' object
+  // but without paying the cost of internal memory allocation.
   void Reset();
 
+  // Decompress the entire mem-block and return decompressed mem-block.
+  // This is used for decompressing small strings or small files
+  // which doesn't require streaming decompression.
+  static std::vector<uint8_t> DecompressFully(const uint8_t* data, size_t len);
+
  private:
   std::unique_ptr<z_stream_s> z_stream_;
 };
diff --git a/src/trace_processor/util/gzip_utils_unittest.cc b/src/trace_processor/util/gzip_utils_unittest.cc
new file mode 100644
index 0000000..a58332e
--- /dev/null
+++ b/src/trace_processor/util/gzip_utils_unittest.cc
@@ -0,0 +1,145 @@
+/*
+ * 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 "src/trace_processor/util/gzip_utils.h"
+
+#include <zlib.h>
+#include <fstream>
+#include <iostream>
+#include "perfetto/base/logging.h"
+
+using std::string;
+
+namespace perfetto {
+namespace trace_processor {
+namespace util {
+
+static std::string TrivialGzipCompress(const std::string& input) {
+  constexpr auto buffer_len = 10000;
+  std::unique_ptr<char[]> output_ptr(new char[buffer_len]);
+  char* output = output_ptr.get();
+  z_stream defstream;
+  defstream.zalloc = Z_NULL;
+  defstream.zfree = Z_NULL;
+  defstream.opaque = Z_NULL;
+  defstream.avail_in = uint32_t(input.size());
+  defstream.next_in =
+      const_cast<Bytef*>(reinterpret_cast<const Bytef*>(input.data()));
+  defstream.avail_out = buffer_len;
+  defstream.next_out = reinterpret_cast<Bytef*>(output);
+  deflateInit(&defstream, Z_BEST_COMPRESSION);  // GZip decompress
+  deflate(&defstream, Z_FINISH);
+  deflateEnd(&defstream);
+  PERFETTO_CHECK(defstream.avail_out > 0);
+  return std::string(output, buffer_len - defstream.avail_out);
+}
+
+// Trivially decompress using ZlibOnlineDecompress.
+// It's called 'trivial' because we are feeding the entire input in one shot.
+static std::string TrivialDecompress(const std::string& input) {
+  string output;
+  GzipDecompressor decompressor;
+  decompressor.FeedAndExtract(
+      reinterpret_cast<const uint8_t*>(input.data()), uint32_t(input.size()),
+      [&](const uint8_t* data, size_t len) {
+        output.append(reinterpret_cast<const char*>(data), len);
+      });
+  return output;
+}
+
+// Decompress a large GZip file using a in-memory buffer of 4KB, and write the
+// decompressed output in another file.
+static void DecompressGzipFileInFileOut(const std::string& input_file,
+                                        const std::string& output_file) {
+  std::ofstream output(output_file.c_str(), std::ios::out | std::ios::binary);
+  std::ifstream input(input_file.c_str(), std::ios::binary);
+  GzipDecompressor decompressor;
+  constexpr uint32_t buffer_sizeof = 4096;
+  char buffer[buffer_sizeof];
+  while (!input.eof()) {
+    input.read(buffer, buffer_sizeof);
+    decompressor.FeedAndExtract(
+        reinterpret_cast<const uint8_t*>(buffer), size_t(input.gcount()),
+        [&](const uint8_t* data, size_t len) {
+          output.write(reinterpret_cast<const char*>(data),
+                       std::streamsize(len));
+        });
+  }
+  EXPECT_FALSE(input.bad());
+}
+
+TEST(GzipDecompressor, Basic) {
+  string input = "Abc..Def..Ghi";
+  string compressed = TrivialGzipCompress(input);
+  EXPECT_EQ(21u, compressed.size());
+  string decompressed = TrivialDecompress(compressed);
+  EXPECT_EQ(input, decompressed);
+}
+
+TEST(GzipDecompressor, Streaming) {
+  string input = "Abc..Def..Ghi";
+  string compressed = TrivialGzipCompress(input);
+  string decompressed;
+  auto consumer = [&](const uint8_t* data, size_t len) {
+    decompressed.append(reinterpret_cast<const char*>(data), len);
+  };
+  GzipDecompressor decompressor;
+  auto compressed_u8 = reinterpret_cast<const uint8_t*>(compressed.data());
+  ASSERT_GT(compressed.size(), 17u);
+  decompressor.FeedAndExtract(compressed_u8, 7, consumer);
+  decompressor.FeedAndExtract(compressed_u8 + 7, 10, consumer);
+  decompressor.FeedAndExtract(compressed_u8 + 17, compressed.size() - 17,
+                              consumer);
+
+  EXPECT_EQ(input, decompressed);
+}
+
+static std::string ReadFile(const std::string& file_name) {
+  std::ifstream fd(file_name, std::ios::binary);
+  std::stringstream buffer;
+  buffer << fd.rdbuf();
+  fd.close();
+  return buffer.str();
+}
+
+static void WriteFile(const std::string& file_name,
+                      const std::string& content) {
+  std::ofstream fd(file_name, std::ios::out | std::ios::binary);
+  fd.write(content.data(), std::streamsize(content.size()));
+  fd.close();
+}
+
+TEST(GzipDecompressor, DISABLED_FileInFileOut) {
+  auto big_string = []() {
+    std::string output;
+    for (int i = 0; i < 1000; i++) {
+      output += "Abc..Def..Ghi.";  // len = 14
+    }
+    return output;
+  }();
+  constexpr auto gz_file = "/tmp/abc.gz";
+  constexpr auto txt_file = "/tmp/abc.txt";
+  EXPECT_EQ(size_t(1000 * 14), big_string.size());
+  WriteFile(gz_file, TrivialGzipCompress(big_string));
+  DecompressGzipFileInFileOut(gz_file, txt_file);
+  EXPECT_TRUE(ReadFile(txt_file) == big_string);
+}
+
+}  // namespace util
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/atrace_wrapper.cc b/src/traced/probes/ftrace/atrace_wrapper.cc
index aa28916..22523f1 100644
--- a/src/traced/probes/ftrace/atrace_wrapper.cc
+++ b/src/traced/probes/ftrace/atrace_wrapper.cc
@@ -164,7 +164,6 @@
 
   bool ok = WIFEXITED(status) && WEXITSTATUS(status) == 0;
   if (!ok) {
-    // TODO(lalitm): use the stderr result from atrace.
     PERFETTO_ELOG("%s", error.c_str());
   }
   return ok;
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index 0b3d5b3..baf5468 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -1110,6 +1110,31 @@
        kUnsetFtraceId,
        352,
        kUnsetSize},
+      {"cros_ec_sensorhub_data",
+       "cros_ec",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "current_time", 1, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "current_timestamp", 2, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "delta", 3, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ec_fifo_timestamp", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ec_sensor_num", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "fifo_timestamp", 6, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       364,
+       kUnsetSize},
       {"dma_heap_stat",
        "dmabuf_heap",
        {
@@ -5745,6 +5770,80 @@
        kUnsetFtraceId,
        361,
        kUnsetSize},
+      {"napi_gro_receive_entry",
+       "net",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_len", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "gso_size", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "gso_type", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "hash", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ip_summed", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "l4_hash", 6, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "len", 7, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "mac_header", 8, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "mac_header_valid", 9, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "name", 10, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "napi_id", 11, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nr_frags", 12, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "protocol", 13, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "queue_mapping", 14, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "skbaddr", 15, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "truesize", 16, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "vlan_proto", 17, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "vlan_tagged", 18, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "vlan_tci", 19, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       365,
+       kUnsetSize},
+      {"napi_gro_receive_exit",
+       "net",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ret", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       366,
+       kUnsetSize},
       {"oom_score_adj_update",
        "oom",
        {
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/cros_ec/cros_ec_sensorhub_data/format b/src/traced/probes/ftrace/test/data/synthetic/events/cros_ec/cros_ec_sensorhub_data/format
new file mode 100644
index 0000000..d578512
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/cros_ec/cros_ec_sensorhub_data/format
@@ -0,0 +1,17 @@
+name: cros_ec_sensorhub_data
+ID: 1195
+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 ec_sensor_num;        offset:8;       size:4; signed:0;
+        field:u32 ec_fifo_timestamp;    offset:12;      size:4; signed:0;
+        field:s64 fifo_timestamp;       offset:16;      size:8; signed:1;
+        field:s64 current_timestamp;    offset:24;      size:8; signed:1;
+        field:s64 current_time; offset:32;      size:8; signed:1;
+        field:s64 delta;        offset:40;      size:8; signed:1;
+
+print fmt: "ec_num: %4u, ec_fifo_ts: %9u, fifo_ts: %12lld, curr_ts: %12lld, curr_time: %12lld, delta %12lld", REC->ec_sensor_num, REC->ec_fifo_timestamp, REC->fifo_timestamp, REC->current_timestamp, REC->current_time, REC->delta
+
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/net/napi_gro_receive_entry/format b/src/traced/probes/ftrace/test/data/synthetic/events/net/napi_gro_receive_entry/format
new file mode 100644
index 0000000..1d8d557
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/net/napi_gro_receive_entry/format
@@ -0,0 +1,29 @@
+name: napi_gro_receive_entry
+ID: 928
+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 napi_id;	offset:12;	size:4;	signed:0;
+	field:u16 queue_mapping;	offset:16;	size:2;	signed:0;
+	field:const void * skbaddr;	offset:24;	size:8;	signed:0;
+	field:bool vlan_tagged;	offset:32;	size:1;	signed:0;
+	field:u16 vlan_proto;	offset:34;	size:2;	signed:0;
+	field:u16 vlan_tci;	offset:36;	size:2;	signed:0;
+	field:u16 protocol;	offset:38;	size:2;	signed:0;
+	field:u8 ip_summed;	offset:40;	size:1;	signed:0;
+	field:u32 hash;	offset:44;	size:4;	signed:0;
+	field:bool l4_hash;	offset:48;	size:1;	signed:0;
+	field:unsigned int len;	offset:52;	size:4;	signed:0;
+	field:unsigned int data_len;	offset:56;	size:4;	signed:0;
+	field:unsigned int truesize;	offset:60;	size:4;	signed:0;
+	field:bool mac_header_valid;	offset:64;	size:1;	signed:0;
+	field:int mac_header;	offset:68;	size:4;	signed:1;
+	field:unsigned char nr_frags;	offset:72;	size:1;	signed:0;
+	field:u16 gso_size;	offset:74;	size:2;	signed:0;
+	field:u16 gso_type;	offset:76;	size:2;	signed:0;
+
+print fmt: "dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x", __get_str(name), REC->napi_id, REC->queue_mapping, REC->skbaddr, REC->vlan_tagged, REC->vlan_proto, REC->vlan_tci, REC->protocol, REC->ip_summed, REC->hash, REC->l4_hash, REC->len, REC->data_len, REC->truesize, REC->mac_header_valid, REC->mac_header, REC->nr_frags, REC->gso_size, REC->gso_type
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/net/napi_gro_receive_exit/format b/src/traced/probes/ftrace/test/data/synthetic/events/net/napi_gro_receive_exit/format
new file mode 100644
index 0000000..2eb347d
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/net/napi_gro_receive_exit/format
@@ -0,0 +1,11 @@
+name: napi_gro_receive_exit
+ID: 929
+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 ret;	offset:8;	size:4;	signed:1;
+
+print fmt: "ret=%d", REC->ret
diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc
index 22e0cdb..76e8ccc 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -227,10 +227,16 @@
   if (proc_status.empty())
     return;
   int tgid = ToInt(ReadProcStatusEntry(proc_status, "Tgid:"));
-  if (tgid <= 0)
+  int tid = ToInt(ReadProcStatusEntry(proc_status, "Pid:"));
+  if (tgid <= 0 || tid <= 0)
     return;
-  if (!seen_pids_.count(tgid))
-    WriteProcess(tgid, proc_status);
+
+  if (!seen_pids_.count(tgid)) {
+    // We need to read the status file if |pid| is non-main thread.
+    const std::string& proc_status_tgid =
+        (tgid == tid ? proc_status : ReadProcPidFile(tgid, "status"));
+    WriteProcess(tgid, proc_status_tgid);
+  }
   if (pid != tgid) {
     PERFETTO_DCHECK(!seen_pids_.count(pid));
     std::string thread_name;
@@ -273,6 +279,8 @@
 void ProcessStatsDataSource::WriteProcess(int32_t pid,
                                           const std::string& proc_status) {
   PERFETTO_DCHECK(ToInt(ReadProcStatusEntry(proc_status, "Tgid:")) == pid);
+  // Assert that |proc_status| is not for a non-main thread.
+  PERFETTO_DCHECK(ToInt(ReadProcStatusEntry(proc_status, "Pid:")) == pid);
   auto* proc = GetOrCreatePsTree()->add_processes();
   proc->set_pid(pid);
   proc->set_ppid(ToInt(ReadProcStatusEntry(proc_status, "PPid:")));
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 62ec0dc..0fa7d54 100644
--- a/src/traced/probes/ps/process_stats_data_source_unittest.cc
+++ b/src/traced/probes/ps/process_stats_data_source_unittest.cc
@@ -665,7 +665,11 @@
       .WillOnce(Return(
           "Name: foo\nTgid:\t42\nPid:   43\nPPid:  17\nNSpid:\t43\t3\n"));
 
-  data_source->OnPids({42, 43});
+  // It's possible that OnPids() is called with a non-main thread is seen before
+  // the main thread for a process. When this happens, the data source
+  // will WriteProcess(42) first and then WriteThread(43).
+  data_source->OnPids({43});
+  data_source->OnPids({42});  // This will be a no-op.
 
   auto trace = writer_raw_->GetAllTracePackets();
   ASSERT_EQ(trace.size(), 1u);
diff --git a/src/traced/service/builtin_producer.cc b/src/traced/service/builtin_producer.cc
index 20d9849..a2007be 100644
--- a/src/traced/service/builtin_producer.cc
+++ b/src/traced/service/builtin_producer.cc
@@ -67,8 +67,16 @@
 }
 
 void BuiltinProducer::ConnectInProcess(TracingService* svc) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // TODO(primiano): ConnectProducer should take a base::PlatformProcessId not
+  // pid_t, as they are different on Windows. But that is a larger refactoring
+  // and not worth given this is the only use case where it clashes.
+  const pid_t cur_proc_id = 0;
+#else
+  const pid_t cur_proc_id = base::GetProcessId();
+#endif
   endpoint_ = svc->ConnectProducer(
-      this, base::GetCurrentUserId(), base::GetProcessId(), "traced",
+      this, base::GetCurrentUserId(), cur_proc_id, "traced",
       /*shared_memory_size_hint_bytes=*/16 * 1024, /*in_process=*/true,
       TracingService::ProducerSMBScrapingMode::kDisabled,
       /*shared_memory_page_size_hint_bytes=*/4096);
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 696732e..18f6e69 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -1656,6 +1656,7 @@
     return;
   }
 
+  ++tracing_session->flushes_requested;
   FlushRequestID flush_request_id = ++last_flush_request_id_;
   PendingFlush& pending_flush =
       tracing_session->pending_flushes
@@ -1734,7 +1735,6 @@
 
   // If there were no producers to flush, consider it a success.
   bool success = it->second.producers.empty();
-
   auto callback = std::move(it->second.callback);
   tracing_session->pending_flushes.erase(it);
   CompleteFlush(tsid, std::move(callback), success);
@@ -1758,6 +1758,9 @@
       tracing_session,
       protos::pbzero::TracingServiceEvent::kAllDataSourcesFlushedFieldNumber,
       true /* snapshot_clocks */);
+
+  tracing_session->flushes_succeeded += success ? 1 : 0;
+  tracing_session->flushes_failed += success ? 0 : 1;
   callback(success);
 }
 
@@ -1889,6 +1892,8 @@
     if (!weak_this)
       return;
     TracingSession* session = weak_this->GetTracingSession(tsid);
+    session->final_flush_outcome = success ? TraceStats::FINAL_FLUSH_SUCCEEDED
+                                           : TraceStats::FINAL_FLUSH_FAILED;
     if (session->consumer_maybe_null) {
       // If the consumer is still attached, just disable the session but give it
       // a chance to read the contents.
@@ -2068,12 +2073,6 @@
       IsWaitingForTrigger(tracing_session))
     return false;
 
-  // Speculative fix for the memory watchdog crash in b/195145848. This function
-  // uses the heap extensively and might need a M_PURGE. window.gc() is back.
-  // TODO(primiano): if this fixes the crash we might want to coalesce the purge
-  // and throttle it.
-  auto on_ret = base::OnScopeExit([] { base::MaybeReleaseAllocatorMemToOS(); });
-
   // ReadBuffers() can allocate memory internally, for filtering. By limiting
   // the data that ReadBuffers() reads to kWriteIntoChunksSize per iteration,
   // we limit the amount of memory used on each iteration.
@@ -2271,6 +2270,13 @@
 
   MaybeFilterPackets(tracing_session, &packets);
 
+  if (!*has_more) {
+    // We've observed some extremely high memory usage by scudo after
+    // MaybeFilterPackets in the past. The original bug (b/195145848) is fixed
+    // now, but this code asks scudo to release memory just in case.
+    base::MaybeReleaseAllocatorMemToOS();
+  }
+
   return packets;
 }
 
@@ -3187,6 +3193,10 @@
   trace_stats.set_chunks_discarded(chunks_discarded_);
   trace_stats.set_patches_discarded(patches_discarded_);
   trace_stats.set_invalid_packets(tracing_session->invalid_packets);
+  trace_stats.set_flushes_requested(tracing_session->flushes_requested);
+  trace_stats.set_flushes_succeeded(tracing_session->flushes_succeeded);
+  trace_stats.set_flushes_failed(tracing_session->flushes_failed);
+  trace_stats.set_final_flush_outcome(tracing_session->final_flush_outcome);
 
   if (tracing_session->trace_filter) {
     auto* filt_stats = trace_stats.mutable_filter_stats();
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 99ee6ea..d0df0a6 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -57,6 +57,12 @@
 class TaskRunner;
 }  // namespace base
 
+namespace protos {
+namespace gen {
+enum TraceStats_FinalFlushOutcome : int;
+}
+}  // namespace protos
+
 class Consumer;
 class Producer;
 class SharedMemory;
@@ -558,6 +564,14 @@
     // Packets that failed validation of the TrustedPacket.
     uint64_t invalid_packets = 0;
 
+    // Flush() stats. See comments in trace_stats.proto for more.
+    uint64_t flushes_requested = 0;
+    uint64_t flushes_succeeded = 0;
+    uint64_t flushes_failed = 0;
+
+    // Outcome of the final Flush() done by FlushAndDisableTracing().
+    protos::gen::TraceStats_FinalFlushOutcome final_flush_outcome{};
+
     // Set to true on the first call to MaybeNotifyAllDataSourcesStarted().
     bool did_notify_all_data_source_started = false;
 
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 0c27e4a..3717fb2 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -26,14 +26,11 @@
 #include "perfetto/tracing/track_event_interned_data_index.h"
 #include "protos/perfetto/common/data_source_descriptor.gen.h"
 #include "protos/perfetto/common/track_event_descriptor.pbzero.h"
-#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
 
-using perfetto::protos::pbzero::ClockSnapshot;
-
 namespace perfetto {
 
 TrackEventSessionObserver::~TrackEventSessionObserver() = default;
@@ -51,13 +48,6 @@
 static constexpr const char kLegacySlowPrefix[] = "disabled-by-default-";
 static constexpr const char kSlowTag[] = "slow";
 static constexpr const char kDebugTag[] = "debug";
-// Allows to specify a custom unit different than the default (ns) for
-// the incremental clock.
-// A multiplier of 1000 means that a timestamp = 3 should be
-// interpreted as 3000 ns = 3 us.
-// TODO(mohitms): Move it to TrackEventConfig.
-constexpr uint64_t kIncrementalTimestampUnitMultiplier = 1;
-static_assert(kIncrementalTimestampUnitMultiplier >= 1, "");
 
 void ForEachObserver(
     std::function<bool(TrackEventSessionObserver*&)> callback) {
@@ -313,94 +303,50 @@
 }
 
 // static
-TraceTimestamp TrackEventInternal::GetTraceTime() {
-  return {TrackEventIncrementalState::kClockIdIncremental, GetTimeNs()};
-}
-
-// static
 int TrackEventInternal::GetSessionCount() {
   return session_count_.load();
 }
 
 // static
-void TrackEventInternal::ResetIncrementalState(
-    TraceWriterBase* trace_writer,
-    TrackEventIncrementalState* incr_state,
-    const TraceTimestamp& timestamp) {
-  auto sequence_timestamp = timestamp;
-  if (timestamp.clock_id != TrackEventInternal::GetClockId() &&
-      timestamp.clock_id != TrackEventIncrementalState::kClockIdIncremental) {
-    sequence_timestamp = TrackEventInternal::GetTraceTime();
-  }
-
-  incr_state->last_timestamp_ns = sequence_timestamp.value;
+void TrackEventInternal::ResetIncrementalState(TraceWriterBase* trace_writer,
+                                               TraceTimestamp timestamp) {
   auto default_track = ThreadTrack::Current();
   {
     // Mark any incremental state before this point invalid. Also set up
     // defaults so that we don't need to repeat constant data for each packet.
     auto packet = NewTracePacket(
-        trace_writer, incr_state, timestamp,
+        trace_writer, timestamp,
         protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
     auto defaults = packet->set_trace_packet_defaults();
-    defaults->set_timestamp_clock_id(
-        TrackEventIncrementalState::kClockIdIncremental);
+    defaults->set_timestamp_clock_id(GetClockId());
 
     // Establish the default track for this event sequence.
     auto track_defaults = defaults->set_track_event_defaults();
     track_defaults->set_track_uuid(default_track.uuid);
-
-    ClockSnapshot* clocks = packet->set_clock_snapshot();
-    // Trace clock.
-    ClockSnapshot::Clock* trace_clock = clocks->add_clocks();
-    trace_clock->set_clock_id(GetClockId());
-    trace_clock->set_timestamp(sequence_timestamp.value);
-    // Delta-encoded incremental clock in nano seconds.
-    // TODO(b/168311581): Make the unit of this clock configurable to allow
-    // trade-off between precision and encoded trace size.
-    ClockSnapshot::Clock* clock_incremental = clocks->add_clocks();
-    clock_incremental->set_clock_id(
-        TrackEventIncrementalState::kClockIdIncremental);
-    auto ts_unit_multiplier = kIncrementalTimestampUnitMultiplier;
-    clock_incremental->set_timestamp(sequence_timestamp.value /
-                                     ts_unit_multiplier);
-    clock_incremental->set_is_incremental(true);
-    clock_incremental->set_unit_multiplier_ns(ts_unit_multiplier);
   }
 
   // Every thread should write a descriptor for its default track, because most
   // trace points won't explicitly reference it. We also write the process
   // descriptor from every thread that writes trace events to ensure it gets
   // emitted at least once.
-  WriteTrackDescriptor(default_track, trace_writer, incr_state,
-                       sequence_timestamp);
-
-  WriteTrackDescriptor(ProcessTrack::Current(), trace_writer, incr_state,
-                       sequence_timestamp);
+  WriteTrackDescriptor(default_track, trace_writer);
+  WriteTrackDescriptor(ProcessTrack::Current(), trace_writer);
 }
 
 // static
 protozero::MessageHandle<protos::pbzero::TracePacket>
 TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
-                                   TrackEventIncrementalState* incr_state,
-                                   const TraceTimestamp& timestamp,
+                                   TraceTimestamp timestamp,
                                    uint32_t seq_flags) {
   auto packet = trace_writer->NewTracePacket();
-  if (timestamp.clock_id == TrackEventIncrementalState::kClockIdIncremental) {
-    if (incr_state->last_timestamp_ns <= timestamp.value) {
-      // No need to set the clock id here, since kClockIdIncremental is the
-      // clock id assumed by default.
-      auto ts_unit_multiplier = kIncrementalTimestampUnitMultiplier;
-      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;
-    } else {
-      // TODO(mohitms): Consider using kIncrementalTimestampUnitMultiplier.
-      packet->set_timestamp(timestamp.value);
-      packet->set_timestamp_clock_id(GetClockId());
-    }
-  } else {
-    packet->set_timestamp(timestamp.value);
-    packet->set_timestamp_clock_id(timestamp.clock_id);
+  packet->set_timestamp(timestamp.nanoseconds);
+  if (timestamp.clock_id != GetClockId()) {
+    packet->set_timestamp_clock_id(static_cast<uint32_t>(timestamp.clock_id));
+  } else if (GetClockId() != protos::pbzero::BUILTIN_CLOCK_BOOTTIME) {
+    // TODO(skyostil): Stop emitting the clock id for the default trace clock
+    // for every event once the trace processor understands trace packet
+    // defaults.
+    packet->set_timestamp_clock_id(GetClockId());
   }
   packet->set_sequence_flags(seq_flags);
   return packet;
@@ -413,10 +359,11 @@
     const Category* category,
     const char* name,
     perfetto::protos::pbzero::TrackEvent::Type type,
-    const TraceTimestamp& timestamp) {
+    TraceTimestamp timestamp) {
   PERFETTO_DCHECK(g_main_thread);
   PERFETTO_DCHECK(!incr_state->was_cleared);
-  auto packet = NewTracePacket(trace_writer, incr_state, timestamp);
+
+  auto packet = NewTracePacket(trace_writer, timestamp);
   EventContext ctx(std::move(packet), incr_state);
 
   auto track_event = ctx.event();
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 8aa589c..26d8a03 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -59,7 +59,6 @@
 #include "protos/perfetto/common/track_event_descriptor.pbzero.h"
 #include "protos/perfetto/config/interceptor_config.gen.h"
 #include "protos/perfetto/config/track_event/track_event_config.gen.h"
-#include "protos/perfetto/trace/clock_snapshot.gen.h"
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/gpu/gpu_render_stage_event.gen.h"
 #include "protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.h"
@@ -73,7 +72,6 @@
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.gen.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
-#include "protos/perfetto/trace/trace_packet_defaults.gen.h"
 #include "protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
 #include "protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
@@ -970,11 +968,6 @@
   bool process_descriptor_found = false;
   uint32_t sequence_id = 0;
   int32_t cur_pid = perfetto::test::GetCurrentProcessId();
-  uint64_t recent_absolute_time_ns = 0;
-  bool found_incremental_clock = false;
-  constexpr auto kClockIdIncremental =
-      perfetto::internal::TrackEventIncrementalState::kClockIdIncremental;
-
   for (const auto& packet : trace.packet()) {
     if (packet.has_track_descriptor()) {
       const auto& desc = packet.track_descriptor();
@@ -991,17 +984,6 @@
       incremental_state_was_cleared = true;
       categories.clear();
       event_names.clear();
-      EXPECT_EQ(kClockIdIncremental,
-                packet.trace_packet_defaults().timestamp_clock_id());
-    }
-    if (packet.has_clock_snapshot()) {
-      for (auto& clock : packet.clock_snapshot().clocks()) {
-        if (clock.is_incremental()) {
-          found_incremental_clock = true;
-          recent_absolute_time_ns = clock.timestamp();
-          EXPECT_EQ(kClockIdIncremental, clock.clock_id());
-        }
-      }
     }
 
     if (!packet.has_track_event())
@@ -1031,13 +1013,18 @@
         event_names[it.iid()] = it.name();
       }
     }
-    EXPECT_TRUE(found_incremental_clock);
-    uint64_t absolute_timestamp = packet.timestamp() + recent_absolute_time_ns;
-    recent_absolute_time_ns = absolute_timestamp;
-    EXPECT_GT(absolute_timestamp, 0u);
-    EXPECT_LE(absolute_timestamp, now);
-    // Packet uses default (incremental) clock.
+
+    EXPECT_GT(packet.timestamp(), 0u);
+    EXPECT_LE(packet.timestamp(), now);
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
     EXPECT_FALSE(packet.has_timestamp_clock_id());
+#else
+    constexpr auto kClockMonotonic =
+        perfetto::protos::pbzero::ClockSnapshot::Clock::MONOTONIC;
+    EXPECT_EQ(packet.timestamp_clock_id(),
+              static_cast<uint32_t>(kClockMonotonic));
+#endif
     if (track_event.type() ==
         perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN) {
       EXPECT_FALSE(begin_found);
@@ -1064,84 +1051,6 @@
   TestCategoryAsTemplateParameter<kTestCategory>();
 }
 
-TEST_P(PerfettoApiTest, TrackEventWithIncrementalTimestamp) {
-  // Create a new trace session.
-  auto* tracing_session = NewTraceWithCategories({"bar"});
-  constexpr auto kClockIdIncremental =
-      perfetto::internal::TrackEventIncrementalState::kClockIdIncremental;
-  tracing_session->get()->StartBlocking();
-
-  std::map<uint64_t, std::string> event_names;
-
-  auto empty_lambda = [](perfetto::EventContext) {};
-
-  constexpr uint64_t kInstantEvent1Time = 92718891479583;
-  TRACE_EVENT_INSTANT(
-      "bar", "InstantEvent1",
-      perfetto::TraceTimestamp{kClockIdIncremental, kInstantEvent1Time},
-      empty_lambda);
-
-  constexpr uint64_t kInstantEvent2Time = 92718891618959;
-  TRACE_EVENT_INSTANT(
-      "bar", "InstantEvent2",
-      perfetto::TraceTimestamp{kClockIdIncremental, kInstantEvent2Time},
-      empty_lambda);
-
-  perfetto::TrackEvent::Flush();
-  tracing_session->get()->StopBlocking();
-
-  std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
-  perfetto::protos::gen::Trace trace;
-  ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
-
-  uint64_t absolute_timestamp = 0;
-  int event_count = 0;
-  // Go through the packets and add the timestamps of those packets that use the
-  // incremental clock - in order to get the absolute timestamps of the track
-  // events.
-  for (const auto& packet : trace.packet()) {
-    if (packet.has_clock_snapshot()) {
-      for (auto& clock : packet.clock_snapshot().clocks()) {
-        if (clock.is_incremental()) {
-          absolute_timestamp = clock.timestamp();
-          EXPECT_EQ(clock.clock_id(), kClockIdIncremental);
-        }
-      }
-    } else if (!packet.has_timestamp_clock_id()) {
-      // Packets that don't have a timestamp_clock_id default to the incremental
-      // clock.
-      absolute_timestamp += packet.timestamp();
-    }
-
-    if (packet.sequence_flags() &
-        perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) {
-      event_names.clear();
-    }
-
-    if (!packet.has_track_event())
-      continue;
-
-    // Update incremental state.
-    if (packet.has_interned_data()) {
-      const auto& interned_data = packet.interned_data();
-      for (const auto& it : interned_data.event_names()) {
-        EXPECT_EQ(event_names.find(it.iid()), event_names.end());
-        event_names[it.iid()] = it.name();
-      }
-    }
-
-    if (event_names[packet.track_event().name_iid()] == "InstantEvent1") {
-      event_count++;
-      ASSERT_EQ(absolute_timestamp, kInstantEvent1Time);
-    } else if (event_names[packet.track_event().name_iid()] ==
-               "InstantEvent2") {
-      event_count++;
-      ASSERT_EQ(absolute_timestamp, kInstantEvent2Time);
-    }
-  }
-  ASSERT_EQ(event_count, 2);
-}
-
 TEST_P(PerfettoApiTest, TrackEventCategories) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"bar"});
@@ -1845,9 +1754,6 @@
   for (const auto& packet : trace.packet()) {
     if (!packet.has_track_event())
       continue;
-
-    EXPECT_EQ(packet.timestamp_clock_id(),
-              static_cast<uint32_t>(perfetto::TrackEvent::GetTraceClockId()));
     event_count++;
     switch (packet.track_event().type()) {
       case perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN:
diff --git a/src/tracing/track_event_state_tracker.cc b/src/tracing/track_event_state_tracker.cc
index 7ff50ac..7cb1b09 100644
--- a/src/tracing/track_event_state_tracker.cc
+++ b/src/tracing/track_event_state_tracker.cc
@@ -17,10 +17,8 @@
 #include "perfetto/tracing/track_event_state_tracker.h"
 
 #include "perfetto/ext/base/hash.h"
-#include "perfetto/tracing/internal/track_event_internal.h"
 
 #include "protos/perfetto/common/interceptor_descriptor.gen.h"
-#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
@@ -32,8 +30,6 @@
 
 namespace perfetto {
 
-using internal::TrackEventIncrementalState;
-
 TrackEventStateTracker::~TrackEventStateTracker() = default;
 TrackEventStateTracker::Delegate::~Delegate() = default;
 
@@ -49,15 +45,8 @@
   perfetto::protos::pbzero::TrackEvent::Decoder track_event(
       packet.track_event());
 
-  auto clock_id = packet.timestamp_clock_id();
-  if (!packet.has_timestamp_clock_id())
-    clock_id = sequence_state.default_clock_id;
+  // TODO(skyostil): Support incremental timestamps.
   uint64_t timestamp = packet.timestamp();
-  // TODO(mohitms): Incorporate unit multiplier as well.
-  if (clock_id == TrackEventIncrementalState::kClockIdIncremental) {
-    timestamp += sequence_state.most_recent_absolute_time_ns;
-    sequence_state.most_recent_absolute_time_ns = timestamp;
-  }
 
   Track* track = &sequence_state.track;
   if (track_event.has_track_uuid()) {
@@ -174,19 +163,6 @@
   }
 #endif
 
-  perfetto::protos::pbzero::ClockSnapshot::Decoder snapshot(
-      packet.clock_snapshot());
-  for (auto it = snapshot.clocks(); it; ++it) {
-    perfetto::protos::pbzero::ClockSnapshot::Clock::Decoder clock(*it);
-    // TODO(mohitms) : Handle the incremental clock other than default one.
-    if (clock.is_incremental() &&
-        clock.clock_id() == TrackEventIncrementalState::kClockIdIncremental) {
-      sequence_state.most_recent_absolute_time_ns =
-          clock.timestamp() * clock.unit_multiplier_ns();
-      break;
-    }
-  }
-
   if (packet.sequence_flags() &
       perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) {
     // Convert any existing event names and categories on the stack to
@@ -232,8 +208,6 @@
       perfetto::protos::pbzero::TrackEventDefaults::Decoder
           track_event_defaults(defaults.track_event_defaults());
       sequence_state.track.uuid = track_event_defaults.track_uuid();
-      if (defaults.has_timestamp_clock_id())
-        sequence_state.default_clock_id = defaults.timestamp_clock_id();
     }
   }
   if (packet.has_track_descriptor()) {
diff --git a/test/configs/BUILD.gn b/test/configs/BUILD.gn
index 4c93db1..b57b9aa 100644
--- a/test/configs/BUILD.gn
+++ b/test/configs/BUILD.gn
@@ -21,8 +21,8 @@
   script = "../../tools/protoc_helper.py"
 
   deps = [
-    "../../protos/perfetto/config:merged_config",
-    "../../protos/perfetto/trace:merged_trace",
+    "../../protos/perfetto/config:merged_config_source_set",
+    "../../protos/perfetto/trace:merged_trace_source_set",
     protoc_target,
   ]
 
diff --git a/test/test_helper.h b/test/test_helper.h
index 8407f20..778f88a 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
@@ -317,6 +318,8 @@
   std::unique_ptr<TracingService::ConsumerEndpoint> endpoint_;  // Keep last.
 };
 
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
 // This class is a reference to a child process that has in essence been execv
 // to the requested binary. The process will start and then wait for Run()
 // before proceeding. We use this to fork new processes before starting any
@@ -414,6 +417,8 @@
   base::Pipe sync_pipe_;
 };
 
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
 }  // namespace perfetto
 
 #endif  // TEST_TEST_HELPER_H_
diff --git a/test/trace_processor/chrome/scroll_jank.out b/test/trace_processor/chrome/scroll_jank.out
index baaa642..163727e 100644
--- a/test/trace_processor/chrome/scroll_jank.out
+++ b/test/trace_processor/chrome/scroll_jank.out
@@ -1,141 +1,141 @@
 
-"gesture_scroll_id","trace_id","jank","ts","dur"
-2708,2709,0,544958000403,19009426
-2708,2711,0,544966000403,22609426
-2708,2715,0,544983000403,28089426
-2708,2717,0,544991000403,30714426
-2708,2719,0,544999000403,34740426
-2708,2721,0,545007000403,37462426
-2708,2725,0,545024000403,31889426
-2708,2727,0,545032000403,23876426
-2708,2729,0,545040000403,28316426
-2708,2733,0,545056000403,22620426
-2708,2735,0,545065000403,23642426
-2708,2739,0,545081000403,19016426
-2708,2741,0,545089000403,22491426
-2708,2743,0,545098000403,24940426
-2708,2747,0,545114000403,19365426
-2708,2749,0,545122000403,22629426
-2708,2753,0,545139000403,17669426
-2708,2755,0,545147000403,20941426
-2708,2757,0,545155000403,23321426
-2708,2761,0,545172000403,18585426
-2708,2763,0,545180000403,22430426
-2708,2765,0,545188000403,23545426
-2708,2769,0,545204000403,19115426
-2708,2771,0,545213000403,21107426
-2708,2773,0,545221000403,25065426
-2708,2777,0,545237000403,20064426
-2708,2779,0,545246000403,22190426
-2708,2783,0,545262000403,16635426
-2708,2785,0,545270000403,19926426
-2708,2787,0,545278000403,23003426
-2708,2791,0,545295000403,17169426
-2708,2793,0,545303000403,20626426
-2708,2795,0,545311000403,23560426
-2708,2799,0,545328000403,18191426
-2708,2801,0,545336000403,20911426
-2708,2803,0,545344000403,24228426
-2708,2807,0,545361000403,18189426
-2708,2809,0,545369000403,21379426
-2708,2811,0,545377000403,24913426
-2708,2815,0,545394000403,18671426
-2708,2817,0,545402000403,21928426
-2708,2821,0,545418000403,17254426
-2708,2823,0,545426000403,20407426
-2708,2825,0,545435000403,22633426
-2708,2827,0,545443000403,27179426
-2708,2831,0,545459000403,20575426
-2708,2833,0,545468000403,23489426
-2708,2837,0,545484000403,18277426
-2708,2839,0,545492000403,21649426
-2708,2841,0,545500000403,24734426
-2708,2845,0,545517000403,18824426
-2708,2847,0,545525000403,22343426
-2708,2849,0,545533000403,25222426
-2708,2853,0,545550000403,19151426
-2708,2855,0,545558000403,22660426
-2708,2859,0,545574000403,17603426
-2708,2861,0,545583000403,19608426
-2708,2863,0,545591000403,22822426
-2708,2867,0,545607000403,18005426
-2708,2869,0,545615000403,21063426
-2708,2871,0,545624000403,23894426
-2708,2875,0,545640000403,18611426
-2708,2877,0,545648000403,21759426
-2708,2879,0,545656000403,25004426
-2708,2881,0,545671000403,32742426
-2708,2884,0,545681794403,32652426
-2708,2885,0,545691182403,34488426
-2708,2886,0,545702355403,34887426
-2708,2887,0,545713526403,34615426
-2708,2888,0,545724697403,36799426
-2708,2889,0,545735867403,35326426
-2708,2890,0,545747040403,35047426
-2708,2891,0,545758211403,34990426
-2708,2892,0,545769381403,39529426
-2708,2893,0,545780550403,35967426
-2708,2894,1,545791721403,45468426
-2708,2895,1,545802892403,45651426
-2708,2897,0,545825234403,34662426
-2917,2918,0,546027000403,24672426
-2917,2920,0,546035000403,27274426
-2917,2922,0,546044000403,28587426
-2917,2924,1,546052000403,41821426
-2917,2926,1,546060000403,44914426
-2917,2930,0,546076000403,28902426
-2917,2932,0,546085000403,31881426
-2917,2934,0,546093000403,34989426
-2917,2938,1,546109000403,41953426
-2917,2940,0,546118000403,32872426
-2917,2942,0,546126000403,35348426
-2917,2946,0,546142000403,42106426
-2917,2948,1,546150000403,45005426
-2917,2950,0,546159000403,35994426
-2917,2954,0,546175000403,30970426
-2917,2956,1,546183000403,45256426
-2917,2960,0,546200000403,40097426
-2917,2962,0,546208000403,32086426
-2917,2964,0,546216000403,34686426
-2917,2968,0,546233000403,28852426
-2917,2970,0,546241000403,32494426
-2917,2972,0,546249000403,37304426
-2917,2976,0,546265000403,32101426
-2917,2978,0,546273000403,34156426
-2917,2982,0,546290000403,27915426
-2917,2984,0,546298000403,30723426
-2917,2986,0,546307000403,32918426
-2917,2990,0,546323000403,29800426
-2917,2992,0,546331000403,31314426
-2917,2994,0,546339000403,35092426
-2917,2998,0,546356000403,28702426
-2917,3000,0,546364000403,32457426
-2917,3002,0,546372000403,35043426
-2917,3006,0,546389000403,29285426
-2917,3008,0,546397000403,32831426
-2917,3010,0,546405000403,35630426
-2917,3014,0,546422000403,30119426
-2917,3016,0,546430000403,33185426
-2917,3020,0,546446000403,28386426
-2917,3022,0,546455000403,30102426
-2917,3024,0,546463000403,33512426
-2917,3028,0,546479000403,28455426
-2917,3030,0,546488000403,30791426
-2917,3032,0,546496000403,34105426
-2917,3036,0,546512000403,29329426
-2917,3038,0,546520000403,32321426
-2917,3040,0,546529000403,34457426
-2917,3044,0,546545000403,29668426
-2917,3046,0,546553000403,32830426
-2917,3048,0,546561000403,35849426
-2917,3052,0,546578000403,30365426
-2917,3054,0,546586000403,33666426
-2917,3056,0,546594000403,36913426
-2917,3060,0,546611000403,30855426
-2917,3062,0,546619000403,33897426
-2917,3064,0,546627000403,36941426
-2917,3068,0,546644000403,31194426
-2917,3070,0,546652000403,34276426
-2917,3074,0,546676000403,32746426
-2917,3077,0,546701627403,29680426
-2917,3079,0,546718977403,34597426
+"gesture_scroll_id","trace_id","jank","ts","dur","jank_budget"
+2708,2709,0,544958000403,19009426,-11933333.350000
+2708,2711,0,544966000403,22609426,-4733333.350000
+2708,2715,0,544983000403,28089426,-2853333.350000
+2708,2717,0,544991000403,30714426,-5708333.350000
+2708,2719,0,544999000403,34740426,-4307333.350000
+2708,2721,0,545007000403,37462426,-2760333.350000
+2708,2725,0,545024000403,31889426,-320333.350000
+2708,2727,0,545032000403,23876426,-12773333.350000
+2708,2729,0,545040000403,28316426,-2637333.350000
+2708,2733,0,545056000403,22620426,-9355333.350000
+2708,2735,0,545065000403,23642426,-3707333.350000
+2708,2739,0,545081000403,19016426,-11808333.350000
+2708,2741,0,545089000403,22491426,-4858333.350000
+2708,2743,0,545098000403,24940426,-2758333.350000
+2708,2747,0,545114000403,19365426,-11597333.350000
+2708,2749,0,545122000403,22629426,-3373333.350000
+2708,2753,0,545139000403,17669426,-11605333.350000
+2708,2755,0,545147000403,20941426,-5061333.350000
+2708,2757,0,545155000403,23321426,-3597333.350000
+2708,2761,0,545172000403,18585426,-12178333.350000
+2708,2763,0,545180000403,22430426,-4488333.350000
+2708,2765,0,545188000403,23545426,-3903333.350000
+2708,2769,0,545204000403,19115426,-10325333.350000
+2708,2771,0,545213000403,21107426,-6341333.350000
+2708,2773,0,545221000403,25065426,-3332333.350000
+2708,2777,0,545237000403,20064426,-10459333.350000
+2708,2779,0,545246000403,22190426,-2778333.350000
+2708,2783,0,545262000403,16635426,-11624333.350000
+2708,2785,0,545270000403,19926426,-5042333.350000
+2708,2787,0,545278000403,23003426,-2499333.350000
+2708,2791,0,545295000403,17169426,-11790333.350000
+2708,2793,0,545303000403,20626426,-4876333.350000
+2708,2795,0,545311000403,23560426,-2964333.350000
+2708,2799,0,545328000403,18191426,-11053333.350000
+2708,2801,0,545336000403,20911426,-5613333.350000
+2708,2803,0,545344000403,24228426,-2294333.350000
+2708,2807,0,545361000403,18189426,-11523333.350000
+2708,2809,0,545369000403,21379426,-5143333.350000
+2708,2811,0,545377000403,24913426,-2091333.350000
+2708,2815,0,545394000403,18671426,-11590333.350000
+2708,2817,0,545402000403,21928426,-3659333.350000
+2708,2821,0,545418000403,17254426,-11486333.350000
+2708,2823,0,545426000403,20407426,-5180333.350000
+2708,2825,0,545435000403,22633426,-6107333.350000
+2708,2827,0,545443000403,27179426,-1729333.350000
+2708,2831,0,545459000403,20575426,-11247333.350000
+2708,2833,0,545468000403,23489426,-3121333.350000
+2708,2837,0,545484000403,18277426,-11705333.350000
+2708,2839,0,545492000403,21649426,-4961333.350000
+2708,2841,0,545500000403,24734426,-2423333.350000
+2708,2845,0,545517000403,18824426,-11852333.350000
+2708,2847,0,545525000403,22343426,-4814333.350000
+2708,2849,0,545533000403,25222426,-2262333.350000
+2708,2853,0,545550000403,19151426,-11842333.350000
+2708,2855,0,545558000403,22660426,-3276333.350000
+2708,2859,0,545574000403,17603426,-10338333.350000
+2708,2861,0,545583000403,19608426,-6328333.350000
+2708,2863,0,545591000403,22822426,-3516333.350000
+2708,2867,0,545607000403,18005426,-11391333.350000
+2708,2869,0,545615000403,21063426,-5275333.350000
+2708,2871,0,545624000403,23894426,-3050333.350000
+2708,2875,0,545640000403,18611426,-11481333.350000
+2708,2877,0,545648000403,21759426,-5185333.350000
+2708,2879,0,545656000403,25004426,-5088333.350000
+2708,2881,0,545671000403,32742426,-595333.350000
+2708,2884,0,545681794403,32652426,-8423333.350000
+2708,2885,0,545691182403,34488426,-6497333.350000
+2708,2886,0,545702355403,34887426,-7934333.350000
+2708,2887,0,545713526403,34615426,-8605333.350000
+2708,2888,0,545724697403,36799426,-6149333.350000
+2708,2889,0,545735867403,35326426,-8054333.350000
+2708,2890,0,545747040403,35047426,-8276333.350000
+2708,2891,0,545758211403,34990426,-8390333.350000
+2708,2892,0,545769381403,39529426,-3794333.350000
+2708,2893,0,545780550403,35967426,-11895333.350000
+2708,2894,1,545791721403,45468426,1167666.650000
+2708,2895,1,545802892403,45651426,2655666.650000
+2708,2897,0,545825234403,34662426,1656666.650000
+2917,2918,0,546027000403,24672426,-10935333.350000
+2917,2920,0,546035000403,27274426,-5731333.350000
+2917,2922,0,546044000403,28587426,-7020333.350000
+2917,2924,1,546052000403,41821426,4900666.650000
+2917,2926,1,546060000403,44914426,7678666.650000
+2917,2930,0,546076000403,28902426,-11312333.350000
+2917,2932,0,546085000403,31881426,-5354333.350000
+2917,2934,0,546093000403,34989426,-5225333.350000
+2917,2938,1,546109000403,41953426,747666.650000
+2917,2940,0,546118000403,32872426,-10809333.350000
+2917,2942,0,546126000403,35348426,-5857333.350000
+2917,2946,0,546142000403,42106426,-1575333.350000
+2917,2948,1,546150000403,45005426,677666.650000
+2917,2950,0,546159000403,35994426,-3309333.350000
+2917,2954,0,546175000403,30970426,-13357333.350000
+2917,2956,1,546183000403,45256426,5952666.650000
+2917,2960,0,546200000403,40097426,-322333.350000
+2917,2962,0,546208000403,32086426,-10933333.350000
+2917,2964,0,546216000403,34686426,-2499333.350000
+2917,2968,0,546233000403,28852426,-11975333.350000
+2917,2970,0,546241000403,32494426,-4691333.350000
+2917,2972,0,546249000403,37304426,-3130333.350000
+2917,2976,0,546265000403,32101426,-10388333.350000
+2917,2978,0,546273000403,34156426,-2092333.350000
+2917,2982,0,546290000403,27915426,-11141333.350000
+2917,2984,0,546298000403,30723426,-5525333.350000
+2917,2986,0,546307000403,32918426,-5215333.350000
+2917,2990,0,546323000403,29800426,-9847333.350000
+2917,2992,0,546331000403,31314426,-6819333.350000
+2917,2994,0,546339000403,35092426,-1943333.350000
+2917,2998,0,546356000403,28702426,-12088333.350000
+2917,3000,0,546364000403,32457426,-4578333.350000
+2917,3002,0,546372000403,35043426,-2575333.350000
+2917,3006,0,546389000403,29285426,-11879333.350000
+2917,3008,0,546397000403,32831426,-4787333.350000
+2917,3010,0,546405000403,35630426,-2822333.350000
+2917,3014,0,546422000403,30119426,-11399333.350000
+2917,3016,0,546430000403,33185426,-3534333.350000
+2917,3020,0,546446000403,28386426,-10049333.350000
+2917,3022,0,546455000403,30102426,-6617333.350000
+2917,3024,0,546463000403,33512426,-3276333.350000
+2917,3028,0,546479000403,28455426,-10669333.350000
+2917,3030,0,546488000403,30791426,-5997333.350000
+2917,3032,0,546496000403,34105426,-3557333.350000
+2917,3036,0,546512000403,29329426,-11325333.350000
+2917,3038,0,546520000403,32321426,-5341333.350000
+2917,3040,0,546529000403,34457426,-3544333.350000
+2917,3044,0,546545000403,29668426,-11495333.350000
+2917,3046,0,546553000403,32830426,-5171333.350000
+2917,3048,0,546561000403,35849426,-2849333.350000
+2917,3052,0,546578000403,30365426,-11634333.350000
+2917,3054,0,546586000403,33666426,-5032333.350000
+2917,3056,0,546594000403,36913426,-2275333.350000
+2917,3060,0,546611000403,30855426,-11375333.350000
+2917,3062,0,546619000403,33897426,-5291333.350000
+2917,3064,0,546627000403,36941426,-2586333.350000
+2917,3068,0,546644000403,31194426,-11415333.350000
+2917,3070,0,546652000403,34276426,-5251333.350000
+2917,3074,0,546676000403,32746426,-5267333.350000
+2917,3077,0,546701627403,29680426,-11399333.350000
+2917,3079,0,546718977403,34597426,-3416333.350000
diff --git a/test/trace_processor/chrome/scroll_jank.sql b/test/trace_processor/chrome/scroll_jank.sql
index 2a502c1..9aaa99b 100644
--- a/test/trace_processor/chrome/scroll_jank.sql
+++ b/test/trace_processor/chrome/scroll_jank.sql
@@ -20,5 +20,6 @@
   trace_id,
   jank,
   ts,
-  dur
+  dur,
+  jank_budget
 FROM scroll_jank;
diff --git a/test/trace_processor/chrome/touch_jank.out b/test/trace_processor/chrome/touch_jank.out
index 9f55ea8..18edb3f 100644
--- a/test/trace_processor/chrome/touch_jank.out
+++ b/test/trace_processor/chrome/touch_jank.out
@@ -1,16 +1,16 @@
 
-"touch_id","trace_id","jank","ts","dur"
-6280,6288,1,1383122421558418,226350000
-6280,6312,0,1383122543558418,185244000
-6318,6328,1,1383123604558418,207945000
-6318,6414,0,1383124088558418,173233000
-6420,6552,0,1383125521558418,161901000
-6586,6602,0,1383126581558418,87964000
-6586,6693,1,1383127076558418,140115000
-6697,6698,1,1383127670558418,301236000
-6697,6723,0,1383127837558418,134236000
-6788,6802,0,1383129053558418,163272000
-6788,6877,1,1383129492558418,370407000
-6906,6911,1,1383130519558418,198057000
-6906,6915,0,1383130553558418,164057000
-6906,6940,0,1383130681558418,167758000
+"touch_id","trace_id","jank","ts","dur","jank_budget"
+6280,6288,1,1383122421558418,226350000,32772666.650000
+6280,6312,0,1383122543558418,185244000,-31034333.350000
+6318,6328,1,1383123604558418,207945000,26378666.650000
+6318,6414,0,1383124088558418,173233000,2998666.650000
+6420,6552,0,1383125521558418,161901000,65603666.650000
+6586,6602,0,1383126581558418,87964000,-60484333.350000
+6586,6693,1,1383127076558418,140115000,43817666.650000
+6697,6698,1,1383127670558418,301236000,158666666.650000
+6697,6723,0,1383127837558418,134236000,-37369333.350000
+6788,6802,0,1383129053558418,163272000,20702666.650000
+6788,6877,1,1383129492558418,370407000,198801666.650000
+6906,6911,1,1383130519558418,198057000,25666666.650000
+6906,6915,0,1383130553558418,164057000,-12034333.350000
+6906,6940,0,1383130681558418,167758000,-4632333.350000
diff --git a/test/trace_processor/chrome/touch_jank.sql b/test/trace_processor/chrome/touch_jank.sql
index 723b146..d601148 100644
--- a/test/trace_processor/chrome/touch_jank.sql
+++ b/test/trace_processor/chrome/touch_jank.sql
@@ -20,5 +20,6 @@
   trace_id,
   jank,
   ts,
-  dur
+  dur,
+  jank_budget
 FROM touch_jank;
diff --git a/test/trace_processor/chrome/touch_jank_synth.out b/test/trace_processor/chrome/touch_jank_synth.out
index 0c6f96f..44758af 100644
--- a/test/trace_processor/chrome/touch_jank_synth.out
+++ b/test/trace_processor/chrome/touch_jank_synth.out
@@ -1,5 +1,5 @@
 
-"touch_id","trace_id","jank","ts","dur"
-87654,34577,0,0,10000000
-87654,34578,1,16000000,33000000
-87654,34579,0,55000000,33000000
+"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/fuchsia/fuchsia_smoke_instants.out b/test/trace_processor/fuchsia/fuchsia_smoke_instants.out
index 6e2dd9d..87964ab 100644
--- a/test/trace_processor/fuchsia/fuchsia_smoke_instants.out
+++ b/test/trace_processor/fuchsia/fuchsia_smoke_instants.out
@@ -1,11 +1,11 @@
-"ts","name","value"
-21442756010,"task_start",0.000000
-21446583438,"task_end",0.000000
-21448366538,"task_start",0.000000
-21450363277,"task_end",0.000000
-21454255741,"task_start",0.000000
-21457834528,"task_end",0.000000
-21459006408,"task_start",0.000000
-21460601866,"task_end",0.000000
-21461282720,"task_start",0.000000
-21462998487,"task_end",0.000000
+"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
index e437aa4..39c37a0 100644
--- a/test/trace_processor/fuchsia/fuchsia_smoke_slices.out
+++ b/test/trace_processor/fuchsia/fuchsia_smoke_slices.out
@@ -1,3 +1,3 @@
 "type","depth","count"
-"thread_track",0,1153
-"thread_track",1,5
+"thread_track",0,2153
+"thread_track",1,1004
diff --git a/test/trace_processor/fuchsia/fuchsia_workstation_smoke_slices.out b/test/trace_processor/fuchsia/fuchsia_workstation_smoke_slices.out
index daab85f..68945e2 100644
--- a/test/trace_processor/fuchsia/fuchsia_workstation_smoke_slices.out
+++ b/test/trace_processor/fuchsia/fuchsia_workstation_smoke_slices.out
@@ -2,8 +2,8 @@
 "process_track",0,85
 "process_track",1,14
 "process_track",2,1
-"thread_track",0,13379
-"thread_track",1,11548
+"thread_track",0,15198
+"thread_track",1,11607
 "thread_track",2,10181
 "thread_track",3,1927
 "thread_track",4,4001
diff --git a/test/trace_processor/fuchsia/smoke_instants.sql b/test/trace_processor/fuchsia/smoke_instants.sql
index b57a531..cc5c40e 100644
--- a/test/trace_processor/fuchsia/smoke_instants.sql
+++ b/test/trace_processor/fuchsia/smoke_instants.sql
@@ -15,7 +15,8 @@
 --
 select
   ts,
-  name,
-  value
-from instants
+  name
+from slice
+where
+  dur = 0
 limit 10;
diff --git a/test/trace_processor/graphics/android_sysui_cuj.out b/test/trace_processor/graphics/android_sysui_cuj.out
index 9d81d7b..b82ae3b 100644
--- a/test/trace_processor/graphics/android_sysui_cuj.out
+++ b/test/trace_processor/graphics/android_sysui_cuj.out
@@ -61,6 +61,12 @@
   }
   frames {
     number: 8
+    ts: 300000000
+    dur: 4000000
+    vsync: 110
+  }
+  frames {
+    number: 9
     ts: 400000000
     dur: 10000000
     vsync: 120
diff --git a/test/trace_processor/graphics/android_sysui_cuj.py b/test/trace_processor/graphics/android_sysui_cuj.py
index a3a8254..dbbf47c 100644
--- a/test/trace_processor/graphics/android_sysui_cuj.py
+++ b/test/trace_processor/graphics/android_sysui_cuj.py
@@ -28,11 +28,19 @@
   trace.add_atrace_end(ts=ts_end, tid=PID, pid=PID)
 
 
-def add_render_thread_atrace(trace, ts, ts_end, buf):
+def add_render_thread_atrace_begin(trace, ts, buf):
   trace.add_atrace_begin(ts=ts, tid=RTID, pid=PID, buf=buf)
+
+
+def add_render_thread_atrace_end(trace, ts_end):
   trace.add_atrace_end(ts=ts_end, tid=RTID, pid=PID)
 
 
+def add_render_thread_atrace(trace, ts, ts_end, buf):
+  add_render_thread_atrace_begin(trace, ts, buf)
+  add_render_thread_atrace_end(trace, ts_end)
+
+
 def add_gpu_thread_atrace(trace, ts, ts_end, buf):
   trace.add_atrace_begin(ts=ts, tid=1666, pid=PID, buf=buf)
   trace.add_atrace_end(ts=ts_end, tid=1666, pid=PID)
@@ -43,14 +51,30 @@
   trace.add_atrace_end(ts=ts_end, tid=JITID, pid=PID)
 
 
-def add_frame(trace, vsync, ts_do_frame, ts_end_do_frame, ts_draw_frame,
-              ts_end_draw_frame, ts_gpu, ts_end_gpu):
+def add_frame(trace,
+              vsync,
+              ts_do_frame,
+              ts_end_do_frame,
+              ts_draw_frame,
+              ts_end_draw_frame,
+              ts_gpu=None,
+              ts_end_gpu=None):
   add_main_thread_atrace(trace, ts_do_frame, ts_end_do_frame,
                          "Choreographer#doFrame %d" % vsync)
-  add_render_thread_atrace(trace, ts_draw_frame, ts_end_draw_frame,
-                           "DrawFrames %d" % vsync)
-  add_gpu_thread_atrace(trace, ts_gpu, ts_end_gpu,
-                        "waiting for GPU completion 123")
+
+  gpu_idx = 1000 + vsync * 10 + 1
+  if ts_gpu is None:
+    gpu_fence_message = "GPU completion fence %d has signaled"
+  else:
+    gpu_fence_message = "Trace GPU completion fence %d"
+  add_render_thread_atrace_begin(trace, ts_draw_frame, "DrawFrames %d" % vsync)
+  add_render_thread_atrace(trace, ts_end_draw_frame - 100,
+                           ts_end_draw_frame - 1, gpu_fence_message % gpu_idx)
+  add_render_thread_atrace_end(trace, ts_end_draw_frame)
+
+  if ts_gpu is not None:
+    add_gpu_thread_atrace(trace, ts_gpu, ts_end_gpu,
+                          "waiting for GPU completion %d" % gpu_idx)
 
 
 def add_display_frame_events(ts,
@@ -248,13 +272,19 @@
     ts_gpu=108_000_000,
     ts_end_gpu=115_600_000)
 
+add_render_thread_atrace_begin(trace, ts=108_000_000, buf="DrawFrames 90")
 add_render_thread_atrace(
-    trace, ts=108_000_000, ts_end=114_000_000, buf="DrawFrames 90")
+    trace,
+    ts=113_000_000,
+    ts_end=113_500_000,
+    buf="Trace GPU completion fence 1902")
+add_render_thread_atrace_end(trace, ts_end=114_000_000)
+
 add_gpu_thread_atrace(
     trace,
     ts=121_500_000,
     ts_end=122_000_000,
-    buf="waiting for GPU completion 123")
+    buf="waiting for GPU completion 1902")
 
 add_frame(
     trace,
@@ -276,8 +306,8 @@
     ts_end_do_frame=315_000_000,
     ts_draw_frame=302_000_000,
     ts_end_draw_frame=304_000_000,
-    ts_gpu=308_000_000,
-    ts_end_gpu=310_000_000)
+    ts_gpu=None,
+    ts_end_gpu=None)
 
 add_render_thread_atrace(
     trace, ts=305_000_000, ts_end=308_000_000, buf="dispatchFrameCallbacks")
diff --git a/test/trace_processor/network/index b/test/trace_processor/network/index
index 51ce794..3f8fa33 100644
--- a/test/trace_processor/network/index
+++ b/test/trace_processor/network/index
@@ -4,3 +4,4 @@
 netperf_metric.textproto android_netperf netperf_metric.out
 inet_sock_set_state.textproto inet_sock_set_state.sql inet_sock_set_state.out
 tcp_retransmit_skb.textproto tcp_retransmit_skb.sql tcp_retransmit_skb.out
+napi_gro_receive.textproto napi_gro_receive.sql napi_gro_receive.out
diff --git a/test/trace_processor/network/napi_gro_receive.out b/test/trace_processor/network/napi_gro_receive.out
new file mode 100644
index 0000000..0648e41
--- /dev/null
+++ b/test/trace_processor/network/napi_gro_receive.out
@@ -0,0 +1,4 @@
+"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.sql b/test/trace_processor/network/napi_gro_receive.sql
new file mode 100644
index 0000000..48ee38f
--- /dev/null
+++ b/test/trace_processor/network/napi_gro_receive.sql
@@ -0,0 +1,16 @@
+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/napi_gro_receive.textproto b/test/trace_processor/network/napi_gro_receive.textproto
new file mode 100644
index 0000000..ee202f8
--- /dev/null
+++ b/test/trace_processor/network/napi_gro_receive.textproto
@@ -0,0 +1,75 @@
+packet {
+  ftrace_events {
+    cpu: 2
+    event {
+      timestamp: 10000
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 1000
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 2
+    event {
+      timestamp: 10020
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 2
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 2
+    event {
+      timestamp: 20000
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 1000
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 2
+    event {
+      timestamp: 20020
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 1
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 4
+    event {
+      timestamp: 30000
+      pid: 200
+      napi_gro_receive_entry {
+        name: "wlan"
+        len: 500
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 4
+    event {
+      timestamp: 30020
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 3
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/network/netperf_metric.out b/test/trace_processor/network/netperf_metric.out
index 24c8aac..0077d53 100644
--- a/test/trace_processor/network/netperf_metric.out
+++ b/test/trace_processor/network/netperf_metric.out
@@ -32,6 +32,7 @@
           data_rate_kbps: 800.0
         }
       }
+      gro_aggregation_ratio: '1:1.75'
     }
     tx {
       total {
@@ -141,4 +142,5 @@
     }
     avg_interstack_latency_ms: 0.4
   }
+  retransmission_rate: 20.0
 }
diff --git a/test/trace_processor/network/netperf_metric.textproto b/test/trace_processor/network/netperf_metric.textproto
index 1684df9..0e478a9 100644
--- a/test/trace_processor/network/netperf_metric.textproto
+++ b/test/trace_processor/network/netperf_metric.textproto
@@ -2,6 +2,36 @@
   ftrace_events {
     cpu: 0
     event {
+      timestamp: 99999900
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 512
+      }
+    }
+    event {
+      timestamp: 99999920
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 1
+      }
+    }
+    event {
+      timestamp: 99999930
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 512
+      }
+    }
+    event {
+      timestamp: 99999940
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 2
+      }
+    }
+    event {
      timestamp: 100000000
      pid: 200
      cpu_frequency {
@@ -25,6 +55,18 @@
       }
     }
     event {
+      timestamp: 100090000
+      pid: 234
+      tcp_retransmit_skb {
+        daddr: 19216801
+        saddr: 127001
+        dport: 5001
+        sport: 56789
+        state: 1
+        skaddr: 77889900
+      }
+    }
+    event {
       timestamp: 100100000
       pid: 200
       net_dev_xmit {
@@ -46,6 +88,36 @@
   ftrace_events {
     cpu: 1
     event {
+      timestamp: 119999900
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 512
+      }
+    }
+    event {
+      timestamp: 119999920
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 1
+      }
+    }
+    event {
+      timestamp: 119999930
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 512
+      }
+    }
+    event {
+      timestamp: 119999940
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 2
+      }
+    }
+    event {
      timestamp: 120000000
      pid: 200
      cpu_frequency {
@@ -90,6 +162,36 @@
   ftrace_events {
     cpu: 0
     event {
+      timestamp: 139999900
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 512
+      }
+    }
+    event {
+      timestamp: 139999920
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 1
+      }
+    }
+    event {
+      timestamp: 139999930
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 512
+      }
+    }
+    event {
+      timestamp: 139999940
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 2
+      }
+    }
+    event {
      timestamp: 140000000
      pid: 200
      cpu_frequency {
@@ -134,6 +236,21 @@
   ftrace_events {
     cpu: 1
     event {
+      timestamp: 139999900
+      pid: 200
+      napi_gro_receive_entry {
+        name: "rmnet0"
+        len: 1024
+      }
+    }
+    event {
+      timestamp: 139999920
+      pid: 300
+      napi_gro_receive_exit {
+        ret: 3
+      }
+    }
+    event {
      timestamp: 140000000
      pid: 200
      cpu_frequency {
diff --git a/test/trace_processor/profiling/heap_graph_deobfuscate_pkg.textproto b/test/trace_processor/profiling/heap_graph_deobfuscate_pkg.textproto
new file mode 100644
index 0000000..4697548
--- /dev/null
+++ b/test/trace_processor/profiling/heap_graph_deobfuscate_pkg.textproto
@@ -0,0 +1,141 @@
+packet {
+  process_tree {
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "<ignored>"
+      uid: 10001
+    }
+  }
+}
+packet {
+  packages_list {
+    packages {
+      name: "com.google.android.gm"
+      uid: 10001
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    roots {
+      root_type: ROOT_JAVA_FRAME
+      object_ids: 0x01
+      object_ids: 0x05
+    }
+    objects {
+      id: 0x01
+      type_id: 1
+      self_size: 64
+      reference_field_id: 1
+      reference_object_id: 0x02
+      reference_field_id: 4
+      reference_object_id: 0x00
+      reference_field_id: 3
+      reference_object_id: 0x02
+    }
+    objects {
+      id: 0x02
+      type_id: 2
+      self_size: 32
+    }
+    objects {
+      id: 0x03
+      type_id: 2
+      self_size: 128
+    }
+    objects {
+      id: 0x04
+      type_id: 3
+      self_size: 1024
+      reference_field_id: 2
+      reference_object_id: 0x01
+    }
+    objects {
+      id: 0x05
+      type_id: 4
+      self_size: 256
+    }
+    objects {
+      id: 0x06
+      type_id: 5
+      self_size: 256
+    }
+    continued: true
+    index: 0
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    location_names {
+      iid: 1
+      str: "base.apk!classes.dex"
+    }
+    types {
+      id: 1
+      class_name: "FactoryProducerDelegateImplActor"
+      location_id: 1
+    }
+    types {
+      id: 2
+      class_name: "Foo"
+      location_id: 1
+    }
+    types {
+      id: 3
+      class_name: "a"
+      location_id: 1
+    }
+    types {
+      id: 4
+      class_name: "a[]"
+      location_id: 1
+    }
+    types {
+      id: 5
+      class_name: "java.lang.Class<a[]>"
+      location_id: 1
+    }
+    field_names {
+      iid: 1
+      str: "FactoryProducerDelegateImplActor.foo"
+    }
+    field_names {
+      iid: 2
+      str: "int a.a"
+    }
+    field_names {
+      iid: 3
+      str: "a.b"
+    }
+    field_names {
+      iid: 4
+      str: "FactoryProducerDelegateImplActor.setToNULL"
+    }
+    continued: true
+    index: 1
+  }
+}
+packet {
+  deobfuscation_mapping {
+    package_name: "com.google.android.gm"
+    obfuscated_classes {
+      obfuscated_name: "a"
+      deobfuscated_name: "DeobfuscatedA"
+      obfuscated_members {
+        obfuscated_name: "a"
+        deobfuscated_name: "deobfuscatedA"
+      }
+      obfuscated_members {
+        obfuscated_name: "b"
+        deobfuscated_name: "Other.deobfuscatedA"
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/profiling/index b/test/trace_processor/profiling/index
index 07a9340..1099a51 100644
--- a/test/trace_processor/profiling/index
+++ b/test/trace_processor/profiling/index
@@ -13,6 +13,7 @@
 heap_graph_baseapk.textproto heap_graph_flamegraph.sql heap_graph_flamegraph.out
 heap_graph_baseapk.textproto heap_graph_object.sql heap_graph_object.out
 heap_graph_baseapk.textproto heap_graph_reference.sql heap_graph_reference.out
+heap_graph_deobfuscate_pkg.textproto heap_graph_object.sql heap_graph_object.out
 
 heap_graph_duplicate.textproto heap_graph_flamegraph.sql heap_graph_duplicate_flamegraph.out
 
diff --git a/tools/ftrace_proto_gen/event_list b/tools/ftrace_proto_gen/event_list
index 0535103..948eb11 100644
--- a/tools/ftrace_proto_gen/event_list
+++ b/tools/ftrace_proto_gen/event_list
@@ -358,3 +358,6 @@
 net/net_dev_xmit
 sock/inet_sock_set_state
 tcp/tcp_retransmit_skb
+cros_ec/cros_ec_sensorhub_data
+net/napi_gro_receive_entry
+net/napi_gro_receive_exit
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 73b4e45..e5c7f82 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -64,12 +64,12 @@
 # These targets are required by internal build rules but don't need to be
 # exported publicly.
 default_targets = [
-    '//test:client_api_example',
     '//src/ipc:perfetto_ipc',
     '//src/ipc/protoc_plugin:ipc_plugin',
     '//src/protozero:protozero',
     '//src/protozero/protoc_plugin:protozero_plugin',
     '//src/protozero/protoc_plugin:cppgen_plugin',
+    '//test:client_api_example',
     '//tools/proto_filter:proto_filter',
     '//tools/proto_merger:proto_merger',
 ] + public_targets
@@ -77,14 +77,18 @@
 # Root proto targets (to force discovery of intermediate proto targets).
 # These targets are marked public.
 proto_targets = [
-    '//protos/perfetto/trace:merged_trace',
+    '//protos/perfetto/trace:merged_trace_source_set',
     '//protos/perfetto/trace:non_minimal_lite',
-    '//protos/perfetto/config:merged_config',
+    '//protos/perfetto/trace:non_minimal_source_set',
+    '//protos/perfetto/config:merged_config_source_set',
     '//protos/perfetto/metrics:lite',
+    '//protos/perfetto/metrics:source_set',
     '//protos/perfetto/metrics/android:lite',
-    '//protos/perfetto/trace:lite',
-    '//protos/perfetto/config:lite',
+    '//protos/perfetto/metrics/android:source_set',
+    '//protos/perfetto/trace:source_set',
+    '//protos/perfetto/config:source_set',
     '//protos/third_party/chromium:lite',
+    '//protos/third_party/chromium:source_set',
 ]
 
 # Path for the protobuf sources in the standalone build.
@@ -143,7 +147,8 @@
 
 
 custom_actions = {
-    gn_utils.GEN_VERSION_TARGET: gen_version_header,
+    gn_utils.GEN_VERSION_TARGET:
+        gen_version_header,
     '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics':
         gen_amalgamated_sql_metrics,
 }
@@ -171,12 +176,14 @@
     self.tools = []
     self.exec_tools = []
     self.outs = []
+    self.exports = []
 
   def __lt__(self, other):
     if isinstance(other, self.__class__):
       return self.name < other.name
-    raise TypeError('\'<\' not supported between instances of \'%s\' and \'%s\''
-                    % (type(self).__name__, type(other).__name__))
+    raise TypeError(
+        '\'<\' not supported between instances of \'%s\' and \'%s\'' %
+        (type(self).__name__, type(other).__name__))
 
   def __str__(self):
     """Converts the object into a Bazel Starlark label."""
@@ -185,8 +192,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'
+        'name', 'srcs', 'hdrs', 'visibility', 'deps', 'outs', 'cmd', 'tools',
+        'exec_tools', 'exports'
     ]
     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])
@@ -250,15 +257,8 @@
   return gn_utils.label_to_target_name_with_path(gn_name)
 
 
-def gen_proto_labels(target):
-  """ Generates the xx_proto_library label for proto targets.
-
-  Bazel requires that each protobuf-related target is modeled with two labels:
-  1. A plugin-agnostic target that defines only the .proto sources and their
-     dependencies.
-  2. A plugin-dependent target (e.g. cc_library, cc_protozero_library) that has
-     only a dependency on 1 and does NOT refer to any .proto sources.
-  """
+def gen_proto_label(target):
+  """ Generates the xx_proto_library label for proto targets."""
   assert (target.type == 'proto_library')
 
   def get_sources_label(target_name):
@@ -267,46 +267,39 @@
 
   sources_label_name = get_sources_label(target.name)
 
-  # Generates 1.
-  sources_label = BazelLabel(sources_label_name, 'perfetto_proto_library')
-  sources_label.comment = target.name
-  assert (all(x.startswith('//') for x in target.sources))
-  assert (all(x.endswith('.proto') for x in target.sources))
-  sources_label.srcs = sorted([x[2:] for x in target.sources])  # Strip //.
-
-  deps = [
-      ':' + get_sources_label(x)
-      for x in target.proto_deps
-
-      # This is to avoid a dependency-on-self in the case where
-      # protos/perfetto/ipc:ipc depends on protos/perfetto/ipc:cpp and both
-      # targets resolve to "protos_perfetto_ipc_protos".
-      if get_sources_label(x) != sources_label_name
-  ]
-  sources_label.deps = sorted(deps)
-
-  # In Bazel, proto_paths are not a supported concept becauase strong dependency
-  # checking is enabled. Instead, we need to depend on the target which includes
-  # the proto we want to depend on.
-  # For example, we include the proto_path |buildtools_protobuf_src| because we
-  # want to depend on the "google/protobuf/descriptor.proto" proto file. This
-  # will be exposed by the |protobuf_descriptor_proto| dep.
-  if buildtools_protobuf_src in target.proto_paths:
-    sources_label.external_deps = [
-        'PERFETTO_CONFIG.deps.protobuf_descriptor_proto'
-    ]
-
-  if target.name in proto_targets:
-    sources_label.visibility = PUBLIC_VISIBILITY
-  else:
-    sources_label.visibility = ['PERFETTO_CONFIG.proto_library_visibility']
-
   # For 'source_set' plugins, we don't want to generate any plugin-dependent
   # targets so just return the label of the proto sources only.
   if target.proto_plugin == 'source_set':
-    return [sources_label]
+    sources_label = BazelLabel(sources_label_name, 'perfetto_proto_library')
+    sources_label.comment = target.name
+    assert (all(x.startswith('//') for x in target.sources))
+    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_sources_label(x) for x in target.proto_deps])
 
-  # Generates 2.
+    # In Bazel, proto_paths are not a supported concept becauase strong
+    # dependency checking is enabled. Instead, we need to depend on the target
+    # which includes the proto we want to depend on.
+    # For example, we include the proto_path |buildtools_protobuf_src| because
+    # we want to depend on the "google/protobuf/descriptor.proto" proto file.
+    # This will be exposed by the |protobuf_descriptor_proto| dep.
+    if buildtools_protobuf_src in target.proto_paths:
+      sources_label.external_deps = [
+          'PERFETTO_CONFIG.deps.protobuf_descriptor_proto'
+      ]
+
+    if target.name in proto_targets:
+      sources_label.visibility = PUBLIC_VISIBILITY
+    else:
+      sources_label.visibility = ['PERFETTO_CONFIG.proto_library_visibility']
+
+    sources_label.exports = sorted([
+      ':' + get_sources_label(d) for d in target.proto_exports
+    ])
+    return sources_label
+
+  # For all other types of plugins, we need to generate 
   if target.proto_plugin == 'proto':
     plugin_label_type = 'perfetto_cc_proto_library'
   elif target.proto_plugin == 'protozero':
@@ -322,28 +315,39 @@
   plugin_label_name = get_bazel_label_name(target.name)
   plugin_label = BazelLabel(plugin_label_name, plugin_label_type)
   plugin_label.comment = target.name
-  plugin_label.deps += [':' + sources_label_name]
 
   # When using the plugins we need to pass down also the transitive deps.
   # For instance consider foo.proto including common.proto. The generated
   # foo.cc will #include "common.gen.h". Hence the generated cc_protocpp_library
   # rule need to pass down the dependency on the target that generates
   # common.gen.{cc,h}.
-  if target.proto_deps and target.proto_plugin in (
-      'cppgen', 'ipc', 'protozero'):
+  if target.proto_plugin in ('cppgen', 'ipc', 'protozero'):
     plugin_label.deps += [
         ':' + get_bazel_label_name(x) for x in target.proto_deps
     ]
 
+  # Add any dependencies on source_set targets (i.e. targets containing proto
+  # files). For descriptors, we will have an explicit edge between the
+  # descriptor and source set wheras for other plugin types, this edge is
+  # implicit.
+  if target.proto_plugin == 'descriptor':
+    plugin_label.deps += [
+      ':' + get_sources_label(x) for x in target.proto_deps
+    ]
+  else:
+    plugin_label.deps += [':' + sources_label_name]
+
+  # Since the descriptor generates an explicit output file which can be
+  # referenced by other targets, we specify a name for it.
   if target.proto_plugin == 'descriptor':
     plugin_label.outs = [plugin_label_name + '.bin']
 
-  return [sources_label, plugin_label]
+  return plugin_label
 
 
 def gen_target(gn_target):
   if gn_target.type == 'proto_library':
-    return gen_proto_labels(gn_target)
+    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)
@@ -374,7 +378,7 @@
       label.hdrs += [x[2:] for x in gn_target.public_headers]
     else:
       raise Error('%s: \'public\' currently supported only for cc_library' %
-              gn_target.name)
+                  gn_target.name)
 
   raw_srcs = [x[2:] for x in gn_target.sources]
   if bazel_type == 'perfetto_cc_library':
@@ -511,20 +515,20 @@
   for target_name in sorted(proto_targets):
     gn.get_target(target_name)
 
-  # Generate targets for the transitive set of proto targets.
-  # TODO explain deduping here.
-  labels = {}
-  for target in sorted(itervalues(gn.proto_libs)):
-    for label in gen_target(target):
-      # Ensure that if the existing target has public visibility, we preserve
-      # that in the new label; this ensures that we don't accidentaly reduce
-      # the visibility of targets which are meant to be public.
-      existing_label = labels.get(label.name)
-      if existing_label and existing_label.visibility == PUBLIC_VISIBILITY:
-        label.visibility = PUBLIC_VISIBILITY
-      labels[label.name] = label
+  # For any non-source set and non-descriptor targets, ensure the source set
+  # associated to that target is discovered.
+  for target in sorted(itervalues(gn.all_targets)):
+    plugin = target.proto_plugin
+    if plugin is None or plugin == 'source_set' or plugin == 'descriptor':
+      continue
+    gn.get_target(re.sub('(lite|zero|cpp|ipc)$', 'source_set', target.name))
 
-  res += ''.join(str(x) for x in sorted(itervalues(labels)))
+  # Generate targets for the transitive set of proto targets.
+  labels = [
+    l for target in sorted(itervalues(gn.proto_libs))
+    for l in gen_target(target)
+  ]
+  res += ''.join(str(x) for x in sorted(labels))
 
   res += '''
 # ##############################################################################
@@ -542,6 +546,7 @@
 
   return res
 
+
 def main():
   parser = argparse.ArgumentParser(
       description='Generate BUILD from a GN description.')
diff --git a/tools/gn_utils.py b/tools/gn_utils.py
index 2a0f8c6..771d16e 100644
--- a/tools/gn_utils.py
+++ b/tools/gn_utils.py
@@ -310,6 +310,7 @@
       # This is typically: 'proto', 'protozero', 'ipc'.
       self.proto_plugin = None
       self.proto_paths = set()
+      self.proto_exports = set()
 
       self.sources = set()
       # TODO(primiano): consider whether the public section should be part of
@@ -395,12 +396,13 @@
       target.is_third_party_dep_ = True
       return target
 
-    proto_target_type, proto_desc = self.get_proto_target_type_(target)
+    proto_target_type, proto_desc = self.get_proto_target_type(target)
     if proto_target_type is not None:
       self.proto_libs[target.name] = target
       target.type = 'proto_library'
       target.proto_plugin = proto_target_type
       target.proto_paths.update(self.get_proto_paths(proto_desc))
+      target.proto_exports.update(self.get_proto_exports(proto_desc))
       target.sources.update(proto_desc.get('sources', []))
       assert (all(x.endswith('.proto') for x in target.sources))
     elif target.type == 'source_set':
@@ -444,7 +446,7 @@
         target.proto_paths.update(dep.proto_paths)
 
         # Don't bubble deps for action targets
-        if target.type != 'action':
+        if target.type != 'action' and proto_target_type != 'descriptor':
           target.proto_deps.update(dep.proto_deps)  # Bubble up deps.
       elif dep.type == 'source_set':
         target.source_set_deps.add(dep_name)
@@ -459,24 +461,17 @@
 
     return target
 
+  def get_proto_exports(self, proto_desc):
+    # exports in metadata will be available for source_set targets.
+    metadata = proto_desc.get('metadata', {})
+    return metadata.get('exports', [])
+
   def get_proto_paths(self, proto_desc):
     # import_dirs in metadata will be available for source_set targets.
     metadata = proto_desc.get('metadata', {})
-    import_dirs = metadata.get('import_dirs', [])
-    if import_dirs:
-      return import_dirs
+    return metadata.get('import_dirs', [])
 
-    # For all non-source-set targets, we need to parse the command line
-    # of the protoc invocation.
-    proto_paths = []
-    args = proto_desc.get('args', [])
-    for i, arg in enumerate(args):
-      if arg != '--proto_path':
-        continue
-      proto_paths.append(re.sub('^../../', '//', args[i + 1]))
-    return proto_paths
-
-  def get_proto_target_type_(self, target):
+  def get_proto_target_type(self, target):
     """ Checks if the target is a proto library and return the plugin.
 
         Returns:
@@ -497,7 +492,7 @@
       return 'descriptor', desc
 
     # Source set proto targets have a non-empty proto_library_sources in the
-    # metadata of the descirption.
+    # metadata of the description.
     metadata = desc.get('metadata', {})
     if 'proto_library_sources' in metadata:
       return 'source_set', desc
diff --git a/tools/heap_profile b/tools/heap_profile
index 6eaaa47..4539dc1 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -576,7 +576,7 @@
 
 
 # BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v23.0
+# Revision: v24.1
 PERFETTO_PREBUILT_MANIFEST = [{
     'tool':
         'trace_to_text',
@@ -585,11 +585,11 @@
     'file_name':
         'trace_to_text',
     'file_size':
-        6939864,
+        6972736,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/mac-amd64/trace_to_text',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/mac-amd64/trace_to_text',
     'sha256':
-        '1626880d1fbec8efc9702583b6c22d617079a08226df414d65888a6b3c7572c8',
+        'cd963e3f63b23977302fcbcb19a9fe64e98aeb2f419ce4212cfafa4e52b8faec',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -601,11 +601,11 @@
     'file_name':
         'trace_to_text.exe',
     'file_size':
-        6658560,
+        6685696,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/windows-amd64/trace_to_text.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/windows-amd64/trace_to_text.exe',
     'sha256':
-        '14334fc93ecb0498201bf2ad72ebe69938c43cb6641cf94cfd7ad646603039ed',
+        'bc951b12148338ff151f33af87e79926d18b7ea3180419a7e6c6c150b604fe25',
     'platform':
         'win32',
     'machine': ['amd64']
@@ -617,11 +617,11 @@
     'file_name':
         'trace_to_text',
     'file_size':
-        7513024,
+        7539312,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-amd64/trace_to_text',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-amd64/trace_to_text',
     'sha256':
-        'e19163f152717d9922e1926489b64c50b30ea772ebb0f21f6abb6e8cd25615b3',
+        'e40fbf44fca2673be1e6aedbc3159ce3cd4f6f33911f9dd1298471bd5b4a05ab',
     'platform':
         'linux',
     'machine': ['x86_64']
diff --git a/tools/record_android_trace b/tools/record_android_trace
index db0cffe..1134a06 100755
--- a/tools/record_android_trace
+++ b/tools/record_android_trace
@@ -226,7 +226,12 @@
     if api_level < 24:
       # adb shell does not redirect stdin. Push the config on a temporary file
       # on the device.
-      mktmp = adb('shell', 'mktemp', '--tmpdir', '/data/local/tmp', stdout=subprocess.PIPE)
+      mktmp = adb(
+          'shell',
+          'mktemp',
+          '--tmpdir',
+          '/data/local/tmp',
+          stdout=subprocess.PIPE)
       on_device_config = mktmp.communicate()[0].decode().strip().strip()
       if mktmp.returncode != 0:
         prt('Failed to create config on device', ANSI.RED)
@@ -399,7 +404,7 @@
 
 
 # BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v23.0
+# Revision: v24.1
 PERFETTO_PREBUILT_MANIFEST = [{
     'tool':
         'tracebox',
@@ -408,11 +413,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1038732,
+        1046604,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/android-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/android-arm/tracebox',
     'sha256':
-        'b37507c28e8eade93fa9f0da86d6b9779f69aadea9d7e33cd3b85866c7b755fc'
+        'a6ab413ef1aecbe93a73d9e3c9d6f00eb06b4c9886c0627a979a9acbf071a4a3'
 }, {
     'tool':
         'tracebox',
@@ -421,11 +426,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1572032,
+        1588032,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/android-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/android-arm64/tracebox',
     'sha256':
-        '83306c567da4f1b3eb910a0407abf88fcc099d22a191ceb096837381b2ebff79'
+        'a0da06d2df7106712f90a5475ab8ef994ae844f4fee7bf4f38aaacc8260062cd'
 }, {
     'tool':
         'tracebox',
@@ -434,11 +439,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1595748,
+        1615908,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/android-x86/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/android-x86/tracebox',
     'sha256':
-        '527c2ff54350fe73197b5164e6f7ed21f3d10c38a667e57781222d679dca4707'
+        'a56591c10f29c00b425ade21d1c61086584166fb00236b9744e3cb06b777fbc8'
 }, {
     'tool':
         'tracebox',
@@ -447,11 +452,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1821888,
+        1841984,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/android-x64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/android-x64/tracebox',
     'sha256':
-        '99cfbb37e7c3ae4ea70fc51811601c409fcd81589d4311a4b805a67f4760203b'
+        '21db734c29f96436685c2a65086e231e03425e35bfdb6fd25a4af729c55c6619'
 }]
 
 
diff --git a/tools/trace_processor b/tools/trace_processor
index 69e8a91..8bbab28 100755
--- a/tools/trace_processor
+++ b/tools/trace_processor
@@ -27,7 +27,7 @@
 TOOL_NAME = 'trace_processor_shell'
 
 # BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v23.0
+# Revision: v24.1
 PERFETTO_PREBUILT_MANIFEST = [{
     'tool':
         'trace_processor_shell',
@@ -36,11 +36,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6924224,
+        6940768,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/mac-amd64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/mac-amd64/trace_processor_shell',
     'sha256':
-        '08bde02f5920fa1af8dc64b857db9c000ac6940f2bb49030742d62adf04e3ff3',
+        '2d553933fdaba7080ff207094c0ed514a2f5573e525a653417b656b9c9c90e16',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -52,11 +52,11 @@
     'file_name':
         'trace_processor_shell.exe',
     'file_size':
-        6656512,
+        6681600,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/windows-amd64/trace_processor_shell.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/windows-amd64/trace_processor_shell.exe',
     'sha256':
-        '8151996f74ad7b3e6fb8c04c5acd351a74fe24e939902cc74380e9789c203309',
+        '1f4cb1a8a1726ebbada1f928c7d856a04fd57994077cead0b65c7b32225ba48b',
     'platform':
         'win32',
     'machine': ['amd64']
@@ -68,11 +68,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        7495496,
+        7520208,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-amd64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-amd64/trace_processor_shell',
     'sha256':
-        'e71bf3c16224a8ecbfb2485864dd3762f642fb052acf560d0631ef6c18a740fd',
+        '232acef777f0b0206d023bf705d1abfd64503a7762e50e813c693dd8bafd91bd',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -84,11 +84,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        4867252,
+        4887472,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-arm/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-arm/trace_processor_shell',
     'sha256':
-        'e909add14c46bcc5a2ed1736169886696867cebc17c5218cb9c143caced886f5',
+        'b03ae322ed90ee7746f5cb33c9a92a82cec34540f3fb1997b343d8cad18db158',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -100,11 +100,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6742056,
+        6765656,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-arm64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-arm64/trace_processor_shell',
     'sha256':
-        'b2cad70adbb139a8878d69999e0dfad40ca24e94f001b7bd67dcbbedcdb6c6dc',
+        '222240d00ca2ae3eac6908df275ca79e2b72ff6a0c934594d2744a2399a58dcc',
     'platform':
         'linux',
     'machine': ['aarch64']
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 1abb0a8..aa770a8 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -15,6 +15,7 @@
 import("../../gn/perfetto.gni")
 import("../../gn/perfetto_cc_proto_descriptor.gni")
 import("../../gn/perfetto_host_executable.gni")
+import("../../gn/test.gni")
 import("../../gn/wasm.gni")
 
 perfetto_host_executable("trace_to_text") {
@@ -83,6 +84,23 @@
   public_deps = [ ":pprofbuilder" ]
 }
 
+source_set("trace_to_text_lib") {
+  deps = [
+    ":gen_cc_trace_descriptor",
+    ":utils",
+    "../../gn:default_deps",
+    "../../include/perfetto/base",
+    "../../src/protozero:proto_ring_buffer",
+    "../../src/trace_processor:storage_minimal",
+    "../../src/trace_processor/util:gzip",
+  ]
+  sources = [
+    "proto_full_utils.h",
+    "trace_to_text.cc",
+    "trace_to_text.h",
+  ]
+}
+
 # The core source files that are used both by the "full" version (the host
 # executable) and by the "lite" version (the WASM module for the UI).
 source_set("common") {
@@ -132,12 +150,11 @@
   testonly = true
   deps = [
     ":common",
-    ":gen_cc_trace_descriptor",
+    ":trace_to_text_lib",
     ":utils",
     "../../gn:default_deps",
     "../../gn:protobuf_full",
     "../../protos/perfetto/trace:zero",
-    "../../src/protozero:proto_ring_buffer",
   ]
   if (enable_perfetto_zlib) {
     deps += [ "../../gn:zlib" ]
@@ -145,7 +162,6 @@
   sources = [
     "proto_full_utils.cc",
     "proto_full_utils.h",
-    "trace_to_text.cc",
   ]
 }
 
@@ -163,3 +179,16 @@
   descriptor_name = "trace.descriptor"
   descriptor_target = "../../protos/perfetto/trace:descriptor"
 }
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":trace_to_text_lib",
+    "../../gn:default_deps",
+    "../../gn:gtest_and_gmock",
+    "../../gn:protobuf_full",
+    "../../include/perfetto/base",
+    "../../include/perfetto/ext/base:base",
+  ]
+  sources = [ "trace_to_text_unittest.cc" ]
+}
diff --git a/tools/trace_to_text/lite_fallbacks.cc b/tools/trace_to_text/lite_fallbacks.cc
index 82e09ff..e208a12 100644
--- a/tools/trace_to_text/lite_fallbacks.cc
+++ b/tools/trace_to_text/lite_fallbacks.cc
@@ -24,7 +24,7 @@
 namespace perfetto {
 namespace trace_to_text {
 
-int TraceToText(std::istream*, std::ostream*) {
+bool TraceToText(std::istream*, std::ostream*) {
   PERFETTO_FATAL(
       "The 'text' command is not available in lite builds of trace_to_text");
 }
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index e4a9f1d..dfd75b6 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -187,8 +187,9 @@
     return 1;
   }
 
-  if (format == "text")
-    return TraceToText(input_stream, output_stream);
+  if (format == "text") {
+    return TraceToText(input_stream, output_stream) ? 0 : 1;
+  }
 
   if (format == "profile") {
     return perf_profile
diff --git a/tools/trace_to_text/trace_to_text.cc b/tools/trace_to_text/trace_to_text.cc
index 7507c75..2ddf713 100644
--- a/tools/trace_to_text/trace_to_text.cc
+++ b/tools/trace_to_text/trace_to_text.cc
@@ -31,9 +31,8 @@
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
-#include <zlib.h>
-#endif
+#include "src/trace_processor/forwarding_trace_parser.h"
+#include "src/trace_processor/util/gzip_utils.h"
 
 namespace perfetto {
 namespace trace_to_text {
@@ -50,6 +49,8 @@
 using google::protobuf::TextFormat;
 using google::protobuf::io::OstreamOutputStream;
 using google::protobuf::io::ZeroCopyOutputStream;
+using trace_processor::TraceType;
+using trace_processor::util::GzipDecompressor;
 
 inline void WriteToZeroCopyOutput(ZeroCopyOutputStream* output,
                                   const char* str,
@@ -83,34 +84,9 @@
                             Message* compressed_msg_scratch,
                             ZeroCopyOutputStream* output) {
 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
-  uint8_t out[4096];
-  std::vector<uint8_t> data;
-
-  z_stream stream{};
-  stream.next_in =
-      const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(packets.data()));
-  stream.avail_in = static_cast<unsigned int>(packets.length());
-
-  if (inflateInit(&stream) != Z_OK) {
-    PERFETTO_ELOG("Error when initiliazing zlib to decompress packets");
-    return;
-  }
-
-  int ret;
-  do {
-    stream.next_out = out;
-    stream.avail_out = sizeof(out);
-    ret = inflate(&stream, Z_NO_FLUSH);
-    if (ret != Z_STREAM_END && ret != Z_OK) {
-      PERFETTO_ELOG("Error when decompressing packets: %s",
-                    (stream.msg ? stream.msg : ""));
-      return;
-    }
-    data.insert(data.end(), out, out + (sizeof(out) - stream.avail_out));
-  } while (ret != Z_STREAM_END);
-  inflateEnd(&stream);
-
-  protos::pbzero::Trace::Decoder decoder(data.data(), data.size());
+  std::vector<uint8_t> whole_data = GzipDecompressor::DecompressFully(
+      reinterpret_cast<const uint8_t*>(packets.data()), packets.size());
+  protos::pbzero::Trace::Decoder decoder(whole_data.data(), whole_data.size());
   WriteToZeroCopyOutput(output, kCompressedPacketsPrefix,
                         sizeof(kCompressedPacketsPrefix) - 1);
   TextFormat::Printer printer;
@@ -146,90 +122,171 @@
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 }
 
-}  // namespace
-
-int TraceToText(std::istream* input, std::ostream* output) {
-  DescriptorPool pool;
+// TracePacket descriptor and metadata, used to print a TracePacket proto as a
+// text proto.
+struct TracePacketProtoDescInfo {
+  TracePacketProtoDescInfo();
   FileDescriptorSet desc_set;
+  DescriptorPool pool;
+  std::unique_ptr<DynamicMessageFactory> factory;
+  const Descriptor* trace_descriptor;
+  const Message* prototype;
+  const FieldDescriptor* compressed_desc;
+};
+
+TracePacketProtoDescInfo::TracePacketProtoDescInfo() {
   desc_set.ParseFromArray(kTraceDescriptor.data(), kTraceDescriptor.size());
   for (const auto& desc : desc_set.file()) {
     pool.BuildFile(desc);
   }
-
-  DynamicMessageFactory factory(&pool);
-  const Descriptor* trace_descriptor =
-      pool.FindMessageTypeByName("perfetto.protos.TracePacket");
-  const Message* prototype = factory.GetPrototype(trace_descriptor);
-  std::unique_ptr<Message> msg(prototype->New());
-
-  OstreamOutputStream zero_copy_output(output);
-  OstreamOutputStream* zero_copy_output_ptr = &zero_copy_output;
-
-  const Reflection* reflect = msg->GetReflection();
-  const FieldDescriptor* compressed_desc = trace_descriptor->FindFieldByNumber(
+  factory.reset(new DynamicMessageFactory(&pool));
+  trace_descriptor = pool.FindMessageTypeByName("perfetto.protos.TracePacket");
+  prototype = factory->GetPrototype(trace_descriptor);
+  compressed_desc = trace_descriptor->FindFieldByNumber(
       protos::pbzero::TracePacket::kCompressedPacketsFieldNumber);
+}
 
-  std::unique_ptr<Message> compressed_packets_msg(prototype->New());
-  std::string compressed_packets;
+// Online algorithm to covert trace binary to text format.
+// Usage:
+//  - Feed the trace-binary in a sequence of memblock, and it will continue to
+//    write the output in given std::ostream*.
+class OnlineTraceToText {
+ public:
+  OnlineTraceToText(std::ostream* output)
+      : zero_copy_out_stream_(output),
+        msg_(pb_desc_info_.prototype->New()),
+        compressed_packets_msg_(pb_desc_info_.prototype->New()),
+        reflect_(msg_->GetReflection()) {
+    printer_.SetInitialIndentLevel(1);
+  }
+  OnlineTraceToText(const OnlineTraceToText&) = delete;
+  OnlineTraceToText& operator=(const OnlineTraceToText&) = delete;
+  void Feed(const uint8_t* data, size_t len);
+  bool ok() const { return ok_; }
 
-  TextFormat::Printer printer;
-  printer.SetInitialIndentLevel(1);
+ private:
+  bool ok_ = true;
+  OstreamOutputStream zero_copy_out_stream_;
+  protozero::ProtoRingBuffer ring_buffer_;
+  TextFormat::Printer printer_;
+  TracePacketProtoDescInfo pb_desc_info_;
+  std::unique_ptr<Message> msg_;
+  std::unique_ptr<Message> compressed_packets_msg_;
+  const Reflection* reflect_;
+  std::string compressed_packets_;
+  size_t bytes_processed_ = 0;
+  size_t packet_ = 0;
+};
 
-  static constexpr size_t kMaxMsgSize = protozero::ProtoRingBuffer::kMaxMsgSize;
-  std::unique_ptr<char[]> data(new char[kMaxMsgSize]);
-  protozero::ProtoRingBuffer ring_buffer;
-
-  uint32_t packet = 0;
-  size_t bytes_processed = 0;
-  while (!input->eof()) {
-    input->read(data.get(), kMaxMsgSize);
-    if (input->bad() || (input->fail() && !input->eof())) {
-      PERFETTO_ELOG("Failed while reading trace");
-      return 1;
+void OnlineTraceToText::Feed(const uint8_t* data, size_t len) {
+  ring_buffer_.Append(data, static_cast<size_t>(len));
+  while (true) {
+    auto token = ring_buffer_.ReadMessage();
+    if (token.fatal_framing_error) {
+      PERFETTO_ELOG("Failed to tokenize trace packet");
+      ok_ = false;
+      return;
     }
-    ring_buffer.Append(data.get(), static_cast<size_t>(input->gcount()));
+    if (!token.valid()) {
+      // no need to set `ok_ = false` here because this just means
+      // we've run out of packets in the ring buffer.
+      break;
+    }
 
-    for (;;) {
-      auto token = ring_buffer.ReadMessage();
-      if (token.fatal_framing_error) {
-        PERFETTO_ELOG("Failed to tokenize trace packet");
-        return 1;
-      }
-      if (!token.valid())
-        break;
-      bytes_processed += token.len;
-
-      if (token.field_id != protos::pbzero::Trace::kPacketFieldNumber) {
-        PERFETTO_ELOG("Skipping invalid field");
-        continue;
-      }
-
-      if ((packet++ & 0x3f) == 0) {
-        fprintf(stderr, "Processing trace: %8zu KB%c", bytes_processed / 1024,
-                kProgressChar);
-        fflush(stderr);
-      }
-
-      if (!msg->ParseFromArray(token.start, static_cast<int>(token.len))) {
-        PERFETTO_ELOG("Skipping invalid packet");
-        continue;
-      }
-
-      if (reflect->HasField(*msg, compressed_desc)) {
-        compressed_packets = reflect->GetStringReference(*msg, compressed_desc,
-                                                         &compressed_packets);
-        PrintCompressedPackets(compressed_packets, compressed_packets_msg.get(),
-                               zero_copy_output_ptr);
-      } else {
-        WriteToZeroCopyOutput(zero_copy_output_ptr, kPacketPrefix,
-                              sizeof(kPacketPrefix) - 1);
-        printer.Print(*msg, zero_copy_output_ptr);
-        WriteToZeroCopyOutput(zero_copy_output_ptr, kPacketSuffix,
-                              sizeof(kPacketSuffix) - 1);
-      }
+    if (token.field_id != protos::pbzero::Trace::kPacketFieldNumber) {
+      PERFETTO_ELOG("Skipping invalid field");
+      continue;
+    }
+    if (!msg_->ParseFromArray(token.start, static_cast<int>(token.len))) {
+      PERFETTO_ELOG("Skipping invalid packet");
+      continue;
+    }
+    bytes_processed_ += token.len;
+    if ((packet_++ & 0x3f) == 0) {
+      fprintf(stderr, "Processing trace: %8zu KB%c", bytes_processed_ / 1024,
+              kProgressChar);
+      fflush(stderr);
+    }
+    if (reflect_->HasField(*msg_, pb_desc_info_.compressed_desc)) {
+      // TODO(mohitms): GetStringReference ignores third argument. Why are we
+      // passing ?
+      compressed_packets_ = reflect_->GetStringReference(
+          *msg_, pb_desc_info_.compressed_desc, &compressed_packets_);
+      PrintCompressedPackets(compressed_packets_, compressed_packets_msg_.get(),
+                             &zero_copy_out_stream_);
+    } else {
+      WriteToZeroCopyOutput(&zero_copy_out_stream_, kPacketPrefix,
+                            sizeof(kPacketPrefix) - 1);
+      printer_.Print(*msg_, &zero_copy_out_stream_);
+      WriteToZeroCopyOutput(&zero_copy_out_stream_, kPacketSuffix,
+                            sizeof(kPacketSuffix) - 1);
     }
   }
-  return 0;
+}
+
+class InputReader {
+ public:
+  InputReader(std::istream* input) : input_(input) {}
+  // Request the input-stream to read next |len_limit| bytes and load
+  // it in |data|. It also updates the |len| with actual number of bytes loaded
+  // in |data|. This can be less than requested |len_limit| if we have reached
+  // at the end of the file.
+  bool Read(uint8_t* data, uint32_t* len, uint32_t len_limit) {
+    if (input_->eof())
+      return false;
+    input_->read(reinterpret_cast<char*>(data), std::streamsize(len_limit));
+    if (input_->bad() || (input_->fail() && !input_->eof())) {
+      PERFETTO_ELOG("Failed while reading trace");
+      ok_ = false;
+      return false;
+    }
+    *len = uint32_t(input_->gcount());
+    return true;
+  }
+  bool ok() const { return ok_; }
+
+ private:
+  std::istream* input_;
+  bool ok_ = true;
+};
+
+}  // namespace
+
+bool TraceToText(std::istream* input, std::ostream* output) {
+  constexpr size_t kMaxMsgSize = protozero::ProtoRingBuffer::kMaxMsgSize;
+  std::unique_ptr<uint8_t[]> buffer(new uint8_t[kMaxMsgSize]);
+  uint32_t buffer_len = 0;
+
+  InputReader input_reader(input);
+  OnlineTraceToText online_trace_to_text(output);
+
+  input_reader.Read(buffer.get(), &buffer_len, kMaxMsgSize);
+  TraceType type = trace_processor::GuessTraceType(buffer.get(), buffer_len);
+
+  if (type == TraceType::kGzipTraceType) {
+    GzipDecompressor decompressor;
+    auto consumer = [&](const uint8_t* data, size_t len) {
+      online_trace_to_text.Feed(data, len);
+    };
+    using ResultCode = GzipDecompressor::ResultCode;
+    do {
+      ResultCode code =
+          decompressor.FeedAndExtract(buffer.get(), buffer_len, consumer);
+      if (code == ResultCode::kError || !online_trace_to_text.ok())
+        return false;
+    } while (input_reader.Read(buffer.get(), &buffer_len, kMaxMsgSize));
+    return input_reader.ok();
+  } else if (type == TraceType::kProtoTraceType) {
+    do {
+      online_trace_to_text.Feed(buffer.get(), buffer_len);
+      if (!online_trace_to_text.ok())
+        return false;
+    } while (input_reader.Read(buffer.get(), &buffer_len, kMaxMsgSize));
+    return input_reader.ok();
+  } else {
+    PERFETTO_ELOG("Unrecognised file.");
+    return false;
+  }
 }
 
 }  // namespace trace_to_text
diff --git a/tools/trace_to_text/trace_to_text.h b/tools/trace_to_text/trace_to_text.h
index 2be24ad..d0f35e0 100644
--- a/tools/trace_to_text/trace_to_text.h
+++ b/tools/trace_to_text/trace_to_text.h
@@ -22,7 +22,8 @@
 namespace perfetto {
 namespace trace_to_text {
 
-int TraceToText(std::istream* input, std::ostream* output);
+// Returns true in case of success.
+bool TraceToText(std::istream* input, std::ostream* output);
 
 }  // namespace trace_to_text
 }  // namespace perfetto
diff --git a/tools/trace_to_text/trace_to_text_unittest.cc b/tools/trace_to_text/trace_to_text_unittest.cc
new file mode 100644
index 0000000..0066c0c
--- /dev/null
+++ b/tools/trace_to_text/trace_to_text_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 "tools/trace_to_text/trace_to_text.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/hash.h"
+#include "test/gtest_and_gmock.h"
+
+#include <fstream>
+
+using std::string;
+
+namespace perfetto {
+namespace trace_to_text {
+
+// Given a file, compute the checksum/hash of file.
+// Learn more @ base::Hash.
+// Precondition: File should exist and be accessible.
+static uint64_t FileHash(const string& filename) {
+  base::Hash hash;
+  std::ifstream input_f(filename, std::ios::binary);
+  PERFETTO_DCHECK(input_f.good());
+  char buffer[4096];
+  while (!input_f.eof()) {
+    input_f.read(buffer, sizeof(buffer));
+    if (input_f.gcount() > 0) {
+      hash.Update(buffer, size_t(input_f.gcount()));
+    }
+  }
+  return hash.digest();
+}
+
+TEST(TraceToText, DISABLED_Basic) {
+  auto tmp_file = "/tmp/trace_" + std::to_string(rand()) + ".txt";
+  auto input_file_names = {"test/data/example_android_trace_30s.pb.gz",
+                           "test/data/example_android_trace_30s.pb"};
+  PERFETTO_LOG("tmp_file = %s.", tmp_file.c_str());
+  for (auto filename : input_file_names) {
+    {
+      std::ifstream input_f(filename, std::ios::binary);
+      std::ofstream output_f(tmp_file, std::ios::out | std::ios::binary);
+      EXPECT_TRUE(TraceToText(&input_f, &output_f));
+      PERFETTO_LOG("Processed %s", filename);
+    }
+    EXPECT_EQ(0xCD794377594BC7DCull, FileHash(tmp_file));
+    remove(tmp_file.c_str());
+  }
+}
+
+}  // namespace trace_to_text
+}  // namespace perfetto
diff --git a/tools/tracebox b/tools/tracebox
index ece2014..e7b1253 100755
--- a/tools/tracebox
+++ b/tools/tracebox
@@ -27,7 +27,7 @@
 TOOL_NAME = 'tracebox'
 
 # BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v23.0
+# Revision: v24.1
 PERFETTO_PREBUILT_MANIFEST = [{
     'tool':
         'tracebox',
@@ -36,11 +36,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1316192,
+        1332696,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/mac-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/mac-amd64/tracebox',
     'sha256':
-        '54101d81876ceed8c2d24701835fd11cd0edc4d43af655ed0420013809138951',
+        'be14a9040e8d0143d5b0838560cf20afb0be2d1971f6df4c79d7b4c46aa5852e',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -52,11 +52,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1739352,
+        1756264,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-amd64/tracebox',
     'sha256':
-        'b42cfaadf4dd35c5c540775a0d909dcbfa5338d1c022767db3a4b1324c19e58b',
+        '2fde750c982826f6a7c3e49b2d6ee7ec72c956170b4bba07e2989f2b81f26269',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -68,11 +68,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1739352,
+        1756264,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-amd64/tracebox',
     'sha256':
-        'b42cfaadf4dd35c5c540775a0d909dcbfa5338d1c022767db3a4b1324c19e58b',
+        '2fde750c982826f6a7c3e49b2d6ee7ec72c956170b4bba07e2989f2b81f26269',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -84,11 +84,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1002132,
+        1012940,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-arm/tracebox',
     'sha256':
-        '65351d250c3c52f8a69afa78069d9a87931c45cf3bf415c207e93de6eb75dbbe',
+        'a6f964b1b155c309fda0fb0b765f9fbf3d030d8c4bd6f9f03dad173ee689668e',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -100,11 +100,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1619248,
+        1635160,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-arm64/tracebox',
     'sha256':
-        '2cb0be71da6b46167a8dd4a9d8af5229dba89b8e508554c16cde20b81f175d3d',
+        'ed998b18606a6cffb490336990220e6290faa4544e52a5c77273f69507f80e89',
     'platform':
         'linux',
     'machine': ['aarch64']
diff --git a/tools/traceconv b/tools/traceconv
index 4754a87..663ad5a 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -27,7 +27,7 @@
 TOOL_NAME = 'trace_to_text'
 
 # BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v23.0
+# Revision: v24.1
 PERFETTO_PREBUILT_MANIFEST = [{
     'tool':
         'trace_to_text',
@@ -36,11 +36,11 @@
     'file_name':
         'trace_to_text',
     'file_size':
-        6939864,
+        6972736,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/mac-amd64/trace_to_text',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/mac-amd64/trace_to_text',
     'sha256':
-        '1626880d1fbec8efc9702583b6c22d617079a08226df414d65888a6b3c7572c8',
+        'cd963e3f63b23977302fcbcb19a9fe64e98aeb2f419ce4212cfafa4e52b8faec',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -52,11 +52,11 @@
     'file_name':
         'trace_to_text',
     'file_size':
-        7513024,
+        7539312,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/linux-amd64/trace_to_text',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/linux-amd64/trace_to_text',
     'sha256':
-        'e19163f152717d9922e1926489b64c50b30ea772ebb0f21f6abb6e8cd25615b3',
+        'e40fbf44fca2673be1e6aedbc3159ce3cd4f6f33911f9dd1298471bd5b4a05ab',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -68,11 +68,11 @@
     'file_name':
         'trace_to_text.exe',
     'file_size':
-        6658560,
+        6685696,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v23.0/windows-amd64/trace_to_text.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v24.1/windows-amd64/trace_to_text.exe',
     'sha256':
-        '14334fc93ecb0498201bf2ad72ebe69938c43cb6641cf94cfd7ad646603039ed',
+        'bc951b12148338ff151f33af87e79926d18b7ea3180419a7e6c6c150b604fe25',
     'platform':
         'win32',
     'machine': ['amd64']
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index c98a6a1..4aeb978 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -606,6 +606,9 @@
     .track-button {
       color: white;
     }
+    span.chip {
+      color: #121212;
+    }
   }
   .shell {
     padding: 4px 4px;
@@ -720,6 +723,15 @@
   &.query-error {
     color: red;
   }
+
+  .total-values {
+    text-align: right;
+    padding-right: 10px;
+  }
+
+  .empty-result {
+    padding: 10px;
+  }
 }
 
 .pivot-table-editor-container {
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss
index a191ffa..ab9a458 100644
--- a/ui/src/assets/details.scss
+++ b/ui/src/assets/details.scss
@@ -21,6 +21,9 @@
     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
+    // bottom panels.
+    user-select: none;
     height: 28px;
     min-height: 28px;
     display: grid;
diff --git a/ui/src/base/string_utils.ts b/ui/src/base/string_utils.ts
index 019de9e..27e2614 100644
--- a/ui/src/base/string_utils.ts
+++ b/ui/src/base/string_utils.ts
@@ -96,3 +96,14 @@
   }
   return buf;
 }
+
+// A function used to interpolate strings into SQL query. The only replacement
+// is done is that single quote replaced with two single quotes, according to
+// SQLite documentation:
+// https://www.sqlite.org/lang_expr.html#literal_values_constants_
+//
+// 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('\'', '\'\'')}'`;
+}
diff --git a/ui/src/base/string_utils_unittest.ts b/ui/src/base/string_utils_unittest.ts
index c8dce8e..4265a62 100644
--- a/ui/src/base/string_utils_unittest.ts
+++ b/ui/src/base/string_utils_unittest.ts
@@ -17,6 +17,7 @@
   base64Encode,
   binaryDecode,
   binaryEncode,
+  sqliteString,
   utf8Decode,
   utf8Encode,
 } from './string_utils';
@@ -73,3 +74,8 @@
   expect(binaryDecode(encodedStr)).toEqual(buf);
   expect(binaryDecode(encodedThroughJson)).toEqual(buf);
 });
+
+test('string_utils.sqliteString', () => {
+  expect(sqliteString('that\'s it')).toEqual('\'that\'\'s it\'');
+  expect(sqliteString('no quotes')).toEqual('\'no quotes\'');
+});
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 768493a..76d2dd3 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -917,8 +917,8 @@
     }
   },
 
-  togglePivotTableRedux(state: StateDraft, args: {enabled: boolean}) {
-    state.pivotTableRedux.enabled = args.enabled;
+  togglePivotTableRedux(state: StateDraft, args: {selectionArea: Area|null}) {
+    state.pivotTableRedux.selectionArea = args.selectionArea;
   },
 
   addNewPivotTable(state: StateDraft, args: {
@@ -946,7 +946,9 @@
 
   resetPivotTableRequest(state: StateDraft, args: {pivotTableId: string}):
       void {
-        state.pivotTable[args.pivotTableId].requestedAction = undefined;
+        if (state.pivotTable[args.pivotTableId] !== undefined) {
+          state.pivotTable[args.pivotTableId].requestedAction = undefined;
+        }
       },
 
   setPivotTableRequest(
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index 2b9396e..cc57df5 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -103,6 +103,6 @@
     fetchChromeCategories: false,
     chromeCategories: undefined,
     pivotTableRedux:
-        {enabled: false, query: null, queryId: 0, queryResult: null},
+        {selectionArea: null, query: null, queryId: 0, queryResult: null},
   };
 }
\ No newline at end of file
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index db5e9c6..19f6a2e 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -335,6 +335,7 @@
 // correctly. Generated together with the text of query and passed without the
 // change to the query response.
 export interface PivotTableReduxQueryMetadata {
+  tableName: string;
   pivotColumns: string[];
   aggregationColumns: string[];
 }
@@ -355,8 +356,8 @@
 }
 
 export interface PivotTableReduxState {
-  // Whether the panel should be visible
-  enabled: boolean;
+  // Currently selected area, if null, pivot table is not going to be visible.
+  selectionArea: Area|null;
   // Increasing identifier of the query request, used to avoid performing the
   // same query more than once.
   queryId: number;
diff --git a/ui/src/controller/aggregation/slice_aggregation_controller.ts b/ui/src/controller/aggregation/slice_aggregation_controller.ts
index 8646c45..313e0ce 100644
--- a/ui/src/controller/aggregation/slice_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/slice_aggregation_controller.ts
@@ -57,26 +57,31 @@
       {pivotTableId: SLICE_AGGREGATION_PIVOT_TABLE_ID, action: 'QUERY'}));
 }
 
+export function getSelectedTrackIds(area: Area): number[] {
+  const selectedTrackIds = [];
+  for (const trackId of area.tracks) {
+    const track = globals.state.tracks[trackId];
+    // Track will be undefined for track groups.
+    if (track !== undefined) {
+      if (track.kind === SLICE_TRACK_KIND) {
+        selectedTrackIds.push((track.config as SliceConfig).trackId);
+      }
+      if (track.kind === ASYNC_SLICE_TRACK_KIND) {
+        const config = track.config as AsyncSliceConfig;
+        for (const id of config.trackIds) {
+          selectedTrackIds.push(id);
+        }
+      }
+    }
+  }
+  return selectedTrackIds;
+}
+
 export class SliceAggregationController extends AggregationController {
   async createAggregateView(engine: Engine, area: Area) {
     await engine.query(`drop view if exists ${this.kind};`);
 
-    const selectedTrackIds = [];
-    for (const trackId of area.tracks) {
-      const track = globals.state.tracks[trackId];
-      // Track will be undefined for track groups.
-      if (track !== undefined) {
-        if (track.kind === SLICE_TRACK_KIND) {
-          selectedTrackIds.push((track.config as SliceConfig).trackId);
-        }
-        if (track.kind === ASYNC_SLICE_TRACK_KIND) {
-          const config = track.config as AsyncSliceConfig;
-          for (const id of config.trackIds) {
-            selectedTrackIds.push(id);
-          }
-        }
-      }
-    }
+    const selectedTrackIds = getSelectedTrackIds(area);
 
     if (selectedTrackIds.length === 0) return false;
 
diff --git a/ui/src/controller/pivot_table_redux_controller.ts b/ui/src/controller/pivot_table_redux_controller.ts
index 59a4c1b..5e37e52 100644
--- a/ui/src/controller/pivot_table_redux_controller.ts
+++ b/ui/src/controller/pivot_table_redux_controller.ts
@@ -2,6 +2,10 @@
 import {Engine} from '../common/engine';
 import {featureFlags} from '../common/feature_flags';
 import {ColumnType} from '../common/query_result';
+import {
+  PivotTableReduxQueryMetadata,
+  PivotTableReduxResult
+} from '../common/state';
 import {aggregationIndex} from '../frontend/pivot_table_redux_query_generator';
 
 import {Controller} from './controller';
@@ -98,6 +102,20 @@
   }
 }
 
+function createEmptyQueryResult(metadata: PivotTableReduxQueryMetadata):
+    PivotTableReduxResult {
+  return {
+    tree: {
+      aggregates: [],
+      isCollapsed: false,
+      children: new Map(),
+      rows: [],
+    },
+    metadata
+  };
+}
+
+
 // Controller responsible for showing the panel with pivot table, as well as
 // executing its queries and post-processing query results.
 export class PivotTableReduxController extends Controller<{}> {
@@ -115,9 +133,6 @@
       return;
     }
 
-    const selection = globals.state.currentSelection;
-    const hasSelection = selection !== null;
-
     const pivotTableState = globals.state.pivotTableRedux;
     if (pivotTableState.queryId > this.lastStartedQueryId &&
         pivotTableState.query !== null) {
@@ -143,6 +158,21 @@
           it.next();
           return row;
         }
+
+        if (!it.valid()) {
+          // Iterator is invalid after creation; means that there are no rows
+          // satisfying filtering criteria. Return an empty tree.
+          globals.dispatch(Actions.setPivotStateReduxState({
+            pivotTableState: {
+              queryId: this.lastStartedQueryId,
+              query: null,
+              queryResult: createEmptyQueryResult(query.metadata),
+              selectionArea: pivotTableState.selectionArea
+            }
+          }));
+          return;
+        }
+
         const treeBuilder = new TreeBuilder(
             query.metadata.pivotColumns.length,
             query.metadata.aggregationColumns.length,
@@ -159,12 +189,19 @@
               tree: treeBuilder.build(),
               metadata: query.metadata,
             },
-            enabled: true
+            selectionArea: pivotTableState.selectionArea
           }
         }));
       });
     }
 
-    globals.dispatch(Actions.togglePivotTableRedux({enabled: hasSelection}));
+    const selection = globals.state.currentSelection;
+    if (selection !== null && selection.kind === 'AREA') {
+      const enabledArea = globals.state.areas[selection.areaId];
+      globals.dispatch(
+          Actions.togglePivotTableRedux({selectionArea: enabledArea}));
+    } else {
+      globals.dispatch(Actions.togglePivotTableRedux({selectionArea: null}));
+    }
   }
 }
\ No newline at end of file
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 0088e33..9c8969a 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -327,11 +327,13 @@
       });
     }
 
-    if (globals.state.pivotTableRedux.enabled) {
+    if (globals.state.pivotTableRedux.selectionArea !== null) {
       detailsPanels.push({
         key: 'pivot_table_redux',
         name: 'Pivot Table',
-        vnode: m(PivotTableRedux)
+        vnode:
+            m(PivotTableRedux,
+              {selectionArea: globals.state.pivotTableRedux.selectionArea})
       });
     }
 
diff --git a/ui/src/frontend/pivot_table_redux.ts b/ui/src/frontend/pivot_table_redux.ts
index c2c129a..ea2b7d3 100644
--- a/ui/src/frontend/pivot_table_redux.ts
+++ b/ui/src/frontend/pivot_table_redux.ts
@@ -1,14 +1,20 @@
 import * as m from 'mithril';
 
+import {sqliteString} from '../base/string_utils';
 import {Actions} from '../common/actions';
 import {ColumnType} from '../common/query_result';
-import {PivotTableReduxQuery, PivotTableReduxResult} from '../common/state';
+import {
+  Area,
+  PivotTableReduxQuery,
+  PivotTableReduxResult
+} from '../common/state';
 import {PivotTree} from '../controller/pivot_table_redux_controller';
 
 import {globals} from './globals';
 import {Panel} from './panel';
 import {
   aggregationIndex,
+  areaFilter,
   ColumnSet,
   generateQuery,
   QueryGeneratorError,
@@ -48,26 +54,50 @@
   }
 }
 
-export class PivotTableRedux extends Panel {
+interface PivotTableReduxAttrs {
+  selectionArea: Area;
+}
+
+interface DrillFilter {
+  column: string;
+  value: ColumnType;
+}
+
+// Convert DrillFilter to SQL condition to be used in WHERE clause.
+function renderDrillFilter(filter: DrillFilter): string {
+  if (filter.value === null) {
+    return `${filter.column} IS NULL`;
+  } else if (typeof filter.value === 'number') {
+    return `${filter.column} = ${filter.value}`;
+  }
+  return `${filter.column} = ${sqliteString(filter.value)}`;
+}
+
+export class PivotTableRedux extends Panel<PivotTableReduxAttrs> {
   selectedPivotsMap = new ColumnSet();
   selectedAggregations = new ColumnSet();
+  constrainToArea = true;
   editMode = true;
 
   renderCanvas(): void {}
 
-  generateQuery(): PivotTableReduxQuery {
-    return generateQuery(this.selectedPivotsMap, this.selectedAggregations);
+  generateQuery(attrs: PivotTableReduxAttrs): PivotTableReduxQuery {
+    return generateQuery(
+        this.selectedPivotsMap,
+        this.selectedAggregations,
+        attrs.selectionArea,
+        this.constrainToArea);
   }
 
-  runQuery() {
+  runQuery(attrs: PivotTableReduxAttrs) {
     try {
-      const query = this.generateQuery();
+      const query = this.generateQuery(attrs);
       const lastPivotTableState = globals.state.pivotTableRedux;
       globals.dispatch(Actions.setPivotStateReduxState({
         pivotTableState: {
           query,
           queryId: lastPivotTableState.queryId + 1,
-          enabled: true,
+          selectionArea: lastPivotTableState.selectionArea,
           queryResult: null
         }
       }));
@@ -91,7 +121,7 @@
                     col))));
   }
 
-  renderResultsView() {
+  renderResultsView(attrs: PivotTableReduxAttrs) {
     return m(
         '.pivot-table-redux',
         m('button.mode-button',
@@ -102,11 +132,39 @@
             }
           },
           'Edit'),
-        this.renderResultsTable());
+        this.renderResultsTable(attrs));
+  }
+
+  renderDrillDownCell(
+      area: Area, result: PivotTableReduxResult, filters: DrillFilter[]) {
+    return m(
+        'td',
+        m('button',
+          {
+            title: 'All corresponding slices',
+            onclick: () => {
+              const queryFilters = filters.map(renderDrillFilter);
+              if (this.constrainToArea) {
+                queryFilters.push(areaFilter(area));
+              }
+              const query = `
+                select * from ${result.metadata.tableName}
+                where ${queryFilters.join(' and \n')}
+              `;
+              // TODO(ddrone): the UI of running query as if it was a canned or
+              // custom query is a temporary one, replace with a proper UI.
+              globals.dispatch(Actions.executeQuery({
+                engineId: '0',
+                queryId: 'command',
+                query,
+              }));
+            }
+          },
+          m('i.material-icons', 'arrow_right')));
   }
 
   renderSectionRow(
-      path: PathItem[], tree: PivotTree,
+      area: Area, path: PathItem[], tree: PivotTree,
       result: PivotTableReduxResult): m.Vnode {
     const renderedCells = [];
     for (let j = 0; j + 1 < path.length; j++) {
@@ -133,25 +191,34 @@
       renderedCells.push(m('td', `${value}`));
     }
 
+    const drillFilters: DrillFilter[] = [];
+    for (let i = 0; i < path.length; i++) {
+      drillFilters.push({
+        value: `${path[i].nextKey}`,
+        column: result.metadata.pivotColumns[i]
+      });
+    }
+
+    renderedCells.push(this.renderDrillDownCell(area, result, drillFilters));
     return m('tr', renderedCells);
   }
 
   renderTree(
-      path: PathItem[], tree: PivotTree, result: PivotTableReduxResult,
-      sink: m.Vnode[]) {
+      area: Area, path: PathItem[], tree: PivotTree,
+      result: PivotTableReduxResult, sink: m.Vnode[]) {
     if (tree.isCollapsed) {
-      sink.push(this.renderSectionRow(path, tree, result));
+      sink.push(this.renderSectionRow(area, path, tree, result));
       return;
     }
     if (tree.children.size > 0) {
       // Avoid rendering the intermediate results row for the root of tree
       // and in case there's only one child subtree.
       if (!tree.isCollapsed && path.length > 0 && tree.children.size !== 1) {
-        sink.push(this.renderSectionRow(path, tree, result));
+        sink.push(this.renderSectionRow(area, path, tree, result));
       }
       for (const [key, childTree] of tree.children.entries()) {
         path.push({tree: childTree, nextKey: key});
-        this.renderTree(path, childTree, result, sink);
+        this.renderTree(area, path, childTree, result, sink);
         path.pop();
       }
       return;
@@ -159,11 +226,12 @@
 
     // Avoid rendering the intermediate results row if it has only one leaf
     // row.
-    if (!tree.isCollapsed && tree.rows.length > 1) {
-      sink.push(this.renderSectionRow(path, tree, result));
+    if (!tree.isCollapsed && path.length > 0 && tree.rows.length > 1) {
+      sink.push(this.renderSectionRow(area, path, tree, result));
     }
     for (const row of tree.rows) {
       const renderedCells = [];
+      const drillFilters: DrillFilter[] = [];
       const treeDepth = result.metadata.pivotColumns.length;
       for (let j = 0; j < treeDepth; j++) {
         if (j < path.length) {
@@ -171,62 +239,105 @@
         } else {
           renderedCells.push(m(`td`, `${row[j]}`));
         }
+        drillFilters.push(
+            {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)];
         renderedCells.push(m('td', `${value}`));
       }
 
+      renderedCells.push(this.renderDrillDownCell(area, result, drillFilters));
       sink.push(m('tr', renderedCells));
     }
   }
 
-  renderResultsTable() {
+  renderTotalsRow(queryResult: PivotTableReduxResult) {
+    const overallValuesRow =
+        [m('td.total-values',
+           {'colspan': queryResult.metadata.pivotColumns.length},
+           m('strong', 'Total values:'))];
+    for (const aggValue of queryResult.tree.aggregates) {
+      overallValuesRow.push(m('td', `${aggValue}`));
+    }
+    overallValuesRow.push(m('td'));
+    return m('tr', overallValuesRow);
+  }
+
+  renderResultsTable(attrs: PivotTableReduxAttrs) {
     const state = globals.state.pivotTableRedux;
     if (state.query !== null || state.queryResult === null) {
       return m('div', 'Loading...');
     }
 
     const renderedRows: m.Vnode[] = [];
+    const tree = state.queryResult.tree;
+
+    if (tree.children.size === 0 && tree.rows.length === 0) {
+      // Empty result, render a special message
+      return m('.empty-result', 'No slices in the current selection.');
+    }
+
     this.renderTree(
-        [], state.queryResult.tree, state.queryResult, renderedRows);
+        attrs.selectionArea, [], tree, state.queryResult, renderedRows);
 
     const allColumns = state.queryResult.metadata.pivotColumns.concat(
         state.queryResult.metadata.aggregationColumns);
     return m(
         'table.query-table.pivot-table',
-        m('thead', m('tr', allColumns.map(column => m('td', column)))),
-        m('tbody', renderedRows));
+        m('thead', m('tr', allColumns.map(column => m('td', column)), m('td'))),
+        m('tbody', this.renderTotalsRow(state.queryResult), renderedRows));
   }
 
-  renderQuery(): m.Vnode {
+  renderQuery(attrs: PivotTableReduxAttrs): m.Vnode {
+    // Prepare a button to switch to results mode.
+    let innerElement =
+        m('button.mode-button',
+          {
+            onclick: () => {
+              this.editMode = false;
+              this.runQuery(attrs);
+              globals.rafScheduler.scheduleFullRedraw();
+            }
+          },
+          'Execute');
     try {
-      return m(
-          'div',
-          m('pre', this.generateQuery()),
-          m('button.mode-button',
-            {
-              onclick: () => {
-                this.editMode = false;
-                this.runQuery();
-                globals.rafScheduler.scheduleFullRedraw();
-              }
-            },
-            'Execute'));
+      this.generateQuery(attrs);
     } catch (e) {
       if (e instanceof QueryGeneratorError) {
-        return m('div.query-error', e.message);
+        // If query generation fails, show an error message instead of a button.
+        innerElement = m('div.query-error', e.message);
       } else {
         throw e;
       }
     }
+
+    return m(
+        'div',
+        m('div',
+          m('input', {
+            type: 'checkbox',
+            id: 'constrain-to-selection',
+            checked: this.constrainToArea,
+            onclick: (e: InputEvent) => {
+              const checkbox = e.target as HTMLInputElement;
+              this.constrainToArea = checkbox.checked;
+            }
+          }),
+          m('label',
+            {
+              'for': 'constrain-to-selection',
+            },
+            'Constrain to current time range')),
+        innerElement);
   }
 
-  view() {
-    return this.editMode ? this.renderEditView() : this.renderResultsView();
+  view({attrs}: m.Vnode<PivotTableReduxAttrs>) {
+    return this.editMode ? this.renderEditView(attrs) :
+                           this.renderResultsView(attrs);
   }
 
-  renderEditView() {
+  renderEditView(attrs: PivotTableReduxAttrs) {
     return m(
         '.pivot-table-redux.edit',
         m('div',
@@ -254,6 +365,6 @@
                         setKey: ['thread_slice', t],
                       }),
                       `thread_slice.${t}`)))),
-        this.renderQuery());
+        this.renderQuery(attrs));
   }
 }
\ No newline at end of file
diff --git a/ui/src/frontend/pivot_table_redux_query_generator.ts b/ui/src/frontend/pivot_table_redux_query_generator.ts
index 9bd62fc..8e9586b 100644
--- a/ui/src/frontend/pivot_table_redux_query_generator.ts
+++ b/ui/src/frontend/pivot_table_redux_query_generator.ts
@@ -1,4 +1,8 @@
-import {PivotTableReduxQuery} from '../common/state';
+import {Area, PivotTableReduxQuery} from '../common/state';
+import {toNs} from '../common/time';
+import {
+  getSelectedTrackIds
+} from '../controller/aggregation/slice_aggregation_controller';
 
 export interface Table {
   name: string;
@@ -86,11 +90,21 @@
   return `agg_${aggregationIndex}_level_${rolloverLevel}`;
 }
 
+export function areaFilter(area: Area): string {
+  return `
+    ts > ${toNs(area.startSec)}
+    and ts < ${toNs(area.endSec)}
+    and track_id in (${getSelectedTrackIds(area).join(', ')})
+  `;
+}
+
 function generateInnerQuery(
     pivots: string[],
     aggregations: string[],
     table: string,
-    includeTrack: boolean): string {
+    includeTrack: boolean,
+    area: Area,
+    constrainToArea: boolean): string {
   const pivotColumns = pivots.concat(includeTrack ? ['track_id'] : []);
   const aggregationColumns: string[] = [];
 
@@ -99,10 +113,13 @@
     aggregationColumns.push(`SUM(${agg}) as ${aggregationAlias(i, 0)}`);
   }
 
+  // The condition is inverted because flipped order of literals makes JS
+  // formatter insert huge amounts of whitespace for no good reason.
   return `
     select
       ${pivotColumns.concat(aggregationColumns).join(',\n')}
     from ${table}
+    ${(constrainToArea ? `where ${areaFilter(area)}` : '')}
     group by ${pivotColumns.join(', ')}
   `;
 }
@@ -138,7 +155,9 @@
 
 export function generateQuery(
     selectedPivots: ColumnSet,
-    selectedAggregations: ColumnSet): PivotTableReduxQuery {
+    selectedAggregations: ColumnSet,
+    area: Area,
+    constrainToArea: boolean): PivotTableReduxQuery {
   const sliceTableAggregations =
       computeSliceTableAggregations(selectedAggregations);
   const slicePivots: string[] = [];
@@ -203,7 +222,9 @@
           slicePivots,
           sliceTableAggregations.flatAggregations,
           sliceTableAggregations.tableName,
-          nonSlicePivots.length > 0)}
+          nonSlicePivots.length > 0,
+          area,
+          constrainToArea)}
     ) preaggregated
     ${nonSlicePivots.length > 0 ? joins : ''}
     group by ${nonSlicePivots.concat(prefixedSlicePivots).join(', ')}
@@ -212,6 +233,7 @@
   return {
     text,
     metadata: {
+      tableName: sliceTableAggregations.tableName,
       pivotColumns: nonSlicePivots.concat(slicePivots.map(
           column => `${sliceTableAggregations.tableName}.${column}`)),
       aggregationColumns: sliceTableAggregations.flatAggregations.map(