Merge "trace_processor: show cpu_frequency_limits ftrace event on Perfetto UI"
diff --git a/Android.bp b/Android.bp
index f3ebaa9..ff943ad 100644
--- a/Android.bp
+++ b/Android.bp
@@ -223,10 +223,8 @@
     srcs: [
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
-        ":perfetto_include_perfetto_profiling_normalize",
         ":perfetto_src_base_base",
         ":perfetto_src_base_unix_socket",
-        ":perfetto_src_profiling_common_proc_utils",
         ":perfetto_src_profiling_memory_client",
         ":perfetto_src_profiling_memory_client_api",
         ":perfetto_src_profiling_memory_ring_buffer",
@@ -1931,6 +1929,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -1953,6 +1952,7 @@
         "external/perfetto/protos/perfetto/common/commit_data_request.gen.cc",
         "external/perfetto/protos/perfetto/common/data_source_descriptor.gen.cc",
         "external/perfetto/protos/perfetto/common/descriptor.gen.cc",
+        "external/perfetto/protos/perfetto/common/ftrace_descriptor.gen.cc",
         "external/perfetto/protos/perfetto/common/gpu_counter_descriptor.gen.cc",
         "external/perfetto/protos/perfetto/common/interceptor_descriptor.gen.cc",
         "external/perfetto/protos/perfetto/common/observable_events.gen.cc",
@@ -1975,6 +1975,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -1997,6 +1998,7 @@
         "external/perfetto/protos/perfetto/common/commit_data_request.gen.h",
         "external/perfetto/protos/perfetto/common/data_source_descriptor.gen.h",
         "external/perfetto/protos/perfetto/common/descriptor.gen.h",
+        "external/perfetto/protos/perfetto/common/ftrace_descriptor.gen.h",
         "external/perfetto/protos/perfetto/common/gpu_counter_descriptor.gen.h",
         "external/perfetto/protos/perfetto/common/interceptor_descriptor.gen.h",
         "external/perfetto/protos/perfetto/common/observable_events.gen.h",
@@ -2023,6 +2025,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -2044,6 +2047,7 @@
         "external/perfetto/protos/perfetto/common/commit_data_request.pb.cc",
         "external/perfetto/protos/perfetto/common/data_source_descriptor.pb.cc",
         "external/perfetto/protos/perfetto/common/descriptor.pb.cc",
+        "external/perfetto/protos/perfetto/common/ftrace_descriptor.pb.cc",
         "external/perfetto/protos/perfetto/common/gpu_counter_descriptor.pb.cc",
         "external/perfetto/protos/perfetto/common/interceptor_descriptor.pb.cc",
         "external/perfetto/protos/perfetto/common/observable_events.pb.cc",
@@ -2066,6 +2070,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -2087,6 +2092,7 @@
         "external/perfetto/protos/perfetto/common/commit_data_request.pb.h",
         "external/perfetto/protos/perfetto/common/data_source_descriptor.pb.h",
         "external/perfetto/protos/perfetto/common/descriptor.pb.h",
+        "external/perfetto/protos/perfetto/common/ftrace_descriptor.pb.h",
         "external/perfetto/protos/perfetto/common/gpu_counter_descriptor.pb.h",
         "external/perfetto/protos/perfetto/common/interceptor_descriptor.pb.h",
         "external/perfetto/protos/perfetto/common/observable_events.pb.h",
@@ -2113,6 +2119,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -2135,6 +2142,7 @@
         "external/perfetto/protos/perfetto/common/commit_data_request.pbzero.cc",
         "external/perfetto/protos/perfetto/common/data_source_descriptor.pbzero.cc",
         "external/perfetto/protos/perfetto/common/descriptor.pbzero.cc",
+        "external/perfetto/protos/perfetto/common/ftrace_descriptor.pbzero.cc",
         "external/perfetto/protos/perfetto/common/gpu_counter_descriptor.pbzero.cc",
         "external/perfetto/protos/perfetto/common/interceptor_descriptor.pbzero.cc",
         "external/perfetto/protos/perfetto/common/observable_events.pbzero.cc",
@@ -2157,6 +2165,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -2179,6 +2188,7 @@
         "external/perfetto/protos/perfetto/common/commit_data_request.pbzero.h",
         "external/perfetto/protos/perfetto/common/data_source_descriptor.pbzero.h",
         "external/perfetto/protos/perfetto/common/descriptor.pbzero.h",
+        "external/perfetto/protos/perfetto/common/ftrace_descriptor.pbzero.h",
         "external/perfetto/protos/perfetto/common/gpu_counter_descriptor.pbzero.h",
         "external/perfetto/protos/perfetto/common/interceptor_descriptor.pbzero.h",
         "external/perfetto/protos/perfetto/common/observable_events.pbzero.h",
@@ -2391,6 +2401,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -2921,21 +2932,6 @@
     ],
 }
 
-// GN: //protos/perfetto/config:merged_config_descriptor
-genrule {
-    name: "perfetto_protos_perfetto_config_merged_config_descriptor",
-    srcs: [
-        "protos/perfetto/config/perfetto_config.proto",
-    ],
-    tools: [
-        "aprotoc",
-    ],
-    cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
-    out: [
-        "perfetto_protos_perfetto_config_merged_config_descriptor.bin",
-    ],
-}
-
 // GN: //protos/perfetto/config/power:cpp
 genrule {
     name: "perfetto_protos_perfetto_config_power_cpp_gen",
@@ -4094,6 +4090,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -7221,11 +7218,11 @@
 genrule {
     name: "perfetto_src_perfetto_cmd_gen_cc_config_descriptor",
     srcs: [
-        ":perfetto_protos_perfetto_config_merged_config_descriptor",
+        ":perfetto_protos_perfetto_config_descriptor",
     ],
     cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
     out: [
-        "src/perfetto_cmd/perfetto_config.descriptor.h",
+        "src/perfetto_cmd/config.descriptor.h",
     ],
     tool_files: [
         "tools/gen_cc_proto_descriptor.py",
@@ -9217,14 +9214,6 @@
     ],
 }
 
-// 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",
@@ -9495,7 +9484,6 @@
         ":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",
@@ -9596,9 +9584,6 @@
         ":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",
@@ -9724,7 +9709,6 @@
         "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",
diff --git a/BUILD b/BUILD
index 283d9ce..7e47fd9 100644
--- a/BUILD
+++ b/BUILD
@@ -787,10 +787,10 @@
 perfetto_cc_proto_descriptor(
     name = "src_perfetto_cmd_gen_cc_config_descriptor",
     deps = [
-        ":protos_perfetto_config_merged_config_descriptor",
+        ":protos_perfetto_config_descriptor",
     ],
     outs = [
-        "src/perfetto_cmd/perfetto_config.descriptor.h",
+        "src/perfetto_cmd/config.descriptor.h",
     ],
 )
 
@@ -1974,17 +1974,118 @@
 # Proto libraries
 # ##############################################################################
 
-# GN target: //protos/perfetto/common:cpp
-perfetto_cc_protocpp_library(
-    name = "protos_perfetto_common_cpp",
+# GN target: [//protos/perfetto/config:source_set]
+perfetto_proto_library(
+    name = "config_proto",
+    visibility = PERFETTO_CONFIG.public_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",
     ],
 )
 
-# GN target: //protos/perfetto/common:lite
+# GN target: [//protos/perfetto/config:source_set]
 perfetto_cc_proto_library(
-    name = "protos_perfetto_common_lite",
+    name = "config_cc_proto",
+    visibility = PERFETTO_CONFIG.public_visibility,
+    deps = [
+        ":config_proto",
+    ],
+)
+
+# GN target: [//protos/perfetto/config:source_set]
+perfetto_java_proto_library(
+    name = "config_java_proto",
+    visibility = PERFETTO_CONFIG.public_visibility,
+    deps = [
+        ":config_proto",
+    ],
+)
+
+# GN target: [//protos/perfetto/config:source_set]
+perfetto_java_lite_proto_library(
+    name = "config_java_proto_lite",
+    visibility = PERFETTO_CONFIG.public_visibility,
+    deps = [
+        ":config_proto",
+    ],
+)
+
+# GN target: [//protos/perfetto/trace:non_minimal_source_set, //protos/perfetto/trace:minimal_source_set]
+perfetto_proto_library(
+    name = "trace_proto",
+    visibility = PERFETTO_CONFIG.public_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:non_minimal_source_set, //protos/perfetto/trace:minimal_source_set]
+perfetto_cc_proto_library(
+    name = "trace_cc_proto",
+    visibility = PERFETTO_CONFIG.public_visibility,
+    deps = [
+        ":trace_proto",
+    ],
+)
+
+# GN target: [//protos/perfetto/trace:non_minimal_source_set, //protos/perfetto/trace:minimal_source_set]
+perfetto_java_proto_library(
+    name = "trace_java_proto",
+    visibility = PERFETTO_CONFIG.public_visibility,
+    deps = [
+        ":trace_proto",
+    ],
+)
+
+# GN target: [//protos/perfetto/trace:non_minimal_source_set, //protos/perfetto/trace:minimal_source_set]
+perfetto_java_lite_proto_library(
+    name = "trace_java_proto_lite",
+    visibility = PERFETTO_CONFIG.public_visibility,
+    deps = [
+        ":trace_proto",
+    ],
+)
+
+# GN target: //protos/perfetto/common:cpp
+perfetto_cc_protocpp_library(
+    name = "protos_perfetto_common_cpp",
     deps = [
         ":protos_perfetto_common_protos",
     ],
@@ -2000,6 +2101,7 @@
         "protos/perfetto/common/commit_data_request.proto",
         "protos/perfetto/common/data_source_descriptor.proto",
         "protos/perfetto/common/descriptor.proto",
+        "protos/perfetto/common/ftrace_descriptor.proto",
         "protos/perfetto/common/gpu_counter_descriptor.proto",
         "protos/perfetto/common/interceptor_descriptor.proto",
         "protos/perfetto/common/observable_events.proto",
@@ -2032,14 +2134,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/android:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_android_lite",
-    deps = [
-        ":protos_perfetto_config_android_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/android:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_android_protos",
@@ -2103,14 +2197,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/ftrace:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_ftrace_lite",
-    deps = [
-        ":protos_perfetto_config_ftrace_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/ftrace:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_ftrace_protos",
@@ -2138,14 +2224,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/gpu:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_gpu_lite",
-    deps = [
-        ":protos_perfetto_config_gpu_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/gpu:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_gpu_protos",
@@ -2174,14 +2252,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/inode_file:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_inode_file_lite",
-    deps = [
-        ":protos_perfetto_config_inode_file_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/inode_file:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_inode_file_protos",
@@ -2210,14 +2280,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/interceptors:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_interceptors_lite",
-    deps = [
-        ":protos_perfetto_config_interceptors_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/interceptors:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_interceptors_protos",
@@ -2241,34 +2303,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_lite",
-    deps = [
-        ":protos_perfetto_config_protos",
-    ],
-)
-
-# 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_source_set
-perfetto_proto_library(
-    name = "protos_perfetto_config_merged_config_protos",
-    srcs = [
-        "protos/perfetto/config/perfetto_config.proto",
-    ],
-    visibility = PERFETTO_CONFIG.public_visibility,
-)
-
 # GN target: //protos/perfetto/config/power:cpp
 perfetto_cc_protocpp_library(
     name = "protos_perfetto_config_power_cpp",
@@ -2277,14 +2311,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/power:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_power_lite",
-    deps = [
-        ":protos_perfetto_config_power_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/power:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_power_protos",
@@ -2312,14 +2338,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/process_stats:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_process_stats_lite",
-    deps = [
-        ":protos_perfetto_config_process_stats_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/process_stats:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_process_stats_protos",
@@ -2348,14 +2366,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/profiling:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_profiling_lite",
-    deps = [
-        ":protos_perfetto_config_profiling_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/profiling:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_profiling_protos",
@@ -2392,7 +2402,9 @@
         "protos/perfetto/config/test_config.proto",
         "protos/perfetto/config/trace_config.proto",
     ],
-    visibility = PERFETTO_CONFIG.public_visibility,
+    visibility = [
+        PERFETTO_CONFIG.proto_library_visibility,
+    ],
     deps = [
         ":protos_perfetto_common_protos",
         ":protos_perfetto_config_android_protos",
@@ -2417,14 +2429,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/sys_stats:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_sys_stats_lite",
-    deps = [
-        ":protos_perfetto_config_sys_stats_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/sys_stats:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_sys_stats_protos",
@@ -2456,14 +2460,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/config/track_event:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_config_track_event_lite",
-    deps = [
-        ":protos_perfetto_config_track_event_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/config/track_event:source_set
 perfetto_proto_library(
     name = "protos_perfetto_config_track_event_protos",
@@ -2638,7 +2634,9 @@
         "protos/perfetto/metrics/android/trace_quality.proto",
         "protos/perfetto/metrics/android/unsymbolized_frames.proto",
     ],
-    visibility = PERFETTO_CONFIG.public_visibility,
+    visibility = [
+        PERFETTO_CONFIG.proto_library_visibility,
+    ],
 )
 
 # GN target: //protos/perfetto/metrics/chrome:descriptor
@@ -2720,14 +2718,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/android:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_android_lite",
-    deps = [
-        ":protos_perfetto_trace_android_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/android:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_android_protos",
@@ -2757,14 +2747,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/chrome:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_chrome_lite",
-    deps = [
-        ":protos_perfetto_trace_chrome_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/chrome:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_chrome_protos",
@@ -2797,14 +2779,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/filesystem:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_filesystem_lite",
-    deps = [
-        ":protos_perfetto_trace_filesystem_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/filesystem:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_filesystem_protos",
@@ -2824,14 +2798,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/ftrace:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_ftrace_lite",
-    deps = [
-        ":protos_perfetto_trace_ftrace_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/ftrace:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_ftrace_protos",
@@ -2899,14 +2865,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/gpu:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_gpu_lite",
-    deps = [
-        ":protos_perfetto_trace_gpu_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/gpu:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_gpu_protos",
@@ -2934,14 +2892,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/interned_data:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_interned_data_lite",
-    deps = [
-        ":protos_perfetto_trace_interned_data_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/interned_data:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_interned_data_protos",
@@ -2971,23 +2921,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace:merged_trace_source_set
-perfetto_proto_library(
-    name = "protos_perfetto_trace_merged_trace_protos",
-    srcs = [
-        "protos/perfetto/trace/perfetto_trace.proto",
-    ],
-    visibility = PERFETTO_CONFIG.public_visibility,
-)
-
-# GN target: //protos/perfetto/trace:minimal_lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_minimal_lite",
-    deps = [
-        ":protos_perfetto_trace_minimal_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace:minimal_source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_minimal_protos",
@@ -3035,14 +2968,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace:non_minimal_lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_non_minimal_lite",
-    deps = [
-        ":protos_perfetto_trace_non_minimal_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace:non_minimal_source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_non_minimal_protos",
@@ -3056,7 +2981,9 @@
         "protos/perfetto/trace/trace_packet_defaults.proto",
         "protos/perfetto/trace/ui_state.proto",
     ],
-    visibility = PERFETTO_CONFIG.public_visibility,
+    visibility = [
+        PERFETTO_CONFIG.proto_library_visibility,
+    ],
     deps = [
         ":protos_perfetto_common_protos",
         ":protos_perfetto_config_android_protos",
@@ -3124,14 +3051,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/perfetto:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_perfetto_lite",
-    deps = [
-        ":protos_perfetto_trace_perfetto_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/perfetto:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_perfetto_protos",
@@ -3152,14 +3071,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/power:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_power_lite",
-    deps = [
-        ":protos_perfetto_trace_power_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/power:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_power_protos",
@@ -3227,14 +3138,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/profiling:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_profiling_lite",
-    deps = [
-        ":protos_perfetto_trace_profiling_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/profiling:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_profiling_protos",
@@ -3262,14 +3165,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/ps:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_ps_lite",
-    deps = [
-        ":protos_perfetto_trace_ps_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/ps:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_ps_protos",
@@ -3290,14 +3185,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/sys_stats:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_sys_stats_lite",
-    deps = [
-        ":protos_perfetto_trace_sys_stats_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/sys_stats:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_sys_stats_protos",
@@ -3321,14 +3208,6 @@
     ],
 )
 
-# GN target: //protos/perfetto/trace/system_info:lite
-perfetto_cc_proto_library(
-    name = "protos_perfetto_trace_system_info_lite",
-    deps = [
-        ":protos_perfetto_trace_system_info_protos",
-    ],
-)
-
 # GN target: //protos/perfetto/trace/system_info:source_set
 perfetto_proto_library(
     name = "protos_perfetto_trace_system_info_protos",
@@ -4066,6 +3945,7 @@
     deps = [
         ":protos_perfetto_metrics_protos",
     ],
+    visibility = PERFETTO_CONFIG.public_visibility,
 )
 
 perfetto_java_proto_library(
@@ -4073,27 +3953,7 @@
     deps = [
         ":protos_perfetto_metrics_android_protos",
     ],
-)
-
-perfetto_java_proto_library(
-    name = "protos_perfetto_trace_merged_trace_java",
-    deps = [
-        ":protos_perfetto_trace_merged_trace_protos",
-    ],
-)
-
-perfetto_java_proto_library(
-    name = "protos_perfetto_config_merged_config_java",
-    deps = [
-        ":protos_perfetto_config_merged_config_protos",
-    ],
-)
-
-perfetto_java_lite_proto_library(
-    name = "protos_perfetto_config_merged_config_java_lite",
-    deps = [
-        ":protos_perfetto_config_merged_config_protos",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
 )
 
 perfetto_gensignature_internal_only(
diff --git a/BUILD.extras b/BUILD.extras
index f91b2f4..dbf18af 100644
--- a/BUILD.extras
+++ b/BUILD.extras
@@ -36,6 +36,7 @@
     deps = [
         ":protos_perfetto_metrics_protos",
     ],
+    visibility = PERFETTO_CONFIG.public_visibility,
 )
 
 perfetto_java_proto_library(
@@ -43,27 +44,7 @@
     deps = [
         ":protos_perfetto_metrics_android_protos",
     ],
-)
-
-perfetto_java_proto_library(
-    name = "protos_perfetto_trace_merged_trace_java",
-    deps = [
-        ":protos_perfetto_trace_merged_trace_protos",
-    ],
-)
-
-perfetto_java_proto_library(
-    name = "protos_perfetto_config_merged_config_java",
-    deps = [
-        ":protos_perfetto_config_merged_config_protos",
-    ],
-)
-
-perfetto_java_lite_proto_library(
-    name = "protos_perfetto_config_merged_config_java_lite",
-    deps = [
-        ":protos_perfetto_config_merged_config_protos",
-    ],
+    visibility = PERFETTO_CONFIG.public_visibility,
 )
 
 perfetto_gensignature_internal_only(
diff --git a/BUILD.gn b/BUILD.gn
index 53b8d9a..33f8818 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -134,7 +134,6 @@
     "test/configs",
 
     # 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
diff --git a/CHANGELOG b/CHANGELOG
index ebad664..02ab1f8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 Unreleased:
   Tracing service and probes:
-    *
+    * Removed merged trace and config protos from Bazel. Embedder should
+      instead depend on the non-merged proto targets.
   Trace Processor:
     *
   UI:
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index dbfa0fa..dec85bc 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -80,7 +80,10 @@
 
 if (enable_perfetto_trace_processor) {
   perfetto_unittests_targets += [ "src/trace_processor:unittests" ]
-  perfetto_unittests_targets += [ "tools/trace_to_text:unittests" ]
+
+  # TODO(mohitms): reenable this once we no longer link lite and full protobuf
+  # simultaneously.
+  # 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/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index 58444a8..593204f 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -124,7 +124,6 @@
 std::string ReplaceAll(std::string str,
                        const std::string& to_replace,
                        const std::string& replacement);
-std::string TrimLeading(const std::string& str);
 
 // A BSD-style strlcpy without the return value.
 // Copies at most |dst_size|-1 characters. Unlike strncpy, it always \0
diff --git a/include/perfetto/ext/base/subprocess.h b/include/perfetto/ext/base/subprocess.h
index 80adbdc..cca6c29 100644
--- a/include/perfetto/ext/base/subprocess.h
+++ b/include/perfetto/ext/base/subprocess.h
@@ -106,13 +106,18 @@
                       // This includes crashes or other signals on UNIX.
   };
 
-  enum OutputMode {
+  enum class OutputMode {
     kInherit = 0,  // Inherit's the caller process stdout/stderr.
-    kDevNull,      // dup() onto /dev/null
+    kDevNull,      // dup() onto /dev/null.
     kBuffer,       // dup() onto a pipe and move it into the output() buffer.
     kFd,           // dup() onto the passed args.fd.
   };
 
+  enum class InputMode {
+    kBuffer = 0,  // dup() onto a pipe and write args.input on it.
+    kDevNull,     // dup() onto /dev/null.
+  };
+
   // Input arguments for configuring the subprocess behavior.
   struct Args {
     Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {}
@@ -149,11 +154,13 @@
     // The file descriptors in this list will not be closed.
     std::vector<int> preserve_fds;
 
-    // The data to push in the child process stdin.
+    // The data to push in the child process stdin, if input_mode ==
+    // InputMode::kBuffer.
     std::string input;
 
-    OutputMode stdout_mode = kInherit;
-    OutputMode stderr_mode = kInherit;
+    InputMode stdin_mode = InputMode::kBuffer;
+    OutputMode stdout_mode = OutputMode::kInherit;
+    OutputMode stderr_mode = OutputMode::kInherit;
 
     base::ScopedPlatformHandle out_fd;
 
@@ -213,7 +220,7 @@
   bool timed_out() const { return s_->timed_out; }
 
   // This contains both stdout and stderr (if the corresponding _mode ==
-  // kBuffer). It's non-const so the caller can std::move() it.
+  // OutputMode::kBuffer). It's non-const so the caller can std::move() it.
   std::string& output() { return s_->output; }
   const std::string& output() const { return s_->output; }
 
@@ -236,7 +243,7 @@
     PlatformProcessId pid;
     Status status = kNotStarted;
     int returncode = -1;
-    std::string output;  // Stdin+stderr. Only when kBuffer.
+    std::string output;  // Stdin+stderr. Only when OutputMode::kBuffer.
     std::unique_ptr<ResourceUsage> rusage{new ResourceUsage()};
     bool timed_out = false;
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
diff --git a/infra/luci/recipe_modules/macos_sdk/__init__.py b/infra/luci/recipe_modules/macos_sdk/__init__.py
index 801083d..6a20ec1 100644
--- a/infra/luci/recipe_modules/macos_sdk/__init__.py
+++ b/infra/luci/recipe_modules/macos_sdk/__init__.py
@@ -44,7 +44,7 @@
         ),
         default={
             'sdk_version':
- '12D4e',
+ '13C5066c',
             'tool_package':
  'infra/tools/mac_toolchain/${platform}',
             'tool_version':
diff --git a/infra/luci/recipe_modules/macos_sdk/examples/full.expected/mac.json b/infra/luci/recipe_modules/macos_sdk/examples/full.expected/mac.json
index 2f31aaa..7d0f197 100644
--- a/infra/luci/recipe_modules/macos_sdk/examples/full.expected/mac.json
+++ b/infra/luci/recipe_modules/macos_sdk/examples/full.expected/mac.json
@@ -35,7 +35,7 @@
       "-kind",
       "mac",
       "-xcode-version",
-      "12d4e",
+      "13c5066c",
       "-output-dir",
       "[CACHE]/macos_sdk/XCode.app"
     ],
diff --git a/infra/luci/recipes/perfetto.expected/ci_mac.json b/infra/luci/recipes/perfetto.expected/ci_mac.json
index 33125de..4c9f89f 100644
--- a/infra/luci/recipes/perfetto.expected/ci_mac.json
+++ b/infra/luci/recipes/perfetto.expected/ci_mac.json
@@ -125,7 +125,7 @@
       "-kind",
       "mac",
       "-xcode-version",
-      "12d4e",
+      "13c5066c",
       "-output-dir",
       "[CACHE]/macos_sdk/XCode.app"
     ],
@@ -677,7 +677,7 @@
       "-kind",
       "mac",
       "-xcode-version",
-      "12d4e",
+      "13c5066c",
       "-output-dir",
       "[CACHE]/macos_sdk/XCode.app"
     ],
diff --git a/protos/perfetto/common/BUILD.gn b/protos/perfetto/common/BUILD.gn
index 1af189d..5ecc0de 100644
--- a/protos/perfetto/common/BUILD.gn
+++ b/protos/perfetto/common/BUILD.gn
@@ -25,6 +25,7 @@
     "commit_data_request.proto",
     "data_source_descriptor.proto",
     "descriptor.proto",
+    "ftrace_descriptor.proto",
     "gpu_counter_descriptor.proto",
     "interceptor_descriptor.proto",
     "observable_events.proto",
diff --git a/protos/perfetto/common/data_source_descriptor.proto b/protos/perfetto/common/data_source_descriptor.proto
index 2cdace2..dc2d742 100644
--- a/protos/perfetto/common/data_source_descriptor.proto
+++ b/protos/perfetto/common/data_source_descriptor.proto
@@ -18,6 +18,7 @@
 
 package perfetto.protos;
 
+import "protos/perfetto/common/ftrace_descriptor.proto";
 import "protos/perfetto/common/gpu_counter_descriptor.proto";
 import "protos/perfetto/common/track_event_descriptor.proto";
 
@@ -54,4 +55,6 @@
   optional GpuCounterDescriptor gpu_counter_descriptor = 5 [lazy = true];
 
   optional TrackEventDescriptor track_event_descriptor = 6 [lazy = true];
+
+  optional FtraceDescriptor ftrace_descriptor = 8 [lazy = true];
 }
diff --git a/protos/perfetto/common/ftrace_descriptor.proto b/protos/perfetto/common/ftrace_descriptor.proto
new file mode 100644
index 0000000..5406d6d
--- /dev/null
+++ b/protos/perfetto/common/ftrace_descriptor.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+message FtraceDescriptor {
+  message AtraceCategory {
+    optional string name = 1;
+    optional string description = 2;
+  }
+
+  // Report the available atrace categories.
+  //
+  // Used by Traceur via `perfetto --query`.
+  repeated AtraceCategory atrace_categories = 1;
+}
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 471bfbe..4b96859 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -49,20 +49,3 @@
   deps = [ ":source_set" ]
   sources = [ "trace_config.proto" ]
 }
-
-# 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/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 30195a4..5674f7a 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -13,6 +13,22 @@
 
 option go_package = "github.com/google/perfetto/perfetto_proto";
 
+// Begin of protos/perfetto/common/ftrace_descriptor.proto
+
+message FtraceDescriptor {
+  message AtraceCategory {
+    optional string name = 1;
+    optional string description = 2;
+  }
+
+  // Report the available atrace categories.
+  //
+  // Used by Traceur via `perfetto --query`.
+  repeated AtraceCategory atrace_categories = 1;
+}
+
+// End of protos/perfetto/common/ftrace_descriptor.proto
+
 // Begin of protos/perfetto/common/gpu_counter_descriptor.proto
 
 // Description of GPU counters.
@@ -186,6 +202,8 @@
   optional GpuCounterDescriptor gpu_counter_descriptor = 5 [lazy = true];
 
   optional TrackEventDescriptor track_event_descriptor = 6 [lazy = true];
+
+  optional FtraceDescriptor ftrace_descriptor = 8 [lazy = true];
 }
 
 // End of protos/perfetto/common/data_source_descriptor.proto
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index cfcca49..f0d0462 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -137,9 +137,6 @@
 # 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",
-  ]
+  proto_generators = [ "lite" ]
   sources = [ "perfetto_trace.proto" ]
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 7e7747c..5b04757 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -13,6 +13,22 @@
 
 option go_package = "github.com/google/perfetto/perfetto_proto";
 
+// Begin of protos/perfetto/common/ftrace_descriptor.proto
+
+message FtraceDescriptor {
+  message AtraceCategory {
+    optional string name = 1;
+    optional string description = 2;
+  }
+
+  // Report the available atrace categories.
+  //
+  // Used by Traceur via `perfetto --query`.
+  repeated AtraceCategory atrace_categories = 1;
+}
+
+// End of protos/perfetto/common/ftrace_descriptor.proto
+
 // Begin of protos/perfetto/common/gpu_counter_descriptor.proto
 
 // Description of GPU counters.
@@ -186,6 +202,8 @@
   optional GpuCounterDescriptor gpu_counter_descriptor = 5 [lazy = true];
 
   optional TrackEventDescriptor track_event_descriptor = 6 [lazy = true];
+
+  optional FtraceDescriptor ftrace_descriptor = 8 [lazy = true];
 }
 
 // End of protos/perfetto/common/data_source_descriptor.proto
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index 5efca77..7e98ecd 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -205,11 +205,6 @@
   return str;
 }
 
-std::string TrimLeading(const std::string& str) {
-  size_t idx = str.find_first_not_of(' ');
-  return idx == std::string::npos ? str : str.substr(idx);
-}
-
 size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
   if (PERFETTO_UNLIKELY(dst_size) == 0)
     return 0;
diff --git a/src/base/string_utils_unittest.cc b/src/base/string_utils_unittest.cc
index 39803ff..9a885d1 100644
--- a/src/base/string_utils_unittest.cc
+++ b/src/base/string_utils_unittest.cc
@@ -296,13 +296,6 @@
   EXPECT_EQ(ReplaceAll("abc", "c", "bbb"), "abbbb");
 }
 
-TEST(StringUtilsTest, TrimLeading) {
-  EXPECT_EQ(TrimLeading(""), "");
-  EXPECT_EQ(TrimLeading("a"), "a");
-  EXPECT_EQ(TrimLeading(" aaaa"), "aaaa");
-  EXPECT_EQ(TrimLeading(" aaaaa     "), "aaaaa     ");
-}
-
 TEST(StringUtilsTest, StringCopy) {
   // Nothing should be written when |dst_size| = 0.
   {
diff --git a/src/base/subprocess_posix.cc b/src/base/subprocess_posix.cc
index 5e09544..fd32bb9 100644
--- a/src/base/subprocess_posix.cc
+++ b/src/base/subprocess_posix.cc
@@ -100,41 +100,49 @@
   if (getppid() == 1)
     die("terminating because parent process died");
 
-  if (dup2(args->stdin_pipe_rd, STDIN_FILENO) == -1)
-    die("Failed to dup2(STDIN)");
-  close(args->stdin_pipe_rd);
+  switch (args->create_args->stdin_mode) {
+    case Subprocess::InputMode::kBuffer:
+      if (dup2(args->stdin_pipe_rd, STDIN_FILENO) == -1)
+        die("Failed to dup2(STDIN)");
+      close(args->stdin_pipe_rd);
+      break;
+    case Subprocess::InputMode::kDevNull:
+      if (dup2(open("/dev/null", O_RDONLY), STDIN_FILENO) == -1)
+        die("Failed to dup2(STDOUT)");
+      break;
+  }
 
   switch (args->create_args->stdout_mode) {
-    case Subprocess::kInherit:
+    case Subprocess::OutputMode::kInherit:
       break;
-    case Subprocess::kDevNull: {
+    case Subprocess::OutputMode::kDevNull: {
       if (dup2(open("/dev/null", O_RDWR), STDOUT_FILENO) == -1)
         die("Failed to dup2(STDOUT)");
       break;
     }
-    case Subprocess::kBuffer:
+    case Subprocess::OutputMode::kBuffer:
       if (dup2(args->stdouterr_pipe_wr, STDOUT_FILENO) == -1)
         die("Failed to dup2(STDOUT)");
       break;
-    case Subprocess::kFd:
+    case Subprocess::OutputMode::kFd:
       if (dup2(*args->create_args->out_fd, STDOUT_FILENO) == -1)
         die("Failed to dup2(STDOUT)");
       break;
   }
 
   switch (args->create_args->stderr_mode) {
-    case Subprocess::kInherit:
+    case Subprocess::OutputMode::kInherit:
       break;
-    case Subprocess::kDevNull: {
+    case Subprocess::OutputMode::kDevNull: {
       if (dup2(open("/dev/null", O_RDWR), STDERR_FILENO) == -1)
         die("Failed to dup2(STDERR)");
       break;
     }
-    case Subprocess::kBuffer:
+    case Subprocess::OutputMode::kBuffer:
       if (dup2(args->stdouterr_pipe_wr, STDERR_FILENO) == -1)
         die("Failed to dup2(STDERR)");
       break;
-    case Subprocess::kFd:
+    case Subprocess::OutputMode::kFd:
       if (dup2(*args->create_args->out_fd, STDERR_FILENO) == -1)
         die("Failed to dup2(STDERR)");
       break;
@@ -220,8 +228,10 @@
   }
 
   // Setup the pipes for stdin/err redirection.
-  s_->stdin_pipe = base::Pipe::Create(base::Pipe::kWrNonBlock);
-  proc_args.stdin_pipe_rd = *s_->stdin_pipe.rd;
+  if (args.stdin_mode == InputMode::kBuffer) {
+    s_->stdin_pipe = base::Pipe::Create(base::Pipe::kWrNonBlock);
+    proc_args.stdin_pipe_rd = *s_->stdin_pipe.rd;
+  }
   s_->stdouterr_pipe = base::Pipe::Create(base::Pipe::kRdNonBlock);
   proc_args.stdouterr_pipe_wr = *s_->stdouterr_pipe.wr;
 
diff --git a/src/base/subprocess_unittest.cc b/src/base/subprocess_unittest.cc
index b52c804..6cebd1b 100644
--- a/src/base/subprocess_unittest.cc
+++ b/src/base/subprocess_unittest.cc
@@ -74,8 +74,8 @@
 #else
   Subprocess p({"sh", "-c", "(echo skip_err >&2); echo out_only"});
 #endif
-  p.args.stdout_mode = Subprocess::kBuffer;
-  p.args.stderr_mode = Subprocess::kDevNull;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
+  p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
 
   EXPECT_TRUE(p.Call());
   EXPECT_EQ(p.status(), Subprocess::kTerminated);
@@ -88,8 +88,8 @@
 #else
   Subprocess p({"sh", "-c", "(echo err_only >&2); echo skip_out"});
 #endif
-  p.args.stdout_mode = Subprocess::kDevNull;
-  p.args.stderr_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
+  p.args.stderr_mode = Subprocess::OutputMode::kBuffer;
   EXPECT_TRUE(p.Call());
   EXPECT_EQ(GetOutput(p), "err_only\n");
 }
@@ -100,12 +100,54 @@
 #else
   Subprocess p({"sh", "-c", "echo out; (echo err >&2); echo out2"});
 #endif
-  p.args.stdout_mode = Subprocess::kBuffer;
-  p.args.stderr_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
+  p.args.stderr_mode = Subprocess::OutputMode::kBuffer;
   EXPECT_TRUE(p.Call());
   EXPECT_EQ(GetOutput(p), "out\nerr\nout2\n");
 }
 
+TEST(SubprocessTest, CatInputModeDevNull) {
+  std::string ignored_input = "ignored input";
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  Subprocess p({"cmd", "/C", "findstr . || exit 0"});
+#else
+  Subprocess p({"cat", "-"});
+#endif
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
+  p.args.input = ignored_input;
+  p.args.stdin_mode = Subprocess::InputMode::kDevNull;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kTerminated);
+  EXPECT_EQ(GetOutput(p), "");
+}
+
+TEST(SubprocessTest, BothStdoutAndStderrInputModeDevNull) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  Subprocess p({"cmd", "/c", "echo out&&(echo err>&2)&&echo out2"});
+#else
+  Subprocess p({"sh", "-c", "echo out; (echo err >&2); echo out2"});
+#endif
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
+  p.args.stderr_mode = Subprocess::OutputMode::kBuffer;
+  p.args.stdin_mode = Subprocess::InputMode::kDevNull;
+  EXPECT_TRUE(p.Call());
+  EXPECT_EQ(GetOutput(p), "out\nerr\nout2\n");
+}
+
+TEST(SubprocessTest, AllDevNull) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  Subprocess p({"cmd", "/c", "(exit 1)"});
+#else
+  Subprocess p({"false"});
+#endif
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
+  p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
+  p.args.stdin_mode = Subprocess::InputMode::kDevNull;
+  EXPECT_FALSE(p.Call());
+  EXPECT_EQ(p.status(), Subprocess::kTerminated);
+  EXPECT_EQ(p.returncode(), 1);
+}
+
 TEST(SubprocessTest, BinTrue) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   Subprocess p({"cmd", "/c", "(exit 0)"});
@@ -134,7 +176,7 @@
 #else
   Subprocess p({"echo", "-n", "foobar"});
 #endif
-  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
   EXPECT_TRUE(p.Call());
   EXPECT_EQ(p.status(), Subprocess::kTerminated);
   EXPECT_EQ(p.returncode(), 0);
@@ -148,7 +190,7 @@
 #else
   Subprocess p({"cat", "-"});
 #endif
-  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
   p.args.input = contents;
   EXPECT_TRUE(p.Call());
   EXPECT_EQ(p.status(), Subprocess::kTerminated);
@@ -166,7 +208,7 @@
 #else
   Subprocess p({"cat", tf.path().c_str()});
 #endif
-  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
   EXPECT_TRUE(p.Call());
   EXPECT_EQ(GetOutput(p), contents);
 }
@@ -174,7 +216,7 @@
 TEST(SubprocessTest, Timeout) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   Subprocess p({"ping", "127.0.0.1", "-n", "60"});
-  p.args.stdout_mode = Subprocess::kDevNull;
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
 #else
   Subprocess p({"sleep", "60"});
 #endif
@@ -187,7 +229,7 @@
 TEST(SubprocessTest, TimeoutNotHit) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   Subprocess p({"ping", "127.0.0.1", "-n", "1"});
-  p.args.stdout_mode = Subprocess::kDevNull;
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
 #else
   Subprocess p({"sleep", "0.01"});
 #endif
@@ -201,7 +243,7 @@
 #else
   Subprocess p({"sh", "-c", "while true; do echo stuff; done"});
 #endif
-  p.args.stdout_mode = Subprocess::kDevNull;
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
   EXPECT_FALSE(p.Call(/*timeout_ms=*/10));
   EXPECT_EQ(p.status(), Subprocess::kTerminated);
   EXPECT_TRUE(p.timed_out());
@@ -214,8 +256,8 @@
   // 'sh -c' is to avoid closing stdin (sleep closes it before sleeping).
   Subprocess p({"sh", "-c", "sleep 0.01"});
 #endif
-  p.args.stdout_mode = Subprocess::kDevNull;
-  p.args.stderr_mode = Subprocess::kDevNull;
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
+  p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
   p.args.input = GenLargeString();
   EXPECT_TRUE(p.Call());
   EXPECT_EQ(p.status(), Subprocess::kTerminated);
@@ -232,8 +274,8 @@
   // still handle the timeout properly.
   Subprocess p({"sh", "-c", "sleep 10"});
 #endif
-  p.args.stdout_mode = Subprocess::kDevNull;
-  p.args.stderr_mode = Subprocess::kDevNull;
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
+  p.args.stderr_mode = Subprocess::OutputMode::kDevNull;
   p.args.input = GenLargeString();
   EXPECT_FALSE(p.Call(/*timeout_ms=*/10));
   EXPECT_EQ(p.status(), Subprocess::kTerminated);
@@ -246,7 +288,7 @@
 #else
   Subprocess p({"sleep", "1000"});
 #endif
-  p.args.stdout_mode = Subprocess::kDevNull;
+  p.args.stdout_mode = Subprocess::OutputMode::kDevNull;
   p.Start();
   EXPECT_EQ(p.Poll(), Subprocess::kRunning);
   p.KillAndWaitForTermination();
@@ -267,7 +309,7 @@
 #else
   Subprocess p({"true"});
 #endif
-  p.args.stdout_mode = Subprocess::kFd;
+  p.args.stdout_mode = Subprocess::OutputMode::kFd;
   p.args.out_fd = std::move(pipe.wr);
   p.Start();
 
@@ -296,7 +338,7 @@
 #else
   Subprocess p({"sh", "-c", "echo exec_done; while true; do true; done"});
 #endif
-  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
   p.Start();
 
   // Wait for the fork()+exec() to complete.
@@ -365,7 +407,7 @@
 #else
     Subprocess initial = Subprocess({"sleep", "10000"});
 #endif
-    initial.args.stdout_mode = Subprocess::kDevNull;
+    initial.args.stdout_mode = Subprocess::OutputMode::kDevNull;
     initial.Start();
     Subprocess moved(std::move(initial));
     EXPECT_EQ(moved.Poll(), Subprocess::kRunning);
@@ -379,7 +421,7 @@
 #endif
     initial.args.stdout_mode = Subprocess::OutputMode::kBuffer;
     initial.Start();
-    initial.Wait(/*timeout=*/5000);
+    initial.Wait(/*timeout_ms=*/5000);
     EXPECT_EQ(initial.status(), Subprocess::kTerminated);
     EXPECT_EQ(initial.returncode(), 0);
     EXPECT_EQ(initial.output(), "hello");
@@ -406,7 +448,7 @@
 TEST(SubprocessTest, Entrypoint) {
   Subprocess p;
   p.args.input = "ping\n";
-  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
   p.args.posix_entrypoint_for_testing = [] {
     char buf[32]{};
     PERFETTO_CHECK(fgets(buf, sizeof(buf), stdin));
@@ -428,7 +470,7 @@
   int pipe2_wr = *pipe2.wr;
 
   Subprocess p({"echo", "123"});
-  p.args.stdout_mode = Subprocess::kBuffer;
+  p.args.stdout_mode = Subprocess::OutputMode::kBuffer;
   p.args.preserve_fds.push_back(pipe2_wr);
   p.args.posix_entrypoint_for_testing = [pipe1_wr, pipe2_wr] {
     base::ignore_result(write(pipe1_wr, "fail", 4));
diff --git a/src/base/subprocess_windows.cc b/src/base/subprocess_windows.cc
index d58d7bd..1264655 100644
--- a/src/base/subprocess_windows.cc
+++ b/src/base/subprocess_windows.cc
@@ -61,22 +61,26 @@
   if (!cmd.empty())
     cmd.resize(cmd.size() - 1);
 
-  s_->stdin_pipe = Pipe::Create();
-  // Allow the child process to inherit the other end of the pipe.
-  PERFETTO_CHECK(
-      ::SetHandleInformation(*s_->stdin_pipe.rd, HANDLE_FLAG_INHERIT, 1));
+  if (args.stdin_mode == InputMode::kBuffer) {
+    s_->stdin_pipe = Pipe::Create();
+    // Allow the child process to inherit the other end of the pipe.
+    PERFETTO_CHECK(
+        ::SetHandleInformation(*s_->stdin_pipe.rd, HANDLE_FLAG_INHERIT, 1));
+  }
 
-  if (args.stderr_mode == kBuffer || args.stdout_mode == kBuffer) {
+  if (args.stderr_mode == OutputMode::kBuffer ||
+      args.stdout_mode == OutputMode::kBuffer) {
     s_->stdouterr_pipe = Pipe::Create();
     PERFETTO_CHECK(
         ::SetHandleInformation(*s_->stdouterr_pipe.wr, HANDLE_FLAG_INHERIT, 1));
   }
 
   ScopedPlatformHandle nul_handle;
-  if (args.stderr_mode == kDevNull || args.stdout_mode == kDevNull) {
-    nul_handle.reset(::CreateFileA("NUL", GENERIC_WRITE, FILE_SHARE_WRITE,
-                                   nullptr, OPEN_EXISTING,
-                                   FILE_ATTRIBUTE_NORMAL, nullptr));
+  if (args.stderr_mode == OutputMode::kDevNull ||
+      args.stdout_mode == OutputMode::kDevNull) {
+    nul_handle.reset(::CreateFileA(
+        "NUL", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+        nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
     PERFETTO_CHECK(::SetHandleInformation(*nul_handle, HANDLE_FLAG_INHERIT, 1));
   }
 
@@ -84,13 +88,13 @@
   STARTUPINFOA start_info{};
   start_info.cb = sizeof(STARTUPINFOA);
 
-  if (args.stderr_mode == kInherit) {
+  if (args.stderr_mode == OutputMode::kInherit) {
     start_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
-  } else if (args.stderr_mode == kBuffer) {
+  } else if (args.stderr_mode == OutputMode::kBuffer) {
     start_info.hStdError = *s_->stdouterr_pipe.wr;
-  } else if (args.stderr_mode == kDevNull) {
+  } else if (args.stderr_mode == OutputMode::kDevNull) {
     start_info.hStdError = *nul_handle;
-  } else if (args.stderr_mode == kFd) {
+  } else if (args.stderr_mode == OutputMode::kFd) {
     PERFETTO_CHECK(
         ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
     start_info.hStdError = *args.out_fd;
@@ -98,13 +102,13 @@
     PERFETTO_CHECK(false);
   }
 
-  if (args.stdout_mode == kInherit) {
+  if (args.stdout_mode == OutputMode::kInherit) {
     start_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
-  } else if (args.stdout_mode == kBuffer) {
+  } else if (args.stdout_mode == OutputMode::kBuffer) {
     start_info.hStdOutput = *s_->stdouterr_pipe.wr;
-  } else if (args.stdout_mode == kDevNull) {
+  } else if (args.stdout_mode == OutputMode::kDevNull) {
     start_info.hStdOutput = *nul_handle;
-  } else if (args.stdout_mode == kFd) {
+  } else if (args.stdout_mode == OutputMode::kFd) {
     PERFETTO_CHECK(
         ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
     start_info.hStdOutput = *args.out_fd;
@@ -112,7 +116,12 @@
     PERFETTO_CHECK(false);
   }
 
-  start_info.hStdInput = *s_->stdin_pipe.rd;
+  if (args.stdin_mode == InputMode::kBuffer) {
+    start_info.hStdInput = *s_->stdin_pipe.rd;
+  } else if (args.stdin_mode == InputMode::kDevNull) {
+    start_info.hStdInput = *nul_handle;
+  }
+
   start_info.dwFlags |= STARTF_USESTDHANDLES;
 
   // Create the child process.
@@ -149,9 +158,12 @@
   s_->status = kRunning;
 
   MovableState* s = s_.get();
-  s_->stdin_thread = std::thread(&Subprocess::StdinThread, s, args.input);
+  if (args.stdin_mode == InputMode::kBuffer) {
+    s_->stdin_thread = std::thread(&Subprocess::StdinThread, s, args.input);
+  }
 
-  if (args.stderr_mode == kBuffer || args.stdout_mode == kBuffer) {
+  if (args.stderr_mode == OutputMode::kBuffer ||
+      args.stdout_mode == OutputMode::kBuffer) {
     PERFETTO_DCHECK(s_->stdouterr_pipe.rd);
     s_->stdouterr_thread = std::thread(&Subprocess::StdoutErrThread, s);
   }
@@ -222,14 +234,14 @@
   const int64_t wait_start_ms = base::GetWallTimeMs().count();
 
   // Break out of the loop only after both conditions are satisfied:
-  // - All stdout/stderr data has been read (if kBuffer).
+  // - All stdout/stderr data has been read (if OutputMode::kBuffer).
   // - The process exited.
   // Note that the two events can happen arbitrary order. After the process
   // exits, there might be still data in the pipe buffer, which we want to
   // read fully.
   // Note also that stdout/err might be "complete" before starting, if neither
-  // is operating in kBuffer mode. In that case we just want to wait for the
-  // process termination.
+  // is operating in OutputMode::kBuffer mode. In that case we just want to wait
+  // for the process termination.
   //
   // Instead, don't wait on the stdin to be fully written. The child process
   // might exit prematurely (or crash). If that happens, we can end up in a
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 9cfe232..74ca8fb 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -82,8 +82,8 @@
 }
 
 perfetto_cc_proto_descriptor("gen_cc_config_descriptor") {
-  descriptor_name = "perfetto_config.descriptor"
-  descriptor_target = "../../protos/perfetto/config:merged_config_descriptor"
+  descriptor_name = "config.descriptor"
+  descriptor_target = "../../protos/perfetto/config:descriptor"
 }
 
 source_set("trigger_perfetto_cmd") {
diff --git a/src/perfetto_cmd/pbtxt_to_pb.cc b/src/perfetto_cmd/pbtxt_to_pb.cc
index 7420502..2c7b67a 100644
--- a/src/perfetto_cmd/pbtxt_to_pb.cc
+++ b/src/perfetto_cmd/pbtxt_to_pb.cc
@@ -33,7 +33,7 @@
 #include "perfetto/protozero/message.h"
 #include "perfetto/protozero/message_handle.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
-#include "src/perfetto_cmd/perfetto_config.descriptor.h"
+#include "src/perfetto_cmd/config.descriptor.h"
 
 #include "protos/perfetto/common/descriptor.gen.h"
 
@@ -707,8 +707,7 @@
 
   {
     file_descriptor_set.ParseFromArray(
-        kPerfettoConfigDescriptor.data(),
-        static_cast<int>(kPerfettoConfigDescriptor.size()));
+        kConfigDescriptor.data(), static_cast<int>(kConfigDescriptor.size()));
     for (const auto& file_descriptor : file_descriptor_set.file()) {
       for (const auto& enum_descriptor : file_descriptor.enum_type()) {
         const std::string name =
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index e104296..83d76c0 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -64,6 +64,7 @@
 #include "src/perfetto_cmd/pbtxt_to_pb.h"
 #include "src/perfetto_cmd/trigger_producer.h"
 
+#include "protos/perfetto/common/ftrace_descriptor.gen.h"
 #include "protos/perfetto/common/tracing_service_state.gen.h"
 #include "protos/perfetto/common/track_event_descriptor.gen.h"
 
@@ -1224,13 +1225,21 @@
            producer_id_and_name);
     // Print the category names for clients using the track event SDK.
     if (!ds.ds_descriptor().track_event_descriptor_raw().empty()) {
-      auto raw = ds.ds_descriptor().track_event_descriptor_raw();
-      perfetto::protos::gen::TrackEventDescriptor desc;
+      const std::string& raw = ds.ds_descriptor().track_event_descriptor_raw();
+      protos::gen::TrackEventDescriptor desc;
       if (desc.ParseFromArray(raw.data(), raw.size())) {
         for (const auto& cat : desc.available_categories()) {
           printf("%s,", cat.name().c_str());
         }
       }
+    } else if (!ds.ds_descriptor().ftrace_descriptor_raw().empty()) {
+      const std::string& raw = ds.ds_descriptor().ftrace_descriptor_raw();
+      protos::gen::FtraceDescriptor desc;
+      if (desc.ParseFromArray(raw.data(), raw.size())) {
+        for (const auto& cat : desc.atrace_categories()) {
+          printf("%s,", cat.name().c_str());
+        }
+      }
     }
     printf("\n");
   }  // for data_sources()
@@ -1260,7 +1269,8 @@
     }  // for tracing_sessions()
 
     int sessions_listed = static_cast<int>(svc_state.tracing_sessions().size());
-    if (sessions_listed != svc_state.num_sessions() && geteuid() != 0) {
+    if (sessions_listed != svc_state.num_sessions() &&
+        base::GetCurrentUserId() != 0) {
       printf(
           "\n"
           "NOTE: Some tracing sessions are not reported in the list above.\n"
diff --git a/src/profiling/common/callstack_trie.h b/src/profiling/common/callstack_trie.h
index f8ca304..f5d2378 100644
--- a/src/profiling/common/callstack_trie.h
+++ b/src/profiling/common/callstack_trie.h
@@ -184,6 +184,8 @@
 
   uint64_t next_callstack_id_ = 0;
 
+  // Note: profile_module in trace processor relies on the value of this root
+  // callsite being exactly "1". See the perf_sample parsing code.
   Node root_{MakeRootFrame(), ++next_callstack_id_};
 };
 
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 5400d07..34c6263 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -181,7 +181,6 @@
     ":wire_protocol",
     "../../../gn:default_deps",
     "../../base",
-    "../common:proc_utils",
   ]
   sources = [ "client_api.cc" ]
 }
@@ -296,7 +295,6 @@
     "../../../gn:default_deps",
     "../../base",
     "../../base:unix_socket",
-    "../common:proc_utils",
   ]
   public_deps = [ "../../../gn:libunwindstack" ]
   sources = [
diff --git a/src/profiling/memory/client_api.cc b/src/profiling/memory/client_api.cc
index 6ec2a61..0d46ac2 100644
--- a/src/profiling/memory/client_api.cc
+++ b/src/profiling/memory/client_api.cc
@@ -28,17 +28,14 @@
 #include <atomic>
 #include <cinttypes>
 #include <memory>
-#include <tuple>
 #include <type_traits>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/no_destructor.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/base/utils.h"
 
-#include "src/profiling/common/proc_utils.h"
 #include "src/profiling/memory/client.h"
 #include "src/profiling/memory/client_api_factory.h"
 #include "src/profiling/memory/scoped_spinlock.h"
diff --git a/src/profiling/memory/client_api_factory_android.cc b/src/profiling/memory/client_api_factory_android.cc
index 370dd60..16c3a25 100644
--- a/src/profiling/memory/client_api_factory_android.cc
+++ b/src/profiling/memory/client_api_factory_android.cc
@@ -27,7 +27,6 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/unix_socket.h"
-#include "src/profiling/common/proc_utils.h"
 #include "src/profiling/memory/client.h"
 
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
diff --git a/src/trace_processor/dynamic/ancestor_generator.cc b/src/trace_processor/dynamic/ancestor_generator.cc
index c126ced..1621a37 100644
--- a/src/trace_processor/dynamic/ancestor_generator.cc
+++ b/src/trace_processor/dynamic/ancestor_generator.cc
@@ -20,6 +20,7 @@
 #include <set>
 
 #include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/status_macros.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -50,18 +51,13 @@
 }
 
 template <typename T>
-base::Optional<RowMap> BuildAncestorsRowMap(const T& table,
-                                            typename T::Id starting_id) {
+base::Status BuildAncestorsRowMap(const T& table,
+                                  typename T::Id starting_id,
+                                  RowMap& rowmap_return) {
   auto start_row = table.id().IndexOf(starting_id);
-
   if (!start_row) {
-    // TODO(lalitm): Ideally this should result in an error, or be filtered out
-    // during ValidateConstraints so we can just dereference |start_row|
-    // directly. However ValidateConstraints doesn't know the value we're
-    // filtering for so can't ensure it exists. For now we return a nullptr
-    // which will cause the query to surface an error with the message "SQL
-    // error: constraint failed".
-    return base::nullopt;
+    return base::ErrStatus("no row with id %" PRIu32 "",
+                           static_cast<uint32_t>(starting_id.value));
   }
 
   std::vector<uint32_t> parent_rows;
@@ -72,23 +68,25 @@
     // Update the loop variable by looking up the next parent_id.
     maybe_parent_id = table.parent_id()[parent_row];
   }
-  return RowMap(std::move(parent_rows));
+  rowmap_return = RowMap{parent_rows};
+  return base::OkStatus();
 }
 
 // Constraint_value is used to construct the hidden column "start_id"
 // needed by SQL.
 // Starting_id refers to the id that is used to generate the ancestors.
 template <typename T>
-std::unique_ptr<Table> BuildAncestorsTable(int64_t constraint_value,
-                                           const T& table,
-                                           typename T::Id starting_id) {
+base::Status BuildAncestorsTable(int64_t constraint_value,
+                                 const T& table,
+                                 typename T::Id starting_id,
+                                 std::unique_ptr<Table>& table_return) {
   // Build up all the parents row ids.
-  auto ancestors = BuildAncestorsRowMap(table, starting_id);
-  if (!ancestors) {
-    return nullptr;
-  }
-  return std::unique_ptr<Table>(new Table(ExtendTableWithStartId(
-      table.Apply(std::move(*ancestors)), constraint_value)));
+  RowMap ancestors;
+  RETURN_IF_ERROR(BuildAncestorsRowMap(table, starting_id, ancestors));
+
+  table_return.reset(new Table(ExtendTableWithStartId(
+      table.Apply(std::move(ancestors)), constraint_value)));
+  return base::OkStatus();
 }
 }  // namespace
 
@@ -96,7 +94,7 @@
                                      TraceProcessorContext* context)
     : type_(type), context_(context) {}
 
-util::Status AncestorGenerator::ValidateConstraints(
+base::Status AncestorGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   const auto& cs = qc.constraints();
 
@@ -105,32 +103,46 @@
     return c.column == column && c.op == SQLITE_INDEX_CONSTRAINT_EQ;
   };
   bool has_id_cs = std::find_if(cs.begin(), cs.end(), id_fn) != cs.end();
-  return has_id_cs ? util::OkStatus()
-                   : util::ErrStatus("Failed to find required constraints");
+  return has_id_cs ? base::OkStatus()
+                   : base::ErrStatus("Failed to find required constraints");
 }
 
-std::unique_ptr<Table> AncestorGenerator::ComputeTable(
+base::Status AncestorGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   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;
-  });
-  PERFETTO_DCHECK(it != cs.end());
-  auto start_id = it->value.AsLong();
+  auto constraint_it =
+      std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
+        return c.col_idx == column && c.op == FilterOp::kEq;
+      });
+  PERFETTO_DCHECK(constraint_it != cs.end());
+  if (constraint_it == cs.end() ||
+      constraint_it->value.type != SqlValue::Type::kLong) {
+    return base::ErrStatus("invalid start_id");
+  }
+  auto start_id = constraint_it->value.AsLong();
 
   switch (type_) {
-    case Ancestor::kSlice:
-      return BuildAncestorsTable(
+    case Ancestor::kSlice: {
+      RETURN_IF_ERROR(BuildAncestorsTable(
           /* constraint_id = */ start_id, context_->storage->slice_table(),
-          /* starting_id = */ SliceId(static_cast<uint32_t>(start_id)));
-    case Ancestor::kStackProfileCallsite:
-      return BuildAncestorsTable(
+          /* starting_id = */ SliceId(static_cast<uint32_t>(start_id)),
+          table_return));
+      return base::OkStatus();
+    }
+
+    case Ancestor::kStackProfileCallsite: {
+      RETURN_IF_ERROR(BuildAncestorsTable(
           /* constraint_id = */ start_id,
           context_->storage->stack_profile_callsite_table(),
-          /* starting_id = */ CallsiteId(static_cast<uint32_t>(start_id)));
-    case Ancestor::kSliceByStack:
+          /* starting_id = */ CallsiteId(static_cast<uint32_t>(start_id)),
+          table_return));
+      return base::OkStatus();
+    }
+
+    case Ancestor::kSliceByStack: {
       // Find the all slice ids that have the stack id and find all the
       // ancestors of the slice ids.
       const auto& slice_table = context_->storage->slice_table();
@@ -148,10 +160,12 @@
         }
       }
 
-      return std::unique_ptr<Table>(new Table(ExtendTableWithStartId(
+      table_return.reset(new Table(ExtendTableWithStartId(
           slice_table.Apply(std::move(result)), start_id)));
+      return base::OkStatus();
+    }
   }
-  return nullptr;
+  return base::ErrStatus("unknown AncestorGenerator type");
 }
 
 Table::Schema AncestorGenerator::CreateSchema() {
@@ -193,7 +207,11 @@
 base::Optional<RowMap> AncestorGenerator::GetAncestorSlices(
     const tables::SliceTable& slices,
     SliceId slice_id) {
-  return BuildAncestorsRowMap(slices, slice_id);
+  RowMap ret;
+  auto status = BuildAncestorsRowMap(slices, slice_id, ret);
+  if (!status.ok())
+    return base::nullopt;
+  return std::move(ret);  // -Wreturn-std-move-in-c++11
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/dynamic/ancestor_generator.h b/src/trace_processor/dynamic/ancestor_generator.h
index 48947ac..00679be 100644
--- a/src/trace_processor/dynamic/ancestor_generator.h
+++ b/src/trace_processor/dynamic/ancestor_generator.h
@@ -46,10 +46,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
   // 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 1032fd7..8dbefa1 100644
--- a/src/trace_processor/dynamic/connected_flow_generator.cc
+++ b/src/trace_processor/dynamic/connected_flow_generator.cc
@@ -34,7 +34,7 @@
 
 ConnectedFlowGenerator::~ConnectedFlowGenerator() = default;
 
-util::Status ConnectedFlowGenerator::ValidateConstraints(
+base::Status ConnectedFlowGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   const auto& cs = qc.constraints();
 
@@ -47,8 +47,8 @@
       std::find_if(cs.begin(), cs.end(), flow_id_fn) != cs.end();
 
   return has_flow_id_cs
-             ? util::OkStatus()
-             : util::ErrStatus("Failed to find required constraints");
+             ? base::OkStatus()
+             : base::ErrStatus("Failed to find required constraints");
 }
 
 namespace {
@@ -196,24 +196,27 @@
 
 }  // namespace
 
-std::unique_ptr<Table> ConnectedFlowGenerator::ComputeTable(
+base::Status ConnectedFlowGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   const auto& flow = context_->storage->flow_table();
   const auto& slice = context_->storage->slice_table();
 
   auto it = std::find_if(cs.begin(), cs.end(), [&flow](const Constraint& c) {
     return c.col_idx == flow.GetColumnCount() && c.op == FilterOp::kEq;
   });
-
   PERFETTO_DCHECK(it != cs.end());
+  if (it == cs.end() || it->value.type != SqlValue::Type::kLong) {
+    return base::ErrStatus("invalid start_id");
+  }
 
   SliceId start_id{static_cast<uint32_t>(it->value.AsLong())};
 
   if (!slice.id().IndexOf(start_id)) {
-    PERFETTO_ELOG("Given slice id is invalid (ConnectedFlowGenerator)");
-    return nullptr;
+    return base::ErrStatus("invalid slice id %" PRIu32 "",
+                           static_cast<uint32_t>(start_id.value));
   }
 
   BFS bfs(context_);
@@ -241,11 +244,12 @@
     start_ids->Append(start_id.value);
   }
 
-  return std::unique_ptr<Table>(
+  table_return.reset(
       new Table(flow.Apply(RowMap(std::move(result_rows)))
                     .ExtendWithColumn("start_id", std::move(start_ids),
                                       TypedColumn<uint32_t>::default_flags() |
                                           TypedColumn<uint32_t>::kHidden)));
+  return base::OkStatus();
 }
 
 Table::Schema ConnectedFlowGenerator::CreateSchema() {
diff --git a/src/trace_processor/dynamic/connected_flow_generator.h b/src/trace_processor/dynamic/connected_flow_generator.h
index b2117af..0b8661b 100644
--- a/src/trace_processor/dynamic/connected_flow_generator.h
+++ b/src/trace_processor/dynamic/connected_flow_generator.h
@@ -53,10 +53,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
  private:
   Mode mode_;
diff --git a/src/trace_processor/dynamic/descendant_generator.cc b/src/trace_processor/dynamic/descendant_generator.cc
index ebec3f3..c0df1cd 100644
--- a/src/trace_processor/dynamic/descendant_generator.cc
+++ b/src/trace_processor/dynamic/descendant_generator.cc
@@ -20,6 +20,7 @@
 #include <set>
 
 #include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/status_macros.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -40,40 +41,38 @@
       TypedColumn<uint32_t>::default_flags() | TypedColumn<uint32_t>::kHidden);
 }
 
-base::Optional<RowMap> BuildDescendantsRowMap(const tables::SliceTable& slices,
-                                              SliceId starting_id) {
+base::Status BuildDescendantsRowMap(const tables::SliceTable& slices,
+                                    SliceId starting_id,
+                                    RowMap& rowmap_return) {
   auto start_row = slices.id().IndexOf(starting_id);
   // The query gave an invalid ID that doesn't exist in the slice table.
   if (!start_row) {
-    // TODO(lalitm): Ideally this should result in an error, or be filtered out
-    // during ValidateConstraints so we can just dereference |start_row|
-    // directly. However ValidateConstraints doesn't know the value we're
-    // filtering for so can't ensure it exists. For now we return a nullptr
-    // which will cause the query to surface an error with the message "SQL
-    // error: constraint failed".
-    return base::nullopt;
+    return base::ErrStatus("no row with id %" PRIu32 "",
+                           static_cast<uint32_t>(starting_id.value));
   }
 
   // All nested descendents must be on the same track, with a ts between
   // |start_id.ts| and |start_id.ts| + |start_id.dur|, and who's depth is larger
   // then |start_row|'s. So we just use Filter to select all relevant slices.
-  return slices.FilterToRowMap(
+  rowmap_return = slices.FilterToRowMap(
       {slices.ts().ge(slices.ts()[*start_row]),
        slices.ts().le(slices.ts()[*start_row] + slices.dur()[*start_row]),
        slices.track_id().eq(slices.track_id()[*start_row].value),
        slices.depth().gt(slices.depth()[*start_row])});
+  return base::OkStatus();
 }
 
-std::unique_ptr<Table> BuildDescendantsTable(int64_t constraint_value,
-                                             const tables::SliceTable& slices,
-                                             SliceId starting_id) {
+base::Status BuildDescendantsTable(int64_t constraint_value,
+                                   const tables::SliceTable& slices,
+                                   SliceId starting_id,
+                                   std::unique_ptr<Table>& table_return) {
   // Build up all the children row ids.
-  auto descendants = BuildDescendantsRowMap(slices, starting_id);
-  if (!descendants) {
-    return nullptr;
-  }
-  return std::unique_ptr<Table>(new Table(ExtendTableWithStartId(
-      slices.Apply(std::move(*descendants)), constraint_value)));
+  RowMap descendants;
+  RETURN_IF_ERROR(BuildDescendantsRowMap(slices, starting_id, descendants));
+
+  table_return.reset(new Table(ExtendTableWithStartId(
+      slices.Apply(std::move(descendants)), constraint_value)));
+  return base::OkStatus();
 }
 }  // namespace
 
@@ -81,7 +80,7 @@
                                          TraceProcessorContext* context)
     : type_(type), context_(context) {}
 
-util::Status DescendantGenerator::ValidateConstraints(
+base::Status DescendantGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   const auto& cs = qc.constraints();
 
@@ -90,28 +89,38 @@
     return c.column == column && c.op == SQLITE_INDEX_CONSTRAINT_EQ;
   };
   bool has_id_cs = std::find_if(cs.begin(), cs.end(), id_fn) != cs.end();
-  return has_id_cs ? util::OkStatus()
-                   : util::ErrStatus("Failed to find required constraints");
+  return has_id_cs ? base::OkStatus()
+                   : base::ErrStatus("Failed to find required constraints");
 }
 
-std::unique_ptr<Table> DescendantGenerator::ComputeTable(
+base::Status DescendantGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   const auto& slices = context_->storage->slice_table();
 
   uint32_t column = GetConstraintColumnIndex(context_);
-  auto it = std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
-    return c.col_idx == column && c.op == FilterOp::kEq;
-  });
-  PERFETTO_DCHECK(it != cs.end());
-  auto start_id = it->value.AsLong();
+  auto constraint_it =
+      std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
+        return c.col_idx == column && c.op == FilterOp::kEq;
+      });
+  PERFETTO_DCHECK(constraint_it != cs.end());
+  if (constraint_it == cs.end() ||
+      constraint_it->value.type != SqlValue::Type::kLong) {
+    return base::ErrStatus("invalid start_id");
+  }
+  auto start_id = constraint_it->value.AsLong();
 
   switch (type_) {
-    case Descendant::kSlice:
-      return BuildDescendantsTable(start_id, slices,
-                                   SliceId(static_cast<uint32_t>(start_id)));
-    case Descendant::kSliceByStack:
+    case Descendant::kSlice: {
+      RETURN_IF_ERROR(BuildDescendantsTable(
+          start_id, slices, SliceId(static_cast<uint32_t>(start_id)),
+          table_return));
+      return base::OkStatus();
+    }
+
+    case Descendant::kSliceByStack: {
       auto result = RowMap();
       auto slice_ids = slices.FilterToRowMap({slices.stack_id().eq(start_id)});
 
@@ -124,10 +133,12 @@
         }
       }
 
-      return std::unique_ptr<Table>(new Table(
+      table_return.reset(new Table(
           ExtendTableWithStartId(slices.Apply(std::move(result)), start_id)));
+      return base::OkStatus();
+    }
   }
-  return nullptr;
+  return base::ErrStatus("unknown DescendantGenerator type");
 }
 
 Table::Schema DescendantGenerator::CreateSchema() {
@@ -156,7 +167,11 @@
 base::Optional<RowMap> DescendantGenerator::GetDescendantSlices(
     const tables::SliceTable& slices,
     SliceId slice_id) {
-  return BuildDescendantsRowMap(slices, slice_id);
+  RowMap ret;
+  auto status = BuildDescendantsRowMap(slices, slice_id, ret);
+  if (!status.ok())
+    return base::nullopt;
+  return std::move(ret);  // -Wreturn-std-move-in-c++11
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/dynamic/descendant_generator.h b/src/trace_processor/dynamic/descendant_generator.h
index 216b7e1..318fe7d 100644
--- a/src/trace_processor/dynamic/descendant_generator.h
+++ b/src/trace_processor/dynamic/descendant_generator.h
@@ -41,10 +41,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
   // 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 500d561..ea8a70c 100644
--- a/src/trace_processor/dynamic/describe_slice_generator.cc
+++ b/src/trace_processor/dynamic/describe_slice_generator.cc
@@ -18,6 +18,7 @@
 
 #include "src/trace_processor/analysis/describe_slice.h"
 #include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/status_macros.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -34,10 +35,9 @@
   };
   auto slice_id_it = std::find_if(cs.begin(), cs.end(), slice_id_fn);
 
-  // We should always have valid iterators here because BestIndex should only
-  // allow the constraint set to be chosen when we have an equality constraint
-  // on both ts and upid.
   PERFETTO_CHECK(slice_id_it != cs.end());
+  // TODO(rsavitski): consider checking type of the SqlValue, as erroneous
+  // queries that pass a null here (or otherwise unexpected type) will crash.
 
   uint32_t slice_id_value = static_cast<uint32_t>(slice_id_it->value.AsLong());
   return DescribeSliceGenerator::InputValues{slice_id_value};
@@ -50,7 +50,7 @@
 
 DescribeSliceGenerator::~DescribeSliceGenerator() = default;
 
-util::Status DescribeSliceGenerator::ValidateConstraints(
+base::Status DescribeSliceGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   using T = tables::DescribeSliceTable;
 
@@ -64,21 +64,21 @@
       std::find_if(cs.begin(), cs.end(), slice_id_fn) != cs.end();
 
   return has_slice_id_cs
-             ? util::OkStatus()
-             : util::ErrStatus("Failed to find required constraints");
+             ? base::OkStatus()
+             : base::ErrStatus("Failed to find required constraints");
 }
 
-std::unique_ptr<Table> DescribeSliceGenerator::ComputeTable(
+base::Status DescribeSliceGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   auto input = GetDescribeSliceInputValues(cs);
   const auto& slices = context_->storage->slice_table();
 
   base::Optional<SliceDescription> opt_desc;
-  auto status = DescribeSlice(slices, SliceId{input.slice_id_value}, &opt_desc);
-  if (!status.ok())
-    return nullptr;
+  RETURN_IF_ERROR(
+      DescribeSlice(slices, SliceId{input.slice_id_value}, &opt_desc));
 
   auto* pool = context_->storage->mutable_string_pool();
   std::unique_ptr<tables::DescribeSliceTable> table(
@@ -93,9 +93,8 @@
     row.slice_id = input.slice_id_value;
     table->Insert(row);
   }
-  // We need to explicitly std::move as clang complains about a bug in old
-  // compilers otherwise (-Wreturn-std-move-in-c++11).
-  return std::move(table);
+  table_return = std::move(table);
+  return base::OkStatus();
 }
 
 Table::Schema DescribeSliceGenerator::CreateSchema() {
diff --git a/src/trace_processor/dynamic/describe_slice_generator.h b/src/trace_processor/dynamic/describe_slice_generator.h
index fc7d991..fa8a87f 100644
--- a/src/trace_processor/dynamic/describe_slice_generator.h
+++ b/src/trace_processor/dynamic/describe_slice_generator.h
@@ -41,10 +41,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
  private:
   TraceProcessorContext* context_ = nullptr;
diff --git a/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc b/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
index 617c658..45cf596 100644
--- a/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
+++ b/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
@@ -104,7 +104,7 @@
   return schema;
 }
 
-util::Status ExperimentalAnnotatedStackGenerator::ValidateConstraints(
+base::Status ExperimentalAnnotatedStackGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   const auto& cs = qc.constraints();
   int column = static_cast<int>(GetConstraintColumnIndex(context_));
@@ -113,14 +113,15 @@
     return c.column == column && c.op == SQLITE_INDEX_CONSTRAINT_EQ;
   };
   bool has_id_cs = std::find_if(cs.begin(), cs.end(), id_fn) != cs.end();
-  return has_id_cs ? util::OkStatus()
-                   : util::ErrStatus("Failed to find required constraints");
+  return has_id_cs ? base::OkStatus()
+                   : base::ErrStatus("Failed to find required constraints");
 }
 
-std::unique_ptr<Table> ExperimentalAnnotatedStackGenerator::ComputeTable(
+base::Status ExperimentalAnnotatedStackGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   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();
@@ -133,12 +134,17 @@
         return c.col_idx == constraint_col && c.op == FilterOp::kEq;
       });
   PERFETTO_DCHECK(constraint_it != cs.end());
+  if (constraint_it == cs.end() ||
+      constraint_it->value.type != SqlValue::Type::kLong) {
+    return base::ErrStatus("invalid input callsite id");
+  }
 
-  auto start_id = static_cast<uint32_t>(constraint_it->value.AsLong());
+  uint32_t start_id = static_cast<uint32_t>(constraint_it->value.AsLong());
   base::Optional<uint32_t> start_row =
       cs_table.id().IndexOf(CallsiteId(start_id));
-  if (!start_row)
-    return nullptr;
+  if (!start_row) {
+    return base::ErrStatus("callsite with id %" PRIu32 " not found", start_id);
+  }
 
   // Iteratively walk the parent_id chain to construct the list of callstack
   // entries, each pointing at a frame.
@@ -264,13 +270,14 @@
   for (uint32_t i = 0; i < base_rowmap.size(); i++)
     start_id_vals->Append(start_id);
 
-  return std::unique_ptr<Table>(new Table(
+  table_return.reset(new Table(
       cs_table.Apply(std::move(base_rowmap))
           .ExtendWithColumn("annotation", std::move(annotation_vals),
                             TypedColumn<StringPool::Id>::default_flags())
           .ExtendWithColumn("start_id", std::move(start_id_vals),
                             TypedColumn<uint32_t>::default_flags() |
                                 TypedColumn<uint32_t>::kHidden)));
+  return base::OkStatus();
 }
 
 uint32_t ExperimentalAnnotatedStackGenerator::EstimateRowCount() {
diff --git a/src/trace_processor/dynamic/experimental_annotated_stack_generator.h b/src/trace_processor/dynamic/experimental_annotated_stack_generator.h
index f61c166..8b13536 100644
--- a/src/trace_processor/dynamic/experimental_annotated_stack_generator.h
+++ b/src/trace_processor/dynamic/experimental_annotated_stack_generator.h
@@ -38,10 +38,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
  private:
   TraceProcessorContext* context_ = nullptr;
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator.cc b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
index 530fe39..ce60d47 100644
--- a/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
@@ -43,15 +43,16 @@
   return counter_table_->row_count();
 }
 
-util::Status ExperimentalCounterDurGenerator::ValidateConstraints(
+base::Status ExperimentalCounterDurGenerator::ValidateConstraints(
     const QueryConstraints&) {
-  return util::OkStatus();
+  return base::OkStatus();
 }
 
-std::unique_ptr<Table> ExperimentalCounterDurGenerator::ComputeTable(
+base::Status ExperimentalCounterDurGenerator::ComputeTable(
     const std::vector<Constraint>&,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   if (!dur_column_) {
     dur_column_.reset(
         new NullableVector<int64_t>(ComputeDurColumn(*counter_table_)));
@@ -65,7 +66,8 @@
                 .ExtendWithColumn("delta", std::move(delta_column_.get()),
                                   TypedColumn<int64_t>::default_flags());
 
-  return std::unique_ptr<Table>(new Table(t.Copy()));
+  table_return.reset(new Table(t.Copy()));
+  return base::OkStatus();
 }
 
 // static
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator.h b/src/trace_processor/dynamic/experimental_counter_dur_generator.h
index 4ec9f24..de7071e 100644
--- a/src/trace_processor/dynamic/experimental_counter_dur_generator.h
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator.h
@@ -33,10 +33,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
-                                      const std::vector<Order>&,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
   // 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 1475eab..bcecec1 100644
--- a/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
+++ b/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
@@ -285,7 +285,7 @@
 // For filtering, this method uses the same constraints as
 // ExperimentalFlamegraphGenerator::GetFlamegraphInputValues and should
 // therefore be kept in sync.
-util::Status ExperimentalFlamegraphGenerator::ValidateConstraints(
+base::Status ExperimentalFlamegraphGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   using T = tables::ExperimentalFlamegraphNodesTable;
 
@@ -318,14 +318,15 @@
       std::find_if(cs.begin(), cs.end(), profile_type_fn) != cs.end();
 
   return has_ts_cs && (has_upid_cs || has_upid_group_cs) && has_profile_type_cs
-             ? util::OkStatus()
-             : util::ErrStatus("Failed to find required constraints");
+             ? base::OkStatus()
+             : base::ErrStatus("Failed to find required constraints");
 }
 
-std::unique_ptr<Table> ExperimentalFlamegraphGenerator::ComputeTable(
+base::Status ExperimentalFlamegraphGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   // Get the input column values and compute the flamegraph using them.
   auto values = GetFlamegraphInputValues(cs);
 
@@ -352,9 +353,8 @@
       table->mutable_focus_str()->Set(i, focus_id);
     }
   }
-  // We need to explicitly std::move as clang complains about a bug in old
-  // compilers otherwise (-Wreturn-std-move-in-c++11).
-  return std::move(table);
+  table_return = std::move(table);
+  return base::OkStatus();
 }
 
 Table::Schema ExperimentalFlamegraphGenerator::CreateSchema() {
diff --git a/src/trace_processor/dynamic/experimental_flamegraph_generator.h b/src/trace_processor/dynamic/experimental_flamegraph_generator.h
index c8531ad..4ec3cbc 100644
--- a/src/trace_processor/dynamic/experimental_flamegraph_generator.h
+++ b/src/trace_processor/dynamic/experimental_flamegraph_generator.h
@@ -47,10 +47,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
  private:
   TraceProcessorContext* context_ = nullptr;
diff --git a/src/trace_processor/dynamic/experimental_flat_slice_generator.cc b/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
index 4e8d697..48271b5 100644
--- a/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
+++ b/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
@@ -28,7 +28,7 @@
     TraceProcessorContext* context)
     : context_(context) {}
 
-util::Status ExperimentalFlatSliceGenerator::ValidateConstraints(
+base::Status ExperimentalFlatSliceGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   using CI = tables::ExperimentalFlatSliceTable::ColumnIndex;
   bool has_start_bound = false;
@@ -40,14 +40,15 @@
                      c.op == SQLITE_INDEX_CONSTRAINT_EQ;
   }
   return has_start_bound && has_end_bound
-             ? util::OkStatus()
-             : util::ErrStatus("Failed to find required constraints");
+             ? base::OkStatus()
+             : base::ErrStatus("Failed to find required constraints");
 }
 
-std::unique_ptr<Table> ExperimentalFlatSliceGenerator::ComputeTable(
+base::Status ExperimentalFlatSliceGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   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) &&
@@ -57,11 +58,14 @@
     return c.col_idx == static_cast<uint32_t>(CI::end_bound) &&
            c.op == FilterOp::kEq;
   });
+  // TODO(rsavitski): consider checking the values' types (in case of erroneous
+  // queries passing e.g. null).
   int64_t start_bound = start_it->value.AsLong();
   int64_t end_bound = end_it->value.AsLong();
-  return ComputeFlatSliceTable(context_->storage->slice_table(),
-                               context_->storage->mutable_string_pool(),
-                               start_bound, end_bound);
+  table_return = ComputeFlatSliceTable(context_->storage->slice_table(),
+                                       context_->storage->mutable_string_pool(),
+                                       start_bound, end_bound);
+  return base::OkStatus();
 }
 
 std::unique_ptr<tables::ExperimentalFlatSliceTable>
diff --git a/src/trace_processor/dynamic/experimental_flat_slice_generator.h b/src/trace_processor/dynamic/experimental_flat_slice_generator.h
index 480e477..0083457 100644
--- a/src/trace_processor/dynamic/experimental_flat_slice_generator.h
+++ b/src/trace_processor/dynamic/experimental_flat_slice_generator.h
@@ -60,10 +60,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
   // 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 60d325f..f0cd061 100644
--- a/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
+++ b/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
@@ -41,21 +41,24 @@
   return sched_slice_table_->row_count();
 }
 
-util::Status ExperimentalSchedUpidGenerator::ValidateConstraints(
+base::Status ExperimentalSchedUpidGenerator::ValidateConstraints(
     const QueryConstraints&) {
-  return util::OkStatus();
+  return base::OkStatus();
 }
 
-std::unique_ptr<Table> ExperimentalSchedUpidGenerator::ComputeTable(
+base::Status ExperimentalSchedUpidGenerator::ComputeTable(
     const std::vector<Constraint>&,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   if (!upid_column_) {
     upid_column_.reset(new NullableVector<uint32_t>(ComputeUpidColumn()));
   }
-  return std::unique_ptr<Table>(new Table(sched_slice_table_->ExtendWithColumn(
-      "upid", upid_column_.get(),
-      TypedColumn<base::Optional<uint32_t>>::default_flags())));
+  table_return =
+      std::unique_ptr<Table>(new Table(sched_slice_table_->ExtendWithColumn(
+          "upid", upid_column_.get(),
+          TypedColumn<base::Optional<uint32_t>>::default_flags())));
+  return base::OkStatus();
 }
 
 NullableVector<uint32_t> ExperimentalSchedUpidGenerator::ComputeUpidColumn() {
diff --git a/src/trace_processor/dynamic/experimental_sched_upid_generator.h b/src/trace_processor/dynamic/experimental_sched_upid_generator.h
index e144eea..569e212 100644
--- a/src/trace_processor/dynamic/experimental_sched_upid_generator.h
+++ b/src/trace_processor/dynamic/experimental_sched_upid_generator.h
@@ -35,10 +35,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
-                                      const std::vector<Order>&,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
  private:
   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 9b842bb..c6153af 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
@@ -62,21 +62,22 @@
   return slice_table_->row_count();
 }
 
-util::Status ExperimentalSliceLayoutGenerator::ValidateConstraints(
+base::Status ExperimentalSliceLayoutGenerator::ValidateConstraints(
     const QueryConstraints& cs) {
   for (const auto& c : cs.constraints()) {
     if (c.column == kFilterTrackIdsColumnIndex && sqlite_utils::IsOpEq(c.op)) {
-      return util::OkStatus();
+      return base::OkStatus();
     }
   }
-  return util::ErrStatus(
+  return base::ErrStatus(
       "experimental_slice_layout must have filter_track_ids constraint");
 }
 
-std::unique_ptr<Table> ExperimentalSliceLayoutGenerator::ComputeTable(
+base::Status ExperimentalSliceLayoutGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   std::set<TrackId> selected_tracks;
   std::string filter_string = "";
   for (const auto& c : cs) {
@@ -100,7 +101,8 @@
   // Try and find the table in the cache.
   auto it = layout_table_cache_.find(filter_id);
   if (it != layout_table_cache_.end()) {
-    return std::unique_ptr<Table>(new Table(it->second.Copy()));
+    table_return.reset(new Table(it->second.Copy()));
+    return base::OkStatus();
   }
 
   // Find all the slices for the tracks we want to filter and create a RowMap
@@ -125,7 +127,9 @@
   // Compute the table and add it to the cache for future use.
   Table layout_table = ComputeLayoutTable(filtered_table, filter_id);
   auto res = layout_table_cache_.emplace(filter_id, std::move(layout_table));
-  return std::unique_ptr<Table>(new Table(res.first->second.Copy()));
+
+  table_return.reset(new Table(res.first->second.Copy()));
+  return base::OkStatus();
 }
 
 // Build up a table of slice id -> root slice id by observing each
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator.h b/src/trace_processor/dynamic/experimental_slice_layout_generator.h
index b2d731e..cc27bb2 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator.h
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator.h
@@ -38,10 +38,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>&,
-                                      const std::vector<Order>&,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
  private:
   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 efb8ae8..81fdb7d 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
@@ -104,9 +104,11 @@
 
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
-  std::unique_ptr<Table> table = gen.ComputeTable(
+  std::unique_ptr<Table> table;
+  auto status = gen.ComputeTable(
       {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {},
-      BitVector());
+      BitVector(), table);
+  EXPECT_TRUE(status.ok());
   ExpectOutput(*table, R"(
  #####
 )");
@@ -123,9 +125,11 @@
 
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
-  std::unique_ptr<Table> table = gen.ComputeTable(
+  std::unique_ptr<Table> table;
+  auto status = gen.ComputeTable(
       {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {},
-      BitVector());
+      BitVector(), table);
+  EXPECT_TRUE(status.ok());
   ExpectOutput(*table, R"(
  #####
  #####
@@ -147,9 +151,11 @@
 
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
-  std::unique_ptr<Table> table = gen.ComputeTable(
+  std::unique_ptr<Table> table;
+  auto status = gen.ComputeTable(
       {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {},
-      BitVector());
+      BitVector(), table);
+  EXPECT_TRUE(status.ok());
   ExpectOutput(*table, R"(
  #####
  ####
@@ -178,9 +184,11 @@
 
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
-  std::unique_ptr<Table> table = gen.ComputeTable(
+  std::unique_ptr<Table> table;
+  auto status = gen.ComputeTable(
       {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {},
-      BitVector());
+      BitVector(), table);
+  EXPECT_TRUE(status.ok());
   ExpectOutput(*table, R"(
  ####
  ##
@@ -214,9 +222,11 @@
 
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
 
-  std::unique_ptr<Table> table = gen.ComputeTable(
+  std::unique_ptr<Table> table;
+  auto status = gen.ComputeTable(
       {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {},
-      BitVector());
+      BitVector(), table);
+  EXPECT_TRUE(status.ok());
   ExpectOutput(*table, R"(
 #### ####
 ##   ##
@@ -247,9 +257,11 @@
   base::ignore_result(q);
 
   ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
-  std::unique_ptr<Table> table = gen.ComputeTable(
+  std::unique_ptr<Table> table;
+  auto status = gen.ComputeTable(
       {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {},
-      BitVector());
+      BitVector(), table);
+  EXPECT_TRUE(status.ok());
   ExpectOutput(*table, R"(
 ####
 ##
diff --git a/src/trace_processor/dynamic/thread_state_generator.cc b/src/trace_processor/dynamic/thread_state_generator.cc
index f68f127..7790a1f 100644
--- a/src/trace_processor/dynamic/thread_state_generator.cc
+++ b/src/trace_processor/dynamic/thread_state_generator.cc
@@ -31,15 +31,16 @@
 
 ThreadStateGenerator::~ThreadStateGenerator() = default;
 
-util::Status ThreadStateGenerator::ValidateConstraints(
+base::Status ThreadStateGenerator::ValidateConstraints(
     const QueryConstraints&) {
-  return util::OkStatus();
+  return base::OkStatus();
 }
 
-std::unique_ptr<Table> ThreadStateGenerator::ComputeTable(
+base::Status ThreadStateGenerator::ComputeTable(
     const std::vector<Constraint>&,
     const std::vector<Order>&,
-    const BitVector&) {
+    const BitVector&,
+    std::unique_ptr<Table>& table_return) {
   if (!unsorted_thread_state_table_) {
     int64_t trace_end_ts =
         context_->storage->GetTraceTimestampBoundsNs().second;
@@ -53,8 +54,11 @@
     sorted_thread_state_table_ = unsorted_thread_state_table_->Sort(
         {unsorted_thread_state_table_->ts().ascending()});
   }
+  // TODO(rsavitski): return base::ErrStatus instead?
   PERFETTO_CHECK(sorted_thread_state_table_);
-  return std::unique_ptr<Table>(new Table(sorted_thread_state_table_->Copy()));
+  table_return =
+      std::unique_ptr<Table>(new Table(sorted_thread_state_table_->Copy()));
+  return base::OkStatus();
 }
 
 std::unique_ptr<tables::ThreadStateTable>
@@ -311,7 +315,7 @@
   ThreadSchedInfo& info = state_map[utid];
 
   base::Optional<Variadic> opt_value;
-  util::Status status =
+  base::Status status =
       context_->storage->ExtractArg(arg_set_id, "io_wait", &opt_value);
 
   // We can't do anything better than ignoring any errors here.
diff --git a/src/trace_processor/dynamic/thread_state_generator.h b/src/trace_processor/dynamic/thread_state_generator.h
index db97925..8e111de 100644
--- a/src/trace_processor/dynamic/thread_state_generator.h
+++ b/src/trace_processor/dynamic/thread_state_generator.h
@@ -38,10 +38,11 @@
   Table::Schema CreateSchema() override;
   std::string TableName() override;
   uint32_t EstimateRowCount() override;
-  util::Status ValidateConstraints(const QueryConstraints&) override;
-  std::unique_ptr<Table> ComputeTable(const std::vector<Constraint>& cs,
-                                      const std::vector<Order>& ob,
-                                      const BitVector& cols_used) override;
+  base::Status ValidateConstraints(const QueryConstraints&) override;
+  base::Status ComputeTable(const std::vector<Constraint>& cs,
+                            const std::vector<Order>& ob,
+                            const BitVector& cols_used,
+                            std::unique_ptr<Table>& table_return) override;
 
   // Visible for testing.
   std::unique_ptr<tables::ThreadStateTable> ComputeThreadStateTable(
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index 61b2567..f679ca9 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -249,9 +249,6 @@
       ts, static_cast<double>(sample.timebase_count()),
       sampling_stream.timebase_track_id);
 
-  // TODO(rsavitski): empty callsite is not an error for counter-only samples.
-  // But consider identifying sequences which *should* have a callstack in every
-  // sample, as an invalid stack there is a bug.
   SequenceStackProfileTracker& stack_tracker =
       sequence_state->state()->sequence_stack_profile_tracker();
   ProfilePacketInternLookup intern_lookup(sequence_state);
@@ -259,6 +256,27 @@
   base::Optional<CallsiteId> cs_id =
       stack_tracker.FindOrInsertCallstack(callstack_iid, &intern_lookup);
 
+  // A failed lookup of the interned callstack can mean either:
+  // (a) This is a counter-only profile without callstacks. Due to an
+  //     implementation quirk, these packets still set callstack_iid
+  //     corresponding to a callstack with no frames. To reliably identify this
+  //     case (without resorting to config parsing) we further need to rely on
+  //     the fact that the implementation (callstack_trie.h) always assigns this
+  //     callstack the id "1". Such callstacks should not occur outside of
+  //     counter-only profiles, as there should always be at least a synthetic
+  //     error frame if the unwinding completely failed.
+  // (b) This is a ring-buffer profile where some of the referenced internings
+  //     have been overwritten, and the build predates perf_sample_defaults and
+  //     SEQ_NEEDS_INCREMENTAL_STATE sequence flag in perf_sample packets.
+  //     Such packets should be discarded.
+  if (!cs_id && callstack_iid != 1) {
+    PERFETTO_DLOG("Discarding perf_sample since callstack_iid [%" PRIu64
+                  "] references a missing/partially lost interning according "
+                  "to stack_profile_tracker",
+                  callstack_iid);
+    return;
+  }
+
   UniqueTid utid =
       context_->process_tracker->UpdateThread(sample.tid(), sample.pid());
 
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 48b5b41..691f690 100644
--- a/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
+++ b/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
@@ -374,6 +374,10 @@
   WHERE (state = 'R' OR state = 'R+')
   GROUP BY frame_number
   HAVING SUM(dur) > 8000000
+  AND SUM(dur) > (
+    SELECT 0.4 * dur_main_thread
+    FROM android_sysui_cuj_frames fs
+    WHERE fs.frame_number = android_sysui_cuj_main_thread_state.frame_number)
 
   UNION ALL
   SELECT
@@ -394,6 +398,10 @@
   WHERE (state = 'R' OR state = 'R+')
   GROUP BY frame_number
   HAVING SUM(dur) > 8000000
+  AND SUM(dur) > (
+    SELECT 0.4 * dur_render_thread
+    FROM android_sysui_cuj_frames fs
+    WHERE fs.frame_number = android_sysui_cuj_render_thread_state.frame_number)
 
   UNION ALL
   SELECT
diff --git a/src/trace_processor/metrics/sql/android/process_metadata.sql b/src/trace_processor/metrics/sql/android/process_metadata.sql
index 6873bb2..7167e12 100644
--- a/src/trace_processor/metrics/sql/android/process_metadata.sql
+++ b/src/trace_processor/metrics/sql/android/process_metadata.sql
@@ -26,7 +26,8 @@
 CREATE TABLE process_metadata_table AS
 SELECT
   process.upid,
-  -- TODO(b/169226092) remove this workaround
+  -- workaround for b/169226092: the bug has been fixed it Android T, but
+  -- we support ingesting traces from older Android versions.
   CASE
       -- cmdline gets rewritten after fork, if these are still there we must
       -- have seen a racy capture.
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index b6897e1..1146a64 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -123,7 +123,7 @@
 
   // 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(
+  base::Status status = generator->ValidateConstraints(
       QueryConstraints(std::numeric_limits<uint64_t>::max()));
   bool requires_args = !status.ok();
 
@@ -133,9 +133,9 @@
                                                 false, requires_args);
 }
 
-util::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
+base::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
   *schema = ComputeSchema(schema_, name().c_str());
-  return util::OkStatus();
+  return base::OkStatus();
 }
 
 SqliteTable::Schema DbSqliteTable::ComputeSchema(const Table::Schema& schema,
@@ -169,7 +169,7 @@
       BestIndex(schema_, static_table_->row_count(), qc, info);
       break;
     case TableComputation::kDynamic:
-      util::Status status = generator_->ValidateConstraints(qc);
+      base::Status status = generator_->ValidateConstraints(qc);
       if (!status.ok())
         return SQLITE_CONSTRAINT;
       BestIndex(schema_, generator_->EstimateRowCount(), qc, info);
@@ -451,13 +451,21 @@
       });
       // If we have a dynamically created table, regenerate the table based on
       // the new constraints.
+      std::unique_ptr<Table> computed_table;
       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_)
+      auto status = db_sqlite_table_->generator_->ComputeTable(
+          constraints_, orders_, cols_used_bv, computed_table);
+
+      if (!status.ok()) {
+        auto* sqlite_err = sqlite3_mprintf(
+            "%s: %s", db_sqlite_table_->name().c_str(), status.c_message());
+        db_sqlite_table_->SetErrorMessage(sqlite_err);
         return SQLITE_CONSTRAINT;
+      }
+      PERFETTO_DCHECK(computed_table);
+      dynamic_table_ = std::move(computed_table);
+      upstream_table_ = dynamic_table_.get();
       break;
     }
   }
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 1e713b2..e1a35b8 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -57,17 +57,19 @@
 
     // Checks that the constraint set is valid.
     //
-    // Returning util::OkStatus means that the required constraints are present
+    // Returning base::OkStatus means that the required constraints are present
     // in |qc| for dynamically computing the table (e.g. any required
     // constraints on hidden columns for table-valued functions are present).
-    virtual util::Status ValidateConstraints(const QueryConstraints& qc) = 0;
+    virtual base::Status ValidateConstraints(const QueryConstraints& qc) = 0;
 
     // Dynamically computes the table given the constraints and order by
     // vectors.
-    virtual std::unique_ptr<Table> ComputeTable(
-        const std::vector<Constraint>& cs,
-        const std::vector<Order>& ob,
-        const BitVector& cols_used) = 0;
+    // The table is returned via |table_return|. There are no guarantees on
+    // its value if the method returns a non-ok status.
+    virtual base::Status ComputeTable(const std::vector<Constraint>& cs,
+                                      const std::vector<Order>& ob,
+                                      const BitVector& cols_used,
+                                      std::unique_ptr<Table>& table_return) = 0;
   };
 
   class Cursor : public SqliteTable::Cursor {
@@ -166,7 +168,7 @@
   virtual ~DbSqliteTable() override;
 
   // Table implementation.
-  util::Status Init(int,
+  base::Status Init(int,
                     const char* const*,
                     SqliteTable::Schema*) override final;
   std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
diff --git a/src/traced/probes/android_log/android_log_data_source.cc b/src/traced/probes/android_log/android_log_data_source.cc
index 286d138..4d8d309 100644
--- a/src/traced/probes/android_log/android_log_data_source.cc
+++ b/src/traced/probes/android_log/android_log_data_source.cc
@@ -89,6 +89,7 @@
 const ProbesDataSource::Descriptor AndroidLogDataSource::descriptor = {
     /*name*/ "android.log",
     /*flags*/ Descriptor::kFlagsNone,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 AndroidLogDataSource::AndroidLogDataSource(DataSourceConfig ds_config,
diff --git a/src/traced/probes/filesystem/inode_file_data_source.cc b/src/traced/probes/filesystem/inode_file_data_source.cc
index 5362f2c..8185a6e 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.cc
+++ b/src/traced/probes/filesystem/inode_file_data_source.cc
@@ -83,6 +83,7 @@
 const ProbesDataSource::Descriptor InodeFileDataSource::descriptor = {
     /*name*/ "linux.inode_file_map",
     /*flags*/ Descriptor::kFlagsNone,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 void CreateStaticDeviceToInodeMap(
@@ -106,7 +107,7 @@
 }
 
 InodeFileDataSource::InodeFileDataSource(
-    DataSourceConfig ds_config,
+    const DataSourceConfig& ds_config,
     base::TaskRunner* task_runner,
     TracingSessionID session_id,
     std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
diff --git a/src/traced/probes/filesystem/inode_file_data_source.h b/src/traced/probes/filesystem/inode_file_data_source.h
index 54a5d4d..62cc4d7 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.h
+++ b/src/traced/probes/filesystem/inode_file_data_source.h
@@ -56,7 +56,7 @@
   static const ProbesDataSource::Descriptor descriptor;
 
   InodeFileDataSource(
-      DataSourceConfig,
+      const DataSourceConfig&,
       base::TaskRunner*,
       TracingSessionID,
       std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index dcc8ca1..a047684 100644
--- a/src/traced/probes/ftrace/BUILD.gn
+++ b/src/traced/probes/ftrace/BUILD.gn
@@ -113,6 +113,7 @@
     "..:data_source",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
+    "../../../../protos/perfetto/common:zero",
     "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/interned_data:zero",
     "../../../../protos/perfetto/trace/profiling:zero",
diff --git a/src/traced/probes/ftrace/ftrace_data_source.cc b/src/traced/probes/ftrace/ftrace_data_source.cc
index a801e05..5b6ac34 100644
--- a/src/traced/probes/ftrace/ftrace_data_source.cc
+++ b/src/traced/probes/ftrace/ftrace_data_source.cc
@@ -16,19 +16,68 @@
 
 #include "src/traced/probes/ftrace/ftrace_data_source.h"
 
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/subprocess.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/tracing/core/data_source_descriptor.h"
 #include "src/traced/probes/ftrace/cpu_reader.h"
 #include "src/traced/probes/ftrace/ftrace_controller.h"
 
+#include "protos/perfetto/common/ftrace_descriptor.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
 namespace perfetto {
+namespace {
+
+void FillFtraceDataSourceDescriptor(DataSourceDescriptor* dsd) {
+  protozero::HeapBuffered<protos::pbzero::FtraceDescriptor> ftd;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  base::Subprocess p({"/system/bin/atrace", "--list_categories"});
+  p.args.stdin_mode = base::Subprocess::InputMode::kDevNull;
+  p.args.stdout_mode = base::Subprocess::OutputMode::kBuffer;
+  p.args.stderr_mode = base::Subprocess::OutputMode::kBuffer;
+  bool res = p.Call(/*timeout_ms=*/20000);
+  if (res) {
+    for (base::StringSplitter ss(std::move(p.output()), '\n'); ss.Next();) {
+      base::StringView line(ss.cur_token(), ss.cur_token_size());
+      size_t pos = line.find(" - ");
+      if (pos == line.npos) {
+        continue;
+      }
+      base::StringView name = line.substr(0, pos);
+      // Trim initial whitespaces
+      auto it = std::find_if(name.begin(), name.end(),
+                             [](char c) { return c != ' '; });
+      name = name.substr(static_cast<size_t>(it - name.begin()));
+
+      base::StringView desc = line.substr(pos + 3);
+
+      protos::pbzero::FtraceDescriptor::AtraceCategory* cat =
+          ftd->add_atrace_categories();
+      cat->set_name(name.data(), name.size());
+      cat->set_description(desc.data(), desc.size());
+    }
+  } else {
+    PERFETTO_ELOG("Failed to run atrace --list_categories code(%d): %s",
+                  p.returncode(), p.output().c_str());
+  }
+#endif
+
+  dsd->set_ftrace_descriptor_raw(ftd.SerializeAsString());
+}
+
+}  // namespace
 
 // static
 const ProbesDataSource::Descriptor FtraceDataSource::descriptor = {
     /*name*/ "linux.ftrace",
     /*flags*/ Descriptor::kFlagsNone,
+    /*fill_descriptor_func*/ &FillFtraceDataSourceDescriptor,
 };
 
 FtraceDataSource::FtraceDataSource(
diff --git a/src/traced/probes/initial_display_state/initial_display_state_data_source.cc b/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
index 332cbf5..85ea606 100644
--- a/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
+++ b/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
@@ -34,6 +34,7 @@
     InitialDisplayStateDataSource::descriptor = {
         /* name */ "android.polled_state",
         /* flags */ Descriptor::kFlagsNone,
+        /*fill_descriptor_func*/ nullptr,
 };
 
 InitialDisplayStateDataSource::InitialDisplayStateDataSource(
diff --git a/src/traced/probes/metatrace/metatrace_data_source.cc b/src/traced/probes/metatrace/metatrace_data_source.cc
index 1c5a2a7..6849579 100644
--- a/src/traced/probes/metatrace/metatrace_data_source.cc
+++ b/src/traced/probes/metatrace/metatrace_data_source.cc
@@ -32,6 +32,7 @@
 const ProbesDataSource::Descriptor MetatraceDataSource::descriptor = {
     /*name*/ MetatraceWriter::kDataSourceName,
     /*flags*/ Descriptor::kFlagsNone,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 MetatraceDataSource::MetatraceDataSource(base::TaskRunner* task_runner,
diff --git a/src/traced/probes/packages_list/packages_list_data_source.cc b/src/traced/probes/packages_list/packages_list_data_source.cc
index f9919b8..8276f62 100644
--- a/src/traced/probes/packages_list/packages_list_data_source.cc
+++ b/src/traced/probes/packages_list/packages_list_data_source.cc
@@ -32,6 +32,7 @@
 const ProbesDataSource::Descriptor PackagesListDataSource::descriptor = {
     /*name*/ "android.packages_list",
     /*flags*/ Descriptor::kFlagsNone,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 bool ParsePackagesListStream(protos::pbzero::PackagesList* packages_list_packet,
diff --git a/src/traced/probes/power/android_power_data_source.cc b/src/traced/probes/power/android_power_data_source.cc
index dac5bde..9d2d9a5 100644
--- a/src/traced/probes/power/android_power_data_source.cc
+++ b/src/traced/probes/power/android_power_data_source.cc
@@ -51,6 +51,7 @@
 const ProbesDataSource::Descriptor AndroidPowerDataSource::descriptor = {
     /*name*/ "android.power",
     /*flags*/ Descriptor::kHandlesIncrementalState,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 // Dynamically loads the libperfetto_android_internal.so library which
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.cc b/src/traced/probes/power/linux_power_sysfs_data_source.cc
index ff1e8b4..2e7d66e 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.cc
@@ -105,6 +105,7 @@
 const ProbesDataSource::Descriptor LinuxPowerSysfsDataSource::descriptor = {
     /*name*/ "linux.sysfs_power",
     /*flags*/ Descriptor::kFlagsNone,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 LinuxPowerSysfsDataSource::LinuxPowerSysfsDataSource(
diff --git a/src/traced/probes/probes_data_source.h b/src/traced/probes/probes_data_source.h
index 6ceef56..9025046 100644
--- a/src/traced/probes/probes_data_source.h
+++ b/src/traced/probes/probes_data_source.h
@@ -31,12 +31,16 @@
   // Static properties for a data source. Needs to be available before
   // instantiating each data source. It must have static lifetime.
   struct Descriptor {
+    using FillDescriptorFunc = void (*)(DataSourceDescriptor*);
     enum Flags : uint32_t {
       kFlagsNone = 0,
       kHandlesIncrementalState = 1 << 0,
     };
     const char* const name;
     uint32_t flags;
+    // If not nullptr, called to fill data source specific fields in
+    // DataSourceDescriptor.
+    FillDescriptorFunc fill_descriptor_func;
   };
 
   ProbesDataSource(TracingSessionID, const Descriptor*);
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index ebe5745..8b9fc46 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -32,6 +32,7 @@
 #include "perfetto/ext/tracing/ipc/producer_ipc_client.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
+#include "perfetto/tracing/core/forward_decls.h"
 #include "perfetto/tracing/core/trace_config.h"
 #include "src/android_stats/statsd_logging_helper.h"
 #include "src/traced/probes/android_log/android_log_data_source.h"
@@ -66,19 +67,6 @@
 constexpr size_t kTracingSharedMemSizeHintBytes = 1024 * 1024;
 constexpr size_t kTracingSharedMemPageSizeHintBytes = 32 * 1024;
 
-ProbesDataSource::Descriptor const* const kAllDataSources[]{
-    &FtraceDataSource::descriptor,               //
-    &ProcessStatsDataSource::descriptor,         //
-    &InodeFileDataSource::descriptor,            //
-    &SysStatsDataSource::descriptor,             //
-    &AndroidPowerDataSource::descriptor,         //
-    &LinuxPowerSysfsDataSource::descriptor,      //
-    &AndroidLogDataSource::descriptor,           //
-    &PackagesListDataSource::descriptor,         //
-    &MetatraceDataSource::descriptor,            //
-    &SystemInfoDataSource::descriptor,           //
-    &InitialDisplayStateDataSource::descriptor,  //
-};
 }  // namespace
 
 // State transition diagram:
@@ -107,42 +95,6 @@
   ftrace_.reset();
 }
 
-void ProbesProducer::OnConnect() {
-  PERFETTO_DCHECK(state_ == kConnecting);
-  state_ = kConnected;
-  ResetConnectionBackoff();
-  PERFETTO_LOG("Connected to the service");
-
-  // Register all the data sources.
-  for (const FtraceDataSource::Descriptor* desc : kAllDataSources) {
-    DataSourceDescriptor proto_desc;
-    proto_desc.set_name(desc->name);
-    proto_desc.set_will_notify_on_start(true);
-    proto_desc.set_will_notify_on_stop(true);
-    using Flags = ProbesDataSource::Descriptor::Flags;
-    if (desc->flags & Flags::kHandlesIncrementalState)
-      proto_desc.set_handles_incremental_state_clear(true);
-    endpoint_->RegisterDataSource(proto_desc);
-  }
-
-  // Used by tracebox to synchronize with traced_probes being registered.
-  if (all_data_sources_registered_cb_) {
-    endpoint_->Sync(all_data_sources_registered_cb_);
-  }
-}
-
-void ProbesProducer::OnDisconnect() {
-  PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
-  PERFETTO_LOG("Disconnected from tracing service");
-  if (state_ == kConnected)
-    return task_runner_->PostTask([this] { this->Restart(); });
-
-  state_ = kNotConnected;
-  IncreaseConnectionBackoff();
-  task_runner_->PostDelayedTask([this] { this->Connect(); },
-                                connection_backoff_ms_);
-}
-
 void ProbesProducer::Restart() {
   // We lost the connection with the tracing service. At this point we need
   // to reset all the data sources. Trying to handle that manually is going to
@@ -159,73 +111,9 @@
   ConnectWithRetries(socket_name, task_runner);
 }
 
-void ProbesProducer::SetupDataSource(DataSourceInstanceID instance_id,
-                                     const DataSourceConfig& config) {
-  PERFETTO_DLOG("SetupDataSource(id=%" PRIu64 ", name=%s)", instance_id,
-                config.name().c_str());
-  PERFETTO_DCHECK(data_sources_.count(instance_id) == 0);
-  TracingSessionID session_id = config.tracing_session_id();
-  PERFETTO_CHECK(session_id > 0);
-
-  std::unique_ptr<ProbesDataSource> data_source;
-  if (config.name() == FtraceDataSource::descriptor.name) {
-    data_source = CreateFtraceDataSource(session_id, config);
-  } else if (config.name() == InodeFileDataSource::descriptor.name) {
-    data_source = CreateInodeFileDataSource(session_id, config);
-  } else if (config.name() == ProcessStatsDataSource::descriptor.name) {
-    data_source = CreateProcessStatsDataSource(session_id, config);
-  } else if (config.name() == SysStatsDataSource::descriptor.name) {
-    data_source = CreateSysStatsDataSource(session_id, config);
-  } else if (config.name() == AndroidPowerDataSource::descriptor.name) {
-    data_source = CreateAndroidPowerDataSource(session_id, config);
-  } else if (config.name() == LinuxPowerSysfsDataSource::descriptor.name) {
-    data_source = CreateLinuxPowerSysfsDataSource(session_id, config);
-  } else if (config.name() == AndroidLogDataSource::descriptor.name) {
-    data_source = CreateAndroidLogDataSource(session_id, config);
-  } else if (config.name() == PackagesListDataSource::descriptor.name) {
-    data_source = CreatePackagesListDataSource(session_id, config);
-  } else if (config.name() == MetatraceDataSource::descriptor.name) {
-    data_source = CreateMetatraceDataSource(session_id, config);
-  } else if (config.name() == SystemInfoDataSource::descriptor.name) {
-    data_source = CreateSystemInfoDataSource(session_id, config);
-  } else if (config.name() == InitialDisplayStateDataSource::descriptor.name) {
-    data_source = CreateInitialDisplayStateDataSource(session_id, config);
-  }
-
-  if (!data_source) {
-    PERFETTO_ELOG("Failed to create data source '%s'", config.name().c_str());
-    return;
-  }
-
-  session_data_sources_.emplace(session_id, data_source.get());
-  data_sources_[instance_id] = std::move(data_source);
-}
-
-void ProbesProducer::StartDataSource(DataSourceInstanceID instance_id,
-                                     const DataSourceConfig& config) {
-  PERFETTO_DLOG("StartDataSource(id=%" PRIu64 ", name=%s)", instance_id,
-                config.name().c_str());
-  auto it = data_sources_.find(instance_id);
-  if (it == data_sources_.end()) {
-    // Can happen if SetupDataSource() failed (e.g. ftrace was busy).
-    PERFETTO_ELOG("Data source id=%" PRIu64 " not found", instance_id);
-    return;
-  }
-  ProbesDataSource* data_source = it->second.get();
-  if (data_source->started)
-    return;
-  if (config.trace_duration_ms() != 0) {
-    uint32_t timeout = 5000 + 2 * config.trace_duration_ms();
-    watchdogs_.emplace(
-        instance_id, base::Watchdog::GetInstance()->CreateFatalTimer(
-                         timeout, base::WatchdogCrashReason::kTraceDidntStop));
-  }
-  data_source->started = true;
-  data_source->Start();
-  endpoint_->NotifyDataSourceStarted(instance_id);
-}
-
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateFtraceDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<FtraceDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   // Don't retry if FtraceController::Create() failed once.
@@ -262,20 +150,24 @@
   return std::unique_ptr<ProbesDataSource>(std::move(data_source));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateInodeFileDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<InodeFileDataSource>(
     TracingSessionID session_id,
-    DataSourceConfig source_config) {
+    const DataSourceConfig& source_config) {
   PERFETTO_LOG("Inode file map setup (target_buf=%" PRIu32 ")",
                source_config.target_buffer());
   auto buffer_id = static_cast<BufferID>(source_config.target_buffer());
   if (system_inodes_.empty())
     CreateStaticDeviceToInodeMap("/system", &system_inodes_);
   return std::unique_ptr<InodeFileDataSource>(new InodeFileDataSource(
-      std::move(source_config), task_runner_, session_id, &system_inodes_,
-      &cache_, endpoint_->CreateTraceWriter(buffer_id)));
+      source_config, task_runner_, session_id, &system_inodes_, &cache_,
+      endpoint_->CreateTraceWriter(buffer_id)));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateProcessStatsDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<ProcessStatsDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -284,7 +176,9 @@
       std::unique_ptr<CpuFreqInfo>(new CpuFreqInfo())));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateAndroidPowerDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<AndroidPowerDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -293,8 +187,9 @@
                                  endpoint_->CreateTraceWriter(buffer_id)));
 }
 
+template <>
 std::unique_ptr<ProbesDataSource>
-ProbesProducer::CreateLinuxPowerSysfsDataSource(
+ProbesProducer::CreateDSInstance<LinuxPowerSysfsDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -303,7 +198,9 @@
                                     endpoint_->CreateTraceWriter(buffer_id)));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateAndroidLogDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<AndroidLogDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -312,7 +209,9 @@
                                endpoint_->CreateTraceWriter(buffer_id)));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreatePackagesListDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<PackagesListDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -320,7 +219,9 @@
       config, session_id, endpoint_->CreateTraceWriter(buffer_id)));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateSysStatsDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<SysStatsDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -329,7 +230,9 @@
       std::unique_ptr<CpuFreqInfo>(new CpuFreqInfo())));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateMetatraceDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<MetatraceDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -337,7 +240,9 @@
       task_runner_, session_id, endpoint_->CreateTraceWriter(buffer_id)));
 }
 
-std::unique_ptr<ProbesDataSource> ProbesProducer::CreateSystemInfoDataSource(
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<SystemInfoDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -346,8 +251,9 @@
       std::unique_ptr<CpuFreqInfo>(new CpuFreqInfo())));
 }
 
+template <>
 std::unique_ptr<ProbesDataSource>
-ProbesProducer::CreateInitialDisplayStateDataSource(
+ProbesProducer::CreateDSInstance<InitialDisplayStateDataSource>(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
@@ -356,6 +262,136 @@
       endpoint_->CreateTraceWriter(buffer_id)));
 }
 
+// Another anonymous namespace. This cannot be moved into the anonymous
+// namespace on top (it would fail to compile), because the CreateDSInstance
+// methods need to be fully declared before.
+namespace {
+
+using ProbesDataSourceFactoryFunc = std::unique_ptr<ProbesDataSource> (
+    ProbesProducer::*)(TracingSessionID, const DataSourceConfig&);
+
+struct DataSourceTraits {
+  const ProbesDataSource::Descriptor* descriptor;
+  ProbesDataSourceFactoryFunc factory_func;
+};
+
+template <typename T>
+constexpr DataSourceTraits Ds() {
+  return DataSourceTraits{&T::descriptor, &ProbesProducer::CreateDSInstance<T>};
+}
+
+const DataSourceTraits kAllDataSources[] = {
+    Ds<AndroidLogDataSource>(),   Ds<AndroidPowerDataSource>(),
+    Ds<FtraceDataSource>(),       Ds<InitialDisplayStateDataSource>(),
+    Ds<InodeFileDataSource>(),    Ds<LinuxPowerSysfsDataSource>(),
+    Ds<MetatraceDataSource>(),    Ds<PackagesListDataSource>(),
+    Ds<ProcessStatsDataSource>(), Ds<SysStatsDataSource>(),
+    Ds<SystemInfoDataSource>(),
+};
+
+}  // namespace
+
+void ProbesProducer::OnConnect() {
+  PERFETTO_DCHECK(state_ == kConnecting);
+  state_ = kConnected;
+  ResetConnectionBackoff();
+  PERFETTO_LOG("Connected to the service");
+
+  std::array<DataSourceDescriptor, base::ArraySize(kAllDataSources)>
+      proto_descs;
+  // Generate all data source descriptors.
+  for (size_t i = 0; i < proto_descs.size(); i++) {
+    DataSourceDescriptor& proto_desc = proto_descs[i];
+    const ProbesDataSource::Descriptor* desc = kAllDataSources[i].descriptor;
+
+    proto_desc.set_name(desc->name);
+    proto_desc.set_will_notify_on_start(true);
+    proto_desc.set_will_notify_on_stop(true);
+    using Flags = ProbesDataSource::Descriptor::Flags;
+    if (desc->flags & Flags::kHandlesIncrementalState)
+      proto_desc.set_handles_incremental_state_clear(true);
+    if (desc->fill_descriptor_func) {
+      desc->fill_descriptor_func(&proto_desc);
+    }
+  }
+
+  // Register all the data sources. Separate from the above loop because, if
+  // generating a data source descriptor takes too long, we don't want to be in
+  // a state where only some data sources are registered.
+  for (const DataSourceDescriptor& proto_desc : proto_descs) {
+    endpoint_->RegisterDataSource(proto_desc);
+  }
+
+  // Used by tracebox to synchronize with traced_probes being registered.
+  if (all_data_sources_registered_cb_) {
+    endpoint_->Sync(all_data_sources_registered_cb_);
+  }
+}
+
+void ProbesProducer::OnDisconnect() {
+  PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
+  PERFETTO_LOG("Disconnected from tracing service");
+  if (state_ == kConnected)
+    return task_runner_->PostTask([this] { this->Restart(); });
+
+  state_ = kNotConnected;
+  IncreaseConnectionBackoff();
+  task_runner_->PostDelayedTask([this] { this->Connect(); },
+                                connection_backoff_ms_);
+}
+
+void ProbesProducer::SetupDataSource(DataSourceInstanceID instance_id,
+                                     const DataSourceConfig& config) {
+  PERFETTO_DLOG("SetupDataSource(id=%" PRIu64 ", name=%s)", instance_id,
+                config.name().c_str());
+  PERFETTO_DCHECK(data_sources_.count(instance_id) == 0);
+  TracingSessionID session_id = config.tracing_session_id();
+  PERFETTO_CHECK(session_id > 0);
+
+  std::unique_ptr<ProbesDataSource> data_source;
+
+  for (const DataSourceTraits& rds : kAllDataSources) {
+    if (rds.descriptor->name != config.name()) {
+      continue;
+    }
+    data_source = (this->*(rds.factory_func))(session_id, config);
+    break;
+  }
+
+  if (!data_source) {
+    PERFETTO_ELOG("Failed to create data source '%s'", config.name().c_str());
+    return;
+  }
+
+  session_data_sources_[session_id].emplace(data_source->descriptor,
+                                            data_source.get());
+  data_sources_[instance_id] = std::move(data_source);
+}
+
+void ProbesProducer::StartDataSource(DataSourceInstanceID instance_id,
+                                     const DataSourceConfig& config) {
+  PERFETTO_DLOG("StartDataSource(id=%" PRIu64 ", name=%s)", instance_id,
+                config.name().c_str());
+  auto it = data_sources_.find(instance_id);
+  if (it == data_sources_.end()) {
+    // Can happen if SetupDataSource() failed (e.g. ftrace was busy).
+    PERFETTO_ELOG("Data source id=%" PRIu64 " not found", instance_id);
+    return;
+  }
+  ProbesDataSource* data_source = it->second.get();
+  if (data_source->started)
+    return;
+  if (config.trace_duration_ms() != 0) {
+    uint32_t timeout = 5000 + 2 * config.trace_duration_ms();
+    watchdogs_.emplace(
+        instance_id, base::Watchdog::GetInstance()->CreateFatalTimer(
+                         timeout, base::WatchdogCrashReason::kTraceDidntStop));
+  }
+  data_source->started = true;
+  data_source->Start();
+  endpoint_->NotifyDataSourceStarted(instance_id);
+}
+
 void ProbesProducer::StopDataSource(DataSourceInstanceID id) {
   PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
   auto it = data_sources_.find(id);
@@ -374,12 +410,19 @@
   endpoint_->NotifyDataSourceStopped(id);
 
   TracingSessionID session_id = data_source->tracing_session_id;
-  auto range = session_data_sources_.equal_range(session_id);
-  for (auto kv = range.first; kv != range.second; kv++) {
-    if (kv->second != data_source)
-      continue;
-    session_data_sources_.erase(kv);
-    break;
+
+  auto session_it = session_data_sources_.find(session_id);
+  if (session_it != session_data_sources_.end()) {
+    auto desc_range = session_it->second.equal_range(data_source->descriptor);
+    for (auto ds_it = desc_range.first; ds_it != desc_range.second; ds_it++) {
+      if (ds_it->second == data_source) {
+        session_it->second.erase(ds_it);
+        if (session_it->second.empty()) {
+          session_data_sources_.erase(session_it);
+        }
+        break;
+      }
+    }
   }
   data_sources_.erase(it);
   watchdogs_.erase(id);
@@ -476,56 +519,44 @@
 // userspace tracing buffer. If more than one ftrace data sources are active,
 // this call typically happens after writing for all session has been handled.
 void ProbesProducer::OnFtraceDataWrittenIntoDataSourceBuffers() {
-  TracingSessionID last_session_id = 0;
-  FtraceMetadata* metadata = nullptr;
-  InodeFileDataSource* inode_data_source = nullptr;
-  ProcessStatsDataSource* ps_data_source = nullptr;
+  for (const auto& tracing_session : session_data_sources_) {
+    // Take the metadata (e.g. new pids) collected from ftrace and pass it to
+    // other interested data sources (e.g. the process scraper to get command
+    // lines on new pids and tgid<>tid mappings). Note: there can be more than
+    // one ftrace data source per session. All of them should be considered
+    // (b/169226092).
+    const std::unordered_multimap<const ProbesDataSource::Descriptor*,
+                                  ProbesDataSource*>& ds_by_type =
+        tracing_session.second;
+    auto ft_range = ds_by_type.equal_range(&FtraceDataSource::descriptor);
 
-  // unordered_multimap guarantees that entries with the same key are contiguous
-  // in the iteration.
-  for (auto it = session_data_sources_.begin(); /* check below*/; it++) {
-    // If this is the last iteration or the session id has changed,
-    // dispatch the metadata update to the linked data sources, if any.
-    if (it == session_data_sources_.end() || it->first != last_session_id) {
-      bool has_inodes = metadata && !metadata->inode_and_device.empty();
-      bool has_pids = metadata && !metadata->pids.empty();
-      bool has_rename_pids = metadata && !metadata->rename_pids.empty();
-      if (has_inodes && inode_data_source)
-        inode_data_source->OnInodes(metadata->inode_and_device);
-      // Ordering the rename pids before the seen pids is important so that any
-      // renamed processes get scraped in the OnPids call.
-      if (has_rename_pids && ps_data_source)
-        ps_data_source->OnRenamePids(metadata->rename_pids);
-      if (has_pids && ps_data_source)
-        ps_data_source->OnPids(metadata->pids);
-      if (metadata)
-        metadata->Clear();
-      metadata = nullptr;
-      inode_data_source = nullptr;
-      ps_data_source = nullptr;
-      if (it == session_data_sources_.end())
-        break;
-      last_session_id = it->first;
-    }
-    ProbesDataSource* ds = it->second;
-    if (!ds->started)
-      continue;
-
-    if (ds->descriptor == &FtraceDataSource::descriptor) {
-      metadata = static_cast<FtraceDataSource*>(ds)->mutable_metadata();
-    } else if (ds->descriptor == &InodeFileDataSource::descriptor) {
-      inode_data_source = static_cast<InodeFileDataSource*>(ds);
-    } else if (ds->descriptor == &ProcessStatsDataSource::descriptor) {
-      // A trace session might have declared more than one ps data source.
-      // In those cases we often use one for a full dump on startup (
-      // targeting a dedicated buffer) and another one for on-demand dumps
-      // targeting the main buffer.
-      // Only use the one that has on-demand dumps enabled, if any.
-      auto ps = static_cast<ProcessStatsDataSource*>(ds);
-      if (ps->on_demand_dumps_enabled())
-        ps_data_source = ps;
-    }
-  }  // for (session_data_sources_)
+    auto ino_range = ds_by_type.equal_range(&InodeFileDataSource::descriptor);
+    auto ps_range = ds_by_type.equal_range(&ProcessStatsDataSource::descriptor);
+    for (auto ft_it = ft_range.first; ft_it != ft_range.second; ft_it++) {
+      auto* ftrace_ds = static_cast<FtraceDataSource*>(ft_it->second);
+      if (!ftrace_ds->started)
+        continue;
+      auto* metadata = ftrace_ds->mutable_metadata();
+      for (auto ps_it = ps_range.first; ps_it != ps_range.second; ps_it++) {
+        auto* ps_ds = static_cast<ProcessStatsDataSource*>(ps_it->second);
+        if (!ps_ds->started || !ps_ds->on_demand_dumps_enabled())
+          continue;
+        // Ordering the rename pids before the seen pids is important so that
+        // any renamed processes get scraped in the OnPids call.
+        if (!metadata->rename_pids.empty())
+          ps_ds->OnRenamePids(metadata->rename_pids);
+        if (!metadata->pids.empty())
+          ps_ds->OnPids(metadata->pids);
+      }
+      for (auto in_it = ino_range.first; in_it != ino_range.second; in_it++) {
+        auto* inode_ds = static_cast<InodeFileDataSource*>(in_it->second);
+        if (!inode_ds->started)
+          continue;
+        inode_ds->OnInodes(metadata->inode_and_device);
+      }
+      metadata->Clear();
+    }  // for (FtraceDataSource)
+  }    // for (tracing_session)
 }
 
 void ProbesProducer::ConnectWithRetries(const char* socket_name,
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index 6650501..b021ee5 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -66,42 +66,13 @@
   // Our Impl
   void ConnectWithRetries(const char* socket_name,
                           base::TaskRunner* task_runner);
-  std::unique_ptr<ProbesDataSource> CreateFtraceDataSource(
+
+  // Constructs an instance of a data source of type T.
+  template <typename T>
+  std::unique_ptr<ProbesDataSource> CreateDSInstance(
       TracingSessionID session_id,
       const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateProcessStatsDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateInodeFileDataSource(
-      TracingSessionID session_id,
-      DataSourceConfig config);
-  std::unique_ptr<ProbesDataSource> CreateSysStatsDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateAndroidPowerDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateAndroidPowerStatsDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateAndroidLogDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateLinuxPowerSysfsDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreatePackagesListDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateMetatraceDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateSystemInfoDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
-  std::unique_ptr<ProbesDataSource> CreateInitialDisplayStateDataSource(
-      TracingSessionID session_id,
-      const DataSourceConfig& config);
+
   void ActivateTrigger(std::string trigger);
 
   // Calls `cb` when all data sources have been registered.
@@ -141,8 +112,17 @@
   std::unordered_map<DataSourceInstanceID, std::unique_ptr<ProbesDataSource>>
       data_sources_;
 
-  // Keeps (pointers to) data sources ordered by session id.
-  std::unordered_multimap<TracingSessionID, ProbesDataSource*>
+  // Keeps (pointers to) data sources grouped by session id and data source
+  // type. The pointers do not own the data sources (they're owned by
+  // data_sources_).
+  //
+  // const ProbesDataSource::Descriptor* identifies the type.
+  //
+  // Used by OnFtraceDataWrittenIntoDataSourceBuffers().
+  std::unordered_map<
+      TracingSessionID,
+      std::unordered_multimap<const ProbesDataSource::Descriptor*,
+                              ProbesDataSource*>>
       session_data_sources_;
 
   std::unordered_multimap<FlushRequestID, DataSourceInstanceID>
diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc
index 76e8ccc..d61e41c 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -84,6 +84,7 @@
 const ProbesDataSource::Descriptor ProcessStatsDataSource::descriptor = {
     /*name*/ "linux.process_stats",
     /*flags*/ Descriptor::kHandlesIncrementalState,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 ProcessStatsDataSource::ProcessStatsDataSource(
diff --git a/src/traced/probes/sys_stats/sys_stats_data_source.cc b/src/traced/probes/sys_stats/sys_stats_data_source.cc
index 93c292b..9f2875f 100644
--- a/src/traced/probes/sys_stats/sys_stats_data_source.cc
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.cc
@@ -70,6 +70,7 @@
 const ProbesDataSource::Descriptor SysStatsDataSource::descriptor = {
     /*name*/ "linux.sys_stats",
     /*flags*/ Descriptor::kFlagsNone,
+    /*fill_descriptor_func*/ nullptr,
 };
 
 SysStatsDataSource::SysStatsDataSource(
diff --git a/src/traced/probes/system_info/system_info_data_source.cc b/src/traced/probes/system_info/system_info_data_source.cc
index 3e96239..2178556 100644
--- a/src/traced/probes/system_info/system_info_data_source.cc
+++ b/src/traced/probes/system_info/system_info_data_source.cc
@@ -42,6 +42,7 @@
 const ProbesDataSource::Descriptor SystemInfoDataSource::descriptor = {
     /* name */ "linux.system_info",
     /* flags */ Descriptor::kFlagsNone,
+    /* fill_descriptor_func */ nullptr,
 };
 
 SystemInfoDataSource::SystemInfoDataSource(
diff --git a/test/configs/BUILD.gn b/test/configs/BUILD.gn
index b57b9aa..230fe01 100644
--- a/test/configs/BUILD.gn
+++ b/test/configs/BUILD.gn
@@ -21,8 +21,7 @@
   script = "../../tools/protoc_helper.py"
 
   deps = [
-    "../../protos/perfetto/config:merged_config_source_set",
-    "../../protos/perfetto/trace:merged_trace_source_set",
+    "../../protos/perfetto/trace:non_minimal_source_set",
     protoc_target,
   ]
 
diff --git a/test/stress_test/stress_test.cc b/test/stress_test/stress_test.cc
index c8e9503..75de80a 100644
--- a/test/stress_test/stress_test.cc
+++ b/test/stress_test/stress_test.cc
@@ -178,7 +178,8 @@
   traced.args.env = env_;
   if (!verbose) {
     traced.args.out_fd = OpenLogFile(result_dir + "/traced.log");
-    traced.args.stderr_mode = traced.args.stdout_mode = base::Subprocess::kFd;
+    traced.args.stderr_mode = traced.args.stdout_mode =
+        base::Subprocess::OutputMode::kFd;
   }
   traced.Start();
   g_sig->pids_to_kill.emplace_back(traced.pid());
@@ -195,7 +196,7 @@
       producer.args.out_fd =
           OpenLogFile(result_dir + "/producer." + std::to_string(i) + ".log");
       producer.args.stderr_mode = producer.args.stdout_mode =
-          base::Subprocess::kFd;
+          base::Subprocess::OutputMode::kFd;
     }
     producer.args.env = env_;
     producer.Start();
@@ -213,7 +214,7 @@
   if (!verbose) {
     consumer.args.out_fd = OpenLogFile(result_dir + "/perfetto.log");
     consumer.args.stderr_mode = consumer.args.stdout_mode =
-        base::Subprocess::kFd;
+        base::Subprocess::OutputMode::kFd;
   }
   remove(trace_file_path.c_str());
   consumer.Start();
diff --git a/test/test_helper.h b/test/test_helper.h
index 778f88a..12d436d 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -350,8 +350,8 @@
   Exec(const std::string& argv0,
        std::initializer_list<std::string> args,
        std::string input = "") {
-    subprocess_.args.stderr_mode = base::Subprocess::kBuffer;
-    subprocess_.args.stdout_mode = base::Subprocess::kDevNull;
+    subprocess_.args.stderr_mode = base::Subprocess::OutputMode::kBuffer;
+    subprocess_.args.stdout_mode = base::Subprocess::OutputMode::kDevNull;
     subprocess_.args.input = input;
 
 #if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
diff --git a/tools/ftrace_proto_gen/proto_gen_utils.cc b/tools/ftrace_proto_gen/proto_gen_utils.cc
index 189c8f1..95a6531 100644
--- a/tools/ftrace_proto_gen/proto_gen_utils.cc
+++ b/tools/ftrace_proto_gen/proto_gen_utils.cc
@@ -38,8 +38,8 @@
   const std::string platform = "linux64";
 #endif
   base::Subprocess clang_fmt({"buildtools/" + platform + "/clang-format"});
-  clang_fmt.args.stdout_mode = base::Subprocess::kBuffer;
-  clang_fmt.args.stderr_mode = base::Subprocess::kInherit;
+  clang_fmt.args.stdout_mode = base::Subprocess::OutputMode::kBuffer;
+  clang_fmt.args.stderr_mode = base::Subprocess::OutputMode::kInherit;
   clang_fmt.args.input = input;
   PERFETTO_CHECK(clang_fmt.Call());
   return std::move(clang_fmt.output());
diff --git a/tools/gen_bazel b/tools/gen_bazel
index e5c7f82..d330855 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -25,6 +25,7 @@
 
 from __future__ import print_function
 import argparse
+import itertools
 import json
 import os
 import re
@@ -76,21 +77,25 @@
 
 # Root proto targets (to force discovery of intermediate proto targets).
 # These targets are marked public.
+# TODO(lalitm): remove these in a followup.
 proto_targets = [
-    '//protos/perfetto/trace:merged_trace_source_set',
-    '//protos/perfetto/trace:non_minimal_lite',
-    '//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/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',
 ]
 
+# Proto target groups which will be made public.
+proto_groups = {
+  'config': [
+    '//protos/perfetto/config:source_set'
+  ],
+  'trace': [
+    '//protos/perfetto/trace:non_minimal_source_set',
+    '//protos/perfetto/trace:minimal_source_set'
+  ],
+}
+
 # Path for the protobuf sources in the standalone build.
 buildtools_protobuf_src = '//buildtools/protobuf/src'
 
@@ -257,15 +262,17 @@
   return gn_utils.label_to_target_name_with_path(gn_name)
 
 
+def get_bazel_proto_sources_label(target_name):
+  """Converts a GN target name into a Bazel proto label name."""
+  return re.sub('_(lite|zero|cpp|ipc|source_set|descriptor)$', '',
+                get_bazel_label_name(target_name)) + '_protos'
+
+
 def gen_proto_label(target):
   """ Generates the xx_proto_library label for proto targets."""
   assert (target.type == 'proto_library')
 
-  def get_sources_label(target_name):
-    return re.sub('_(lite|zero|cpp|ipc|source_set|descriptor)$', '',
-                  get_bazel_label_name(target_name)) + '_protos'
-
-  sources_label_name = get_sources_label(target.name)
+  sources_label_name = get_bazel_proto_sources_label(target.name)
 
   # For 'source_set' plugins, we don't want to generate any plugin-dependent
   # targets so just return the label of the proto sources only.
@@ -276,7 +283,7 @@
     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])
+        [':' + get_bazel_proto_sources_label(x) for x in target.proto_deps])
 
     # In Bazel, proto_paths are not a supported concept becauase strong
     # dependency checking is enabled. Instead, we need to depend on the target
@@ -295,7 +302,7 @@
       sources_label.visibility = ['PERFETTO_CONFIG.proto_library_visibility']
 
     sources_label.exports = sorted([
-      ':' + get_sources_label(d) for d in target.proto_exports
+      ':' + get_bazel_proto_sources_label(d) for d in target.proto_exports
     ])
     return sources_label
 
@@ -332,7 +339,7 @@
   # implicit.
   if target.proto_plugin == 'descriptor':
     plugin_label.deps += [
-      ':' + get_sources_label(x) for x in target.proto_deps
+      ':' + get_bazel_proto_sources_label(x) for x in target.proto_deps
     ]
   else:
     plugin_label.deps += [':' + sources_label_name]
@@ -345,6 +352,56 @@
   return plugin_label
 
 
+def gen_proto_group_target(gn, name, target_names):
+  # Get a recursive list of the proto_library targets rooted here which
+  # have src.
+  deps_set = set()
+  def recursive_deps(name, deps_set):
+    target = gn.get_target(name)
+    deps_set.add(target.name)
+    for name in target.proto_deps:
+      recursive_deps(name, deps_set)
+
+  for n in target_names:
+    recursive_deps(n, deps_set)
+
+  deps_list = sorted(list(deps_set))
+  comment = f'''[{', '.join(target_names)}]'''
+
+  # First, create a root source set target which references all the child
+  # source set targets. We publish this as well as depending on this in all
+  # subsequent targets.
+  sources_label = BazelLabel(name + '_proto', 'perfetto_proto_library')
+  sources_label.deps = [
+    ':' + get_bazel_proto_sources_label(name) for name in deps_set
+  ]
+  sources_label.visibility = PUBLIC_VISIBILITY
+  sources_label.comment = comment
+
+  # Next we create a cc proto target depending on the source set target.
+  cc_label = BazelLabel(name + '_cc_proto', 'perfetto_cc_proto_library')
+  cc_label.deps = [':' + sources_label.name]
+  cc_label.visibility = PUBLIC_VISIBILITY
+  cc_label.comment = comment
+
+  # Next we create a java proto target depending on the source set
+  # target.
+  java_label = BazelLabel(name + '_java_proto', 'perfetto_java_proto_library')
+  java_label.deps = [':' + sources_label.name]
+  java_label.visibility = PUBLIC_VISIBILITY
+  java_label.comment = comment
+
+  # Finally we create a java lite proto target depending on the source
+  # set target.
+  lite_name = name + '_java_proto_lite'
+  java_lite_label = BazelLabel(lite_name, 'perfetto_java_lite_proto_library')
+  java_lite_label.deps = [':' + sources_label.name]
+  java_lite_label.visibility = PUBLIC_VISIBILITY
+  java_lite_label.comment = comment
+
+  return [sources_label, cc_label, java_label, java_lite_label]
+
+
 def gen_target(gn_target):
   if gn_target.type == 'proto_library':
     return [gen_proto_label(gn_target)]
@@ -511,6 +568,10 @@
 # ##############################################################################
 
 '''.lstrip()
+  # Generate targets for proto groups.
+  for l_name, t_names in proto_groups.items():
+    res += ''.join(str(x) for x in gen_proto_group_target(gn, l_name, t_names))
+
   # Force discovery of explicilty listed root proto targets.
   for target_name in sorted(proto_targets):
     gn.get_target(target_name)
diff --git a/ui/build.js b/ui/build.js
index e01e45d..e043026 100644
--- a/ui/build.js
+++ b/ui/build.js
@@ -466,6 +466,13 @@
           absPath = pjoin(ROOT_DIR, uri);
         }
 
+        // Don't serve contents outside of the project root (b/221101533).
+        if (path.relative(ROOT_DIR, absPath).startsWith('..')) {
+          res.writeHead(403);
+          res.end('403 Forbidden - Request path outside of the repo root');
+          return;
+        }
+
         fs.readFile(absPath, function(err, data) {
           if (err) {
             res.writeHead(404);
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index 4aeb978..86c0ccb 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -205,10 +205,6 @@
   width: 100%;
 }
 
-.x-scrollable {
-  overflow-x: auto;
-}
-
 .query-table {
     width: 100%;
     font-size: 14px;
@@ -481,6 +477,10 @@
   height: 20px;
 }
 
+.x-scrollable {
+  overflow-x: auto;
+}
+
 header.overview {
   display: flex;
   align-content: center;
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 9c8969a..98dc6a8 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -407,7 +407,7 @@
             return {key: tab.key, name: tab.name};
           }),
         }),
-        m('.details-panel-container',
+        m('.details-panel-container.x-scrollable',
           m(PanelContainer, {doesScroll: true, panels, kind: 'DETAILS'})));
   }
 }