Merge "TraceProcessor: introduce RefCounted<T>, remove std::shared_ptr"
diff --git a/Android.bp b/Android.bp
index 845d0c4..f3f47ef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1753,7 +1753,7 @@
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
-        ":perfetto_src_trace_processor_metrics_lib",
+        ":perfetto_src_trace_processor_metrics_metrics",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_storage_full",
         ":perfetto_src_trace_processor_storage_minimal",
@@ -1885,7 +1885,7 @@
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
-        "perfetto_src_trace_processor_metrics_gen_merged_sql_metrics",
+        "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
     ],
     defaults: [
         "perfetto_defaults",
@@ -6841,6 +6841,7 @@
     name: "perfetto_src_base_base",
     srcs: [
         "src/base/android_utils.cc",
+        "src/base/base64.cc",
         "src/base/crash_keys.cc",
         "src/base/ctrl_c_handler.cc",
         "src/base/event_fd.cc",
@@ -6887,6 +6888,7 @@
 filegroup {
     name: "perfetto_src_base_unittests",
     srcs: [
+        "src/base/base64_unittest.cc",
         "src/base/circular_queue_unittest.cc",
         "src/base/flat_set_unittest.cc",
         "src/base/getopt_compat_unittest.cc",
@@ -7965,7 +7967,7 @@
     ],
     cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
     out: [
-        "src/trace_processor/metrics/chrome/all_chrome_metrics.descriptor.h",
+        "src/trace_processor/metrics/all_chrome_metrics.descriptor.h",
     ],
     tool_files: [
         "tools/gen_cc_proto_descriptor.py",
@@ -7987,113 +7989,113 @@
     ],
 }
 
-// GN: //src/trace_processor/metrics:gen_merged_sql_metrics
-genrule {
-    name: "perfetto_src_trace_processor_metrics_gen_merged_sql_metrics",
+// GN: //src/trace_processor/metrics:metrics
+filegroup {
+    name: "perfetto_src_trace_processor_metrics_metrics",
     srcs: [
-        "src/trace_processor/metrics/android/android_batt.sql",
-        "src/trace_processor/metrics/android/android_camera.sql",
-        "src/trace_processor/metrics/android/android_cpu.sql",
-        "src/trace_processor/metrics/android/android_cpu_agg.sql",
-        "src/trace_processor/metrics/android/android_cpu_raw_metrics_per_core.sql",
-        "src/trace_processor/metrics/android/android_dma_heap.sql",
-        "src/trace_processor/metrics/android/android_fastrpc.sql",
-        "src/trace_processor/metrics/android/android_gpu.sql",
-        "src/trace_processor/metrics/android/android_hwcomposer.sql",
-        "src/trace_processor/metrics/android/android_hwui_metric.sql",
-        "src/trace_processor/metrics/android/android_hwui_threads.sql",
-        "src/trace_processor/metrics/android/android_ion.sql",
-        "src/trace_processor/metrics/android/android_jank.sql",
-        "src/trace_processor/metrics/android/android_lmk.sql",
-        "src/trace_processor/metrics/android/android_lmk_reason.sql",
-        "src/trace_processor/metrics/android/android_mem.sql",
-        "src/trace_processor/metrics/android/android_mem_unagg.sql",
-        "src/trace_processor/metrics/android/android_multiuser.sql",
-        "src/trace_processor/metrics/android/android_multiuser_populator.sql",
-        "src/trace_processor/metrics/android/android_package_list.sql",
-        "src/trace_processor/metrics/android/android_powrails.sql",
-        "src/trace_processor/metrics/android/android_proxy_power.sql",
-        "src/trace_processor/metrics/android/android_simpleperf.sql",
-        "src/trace_processor/metrics/android/android_startup.sql",
-        "src/trace_processor/metrics/android/android_surfaceflinger.sql",
-        "src/trace_processor/metrics/android/android_sysui_cuj.sql",
-        "src/trace_processor/metrics/android/android_sysui_cuj_jank_query.sql",
-        "src/trace_processor/metrics/android/android_task_names.sql",
-        "src/trace_processor/metrics/android/android_thread_time_in_state.sql",
-        "src/trace_processor/metrics/android/android_trace_quality.sql",
-        "src/trace_processor/metrics/android/composer_execution.sql",
-        "src/trace_processor/metrics/android/composition_layers.sql",
-        "src/trace_processor/metrics/android/cpu_info.sql",
-        "src/trace_processor/metrics/android/display_metrics.sql",
-        "src/trace_processor/metrics/android/frame_missed.sql",
-        "src/trace_processor/metrics/android/g2d.sql",
-        "src/trace_processor/metrics/android/g2d_duration.sql",
-        "src/trace_processor/metrics/android/global_counter_span_view.sql",
-        "src/trace_processor/metrics/android/gpu_counter_span_view.sql",
-        "src/trace_processor/metrics/android/java_heap_histogram.sql",
-        "src/trace_processor/metrics/android/java_heap_stats.sql",
-        "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
-        "src/trace_processor/metrics/android/power_drain_in_watts.sql",
-        "src/trace_processor/metrics/android/power_profile_data.sql",
-        "src/trace_processor/metrics/android/process_counter_span_view.sql",
-        "src/trace_processor/metrics/android/process_mem.sql",
-        "src/trace_processor/metrics/android/process_metadata.sql",
-        "src/trace_processor/metrics/android/process_oom_score.sql",
-        "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
-        "src/trace_processor/metrics/android/profiler_smaps.sql",
-        "src/trace_processor/metrics/android/span_view_stats.sql",
-        "src/trace_processor/metrics/android/startup/hsc.sql",
-        "src/trace_processor/metrics/android/startup/launches.sql",
-        "src/trace_processor/metrics/android/thread_counter_span_view.sql",
-        "src/trace_processor/metrics/android/unsymbolized_frames.sql",
-        "src/trace_processor/metrics/chrome/actual_power_by_category.sql",
-        "src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql",
-        "src/trace_processor/metrics/chrome/chrome_event_metadata.sql",
-        "src/trace_processor/metrics/chrome/chrome_processes.sql",
-        "src/trace_processor/metrics/chrome/chrome_thread_slice_with_cpu_time.sql",
-        "src/trace_processor/metrics/chrome/cpu_time_by_category.sql",
-        "src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql",
-        "src/trace_processor/metrics/chrome/estimated_power_by_category.sql",
-        "src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql",
-        "src/trace_processor/metrics/chrome/gesture_flow_event.sql",
-        "src/trace_processor/metrics/chrome/gesture_flow_event_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/gesture_jank.sql",
-        "src/trace_processor/metrics/chrome/rail_modes.sql",
-        "src/trace_processor/metrics/chrome/scroll_flow_event.sql",
-        "src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_task.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_touch_move.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_get_bitmap.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/test_chrome_metric.sql",
-        "src/trace_processor/metrics/chrome/touch_flow_event.sql",
-        "src/trace_processor/metrics/chrome/touch_flow_event_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/touch_jank.sql",
-        "src/trace_processor/metrics/experimental/blink_gc_metric.sql",
-        "src/trace_processor/metrics/experimental/chrome_dropped_frames.sql",
-        "src/trace_processor/metrics/experimental/frame_times.sql",
-        "src/trace_processor/metrics/experimental/media_metric.sql",
-        "src/trace_processor/metrics/experimental/reported_by_page.sql",
-        "src/trace_processor/metrics/trace_metadata.sql",
-        "src/trace_processor/metrics/trace_stats.sql",
-        "src/trace_processor/metrics/webview/webview_power_usage.sql",
-    ],
-    cmd: "$(location tools/gen_merged_sql_metrics.py) --cpp_out=$(out) $(in)",
-    out: [
-        "src/trace_processor/metrics/sql_metrics.h",
-    ],
-    tool_files: [
-        "tools/gen_merged_sql_metrics.py",
+        "src/trace_processor/metrics/metrics.cc",
     ],
 }
 
-// GN: //src/trace_processor/metrics:lib
-filegroup {
-    name: "perfetto_src_trace_processor_metrics_lib",
+// GN: //src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics
+genrule {
+    name: "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
     srcs: [
-        "src/trace_processor/metrics/metrics.cc",
+        "src/trace_processor/metrics/sql/android/android_batt.sql",
+        "src/trace_processor/metrics/sql/android/android_camera.sql",
+        "src/trace_processor/metrics/sql/android/android_cpu.sql",
+        "src/trace_processor/metrics/sql/android/android_cpu_agg.sql",
+        "src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql",
+        "src/trace_processor/metrics/sql/android/android_dma_heap.sql",
+        "src/trace_processor/metrics/sql/android/android_fastrpc.sql",
+        "src/trace_processor/metrics/sql/android/android_gpu.sql",
+        "src/trace_processor/metrics/sql/android/android_hwcomposer.sql",
+        "src/trace_processor/metrics/sql/android/android_hwui_metric.sql",
+        "src/trace_processor/metrics/sql/android/android_hwui_threads.sql",
+        "src/trace_processor/metrics/sql/android/android_ion.sql",
+        "src/trace_processor/metrics/sql/android/android_jank.sql",
+        "src/trace_processor/metrics/sql/android/android_lmk.sql",
+        "src/trace_processor/metrics/sql/android/android_lmk_reason.sql",
+        "src/trace_processor/metrics/sql/android/android_mem.sql",
+        "src/trace_processor/metrics/sql/android/android_mem_unagg.sql",
+        "src/trace_processor/metrics/sql/android/android_multiuser.sql",
+        "src/trace_processor/metrics/sql/android/android_multiuser_populator.sql",
+        "src/trace_processor/metrics/sql/android/android_package_list.sql",
+        "src/trace_processor/metrics/sql/android/android_powrails.sql",
+        "src/trace_processor/metrics/sql/android/android_proxy_power.sql",
+        "src/trace_processor/metrics/sql/android/android_simpleperf.sql",
+        "src/trace_processor/metrics/sql/android/android_startup.sql",
+        "src/trace_processor/metrics/sql/android/android_surfaceflinger.sql",
+        "src/trace_processor/metrics/sql/android/android_sysui_cuj.sql",
+        "src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql",
+        "src/trace_processor/metrics/sql/android/android_task_names.sql",
+        "src/trace_processor/metrics/sql/android/android_thread_time_in_state.sql",
+        "src/trace_processor/metrics/sql/android/android_trace_quality.sql",
+        "src/trace_processor/metrics/sql/android/composer_execution.sql",
+        "src/trace_processor/metrics/sql/android/composition_layers.sql",
+        "src/trace_processor/metrics/sql/android/cpu_info.sql",
+        "src/trace_processor/metrics/sql/android/display_metrics.sql",
+        "src/trace_processor/metrics/sql/android/frame_missed.sql",
+        "src/trace_processor/metrics/sql/android/g2d.sql",
+        "src/trace_processor/metrics/sql/android/g2d_duration.sql",
+        "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
+        "src/trace_processor/metrics/sql/android/java_heap_stats.sql",
+        "src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
+        "src/trace_processor/metrics/sql/android/power_drain_in_watts.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data.sql",
+        "src/trace_processor/metrics/sql/android/process_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/process_mem.sql",
+        "src/trace_processor/metrics/sql/android/process_metadata.sql",
+        "src/trace_processor/metrics/sql/android/process_oom_score.sql",
+        "src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql",
+        "src/trace_processor/metrics/sql/android/profiler_smaps.sql",
+        "src/trace_processor/metrics/sql/android/span_view_stats.sql",
+        "src/trace_processor/metrics/sql/android/startup/hsc.sql",
+        "src/trace_processor/metrics/sql/android/startup/launches.sql",
+        "src/trace_processor/metrics/sql/android/thread_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
+        "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
+        "src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_processes.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_thread_slice_with_cpu_time.sql",
+        "src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql",
+        "src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql",
+        "src/trace_processor/metrics/sql/chrome/estimated_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql",
+        "src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/gesture_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/rail_modes.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql",
+        "src/trace_processor/metrics/sql/chrome/touch_flow_event.sql",
+        "src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/touch_jank.sql",
+        "src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql",
+        "src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql",
+        "src/trace_processor/metrics/sql/experimental/frame_times.sql",
+        "src/trace_processor/metrics/sql/experimental/media_metric.sql",
+        "src/trace_processor/metrics/sql/experimental/reported_by_page.sql",
+        "src/trace_processor/metrics/sql/trace_metadata.sql",
+        "src/trace_processor/metrics/sql/trace_stats.sql",
+        "src/trace_processor/metrics/sql/webview/webview_power_usage.sql",
+    ],
+    cmd: "$(location tools/gen_amalgamated_sql_metrics.py) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h",
+    ],
+    tool_files: [
+        "tools/gen_amalgamated_sql_metrics.py",
     ],
 }
 
@@ -8134,6 +8136,7 @@
 filegroup {
     name: "perfetto_src_trace_processor_sqlite_sqlite",
     srcs: [
+        "src/trace_processor/sqlite/create_function.cc",
         "src/trace_processor/sqlite/db_sqlite_table.cc",
         "src/trace_processor/sqlite/query_constraints.cc",
         "src/trace_processor/sqlite/register_function.cc",
@@ -9343,7 +9346,7 @@
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
-        ":perfetto_src_trace_processor_metrics_lib",
+        ":perfetto_src_trace_processor_metrics_metrics",
         ":perfetto_src_trace_processor_metrics_unittests",
         ":perfetto_src_trace_processor_rpc_rpc",
         ":perfetto_src_trace_processor_rpc_unittests",
@@ -9535,7 +9538,7 @@
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
-        "perfetto_src_trace_processor_metrics_gen_merged_sql_metrics",
+        "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
         "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",
@@ -9643,7 +9646,7 @@
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
-        ":perfetto_src_trace_processor_metrics_lib",
+        ":perfetto_src_trace_processor_metrics_metrics",
         ":perfetto_src_trace_processor_rpc_httpd",
         ":perfetto_src_trace_processor_rpc_rpc",
         ":perfetto_src_trace_processor_sqlite_sqlite",
@@ -9698,7 +9701,7 @@
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
-        "perfetto_src_trace_processor_metrics_gen_merged_sql_metrics",
+        "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
     ],
     defaults: [
         "perfetto_defaults",
@@ -9797,7 +9800,7 @@
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
-        ":perfetto_src_trace_processor_metrics_lib",
+        ":perfetto_src_trace_processor_metrics_metrics",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_storage_full",
         ":perfetto_src_trace_processor_storage_minimal",
@@ -9857,7 +9860,7 @@
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
-        "perfetto_src_trace_processor_metrics_gen_merged_sql_metrics",
+        "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
         "perfetto_tools_trace_to_text_gen_cc_trace_descriptor",
     ],
     defaults: [
diff --git a/BUILD b/BUILD
index a5ef32c..10d6905 100644
--- a/BUILD
+++ b/BUILD
@@ -335,6 +335,7 @@
     name = "include_perfetto_ext_base_base",
     srcs = [
         "include/perfetto/ext/base/android_utils.h",
+        "include/perfetto/ext/base/base64.h",
         "include/perfetto/ext/base/circular_queue.h",
         "include/perfetto/ext/base/container_annotations.h",
         "include/perfetto/ext/base/crash_keys.h",
@@ -643,6 +644,7 @@
     name = "src_base_base",
     srcs = [
         "src/base/android_utils.cc",
+        "src/base/base64.cc",
         "src/base/crash_keys.cc",
         "src/base/ctrl_c_handler.cc",
         "src/base/event_fd.cc",
@@ -998,13 +1000,114 @@
     ],
 )
 
+perfetto_genrule(
+    name = "src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+    srcs = [
+        "src/trace_processor/metrics/sql/android/android_batt.sql",
+        "src/trace_processor/metrics/sql/android/android_camera.sql",
+        "src/trace_processor/metrics/sql/android/android_cpu.sql",
+        "src/trace_processor/metrics/sql/android/android_cpu_agg.sql",
+        "src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql",
+        "src/trace_processor/metrics/sql/android/android_dma_heap.sql",
+        "src/trace_processor/metrics/sql/android/android_fastrpc.sql",
+        "src/trace_processor/metrics/sql/android/android_gpu.sql",
+        "src/trace_processor/metrics/sql/android/android_hwcomposer.sql",
+        "src/trace_processor/metrics/sql/android/android_hwui_metric.sql",
+        "src/trace_processor/metrics/sql/android/android_hwui_threads.sql",
+        "src/trace_processor/metrics/sql/android/android_ion.sql",
+        "src/trace_processor/metrics/sql/android/android_jank.sql",
+        "src/trace_processor/metrics/sql/android/android_lmk.sql",
+        "src/trace_processor/metrics/sql/android/android_lmk_reason.sql",
+        "src/trace_processor/metrics/sql/android/android_mem.sql",
+        "src/trace_processor/metrics/sql/android/android_mem_unagg.sql",
+        "src/trace_processor/metrics/sql/android/android_multiuser.sql",
+        "src/trace_processor/metrics/sql/android/android_multiuser_populator.sql",
+        "src/trace_processor/metrics/sql/android/android_package_list.sql",
+        "src/trace_processor/metrics/sql/android/android_powrails.sql",
+        "src/trace_processor/metrics/sql/android/android_proxy_power.sql",
+        "src/trace_processor/metrics/sql/android/android_simpleperf.sql",
+        "src/trace_processor/metrics/sql/android/android_startup.sql",
+        "src/trace_processor/metrics/sql/android/android_surfaceflinger.sql",
+        "src/trace_processor/metrics/sql/android/android_sysui_cuj.sql",
+        "src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql",
+        "src/trace_processor/metrics/sql/android/android_task_names.sql",
+        "src/trace_processor/metrics/sql/android/android_thread_time_in_state.sql",
+        "src/trace_processor/metrics/sql/android/android_trace_quality.sql",
+        "src/trace_processor/metrics/sql/android/composer_execution.sql",
+        "src/trace_processor/metrics/sql/android/composition_layers.sql",
+        "src/trace_processor/metrics/sql/android/cpu_info.sql",
+        "src/trace_processor/metrics/sql/android/display_metrics.sql",
+        "src/trace_processor/metrics/sql/android/frame_missed.sql",
+        "src/trace_processor/metrics/sql/android/g2d.sql",
+        "src/trace_processor/metrics/sql/android/g2d_duration.sql",
+        "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
+        "src/trace_processor/metrics/sql/android/java_heap_stats.sql",
+        "src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
+        "src/trace_processor/metrics/sql/android/power_drain_in_watts.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data.sql",
+        "src/trace_processor/metrics/sql/android/process_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/process_mem.sql",
+        "src/trace_processor/metrics/sql/android/process_metadata.sql",
+        "src/trace_processor/metrics/sql/android/process_oom_score.sql",
+        "src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql",
+        "src/trace_processor/metrics/sql/android/profiler_smaps.sql",
+        "src/trace_processor/metrics/sql/android/span_view_stats.sql",
+        "src/trace_processor/metrics/sql/android/startup/hsc.sql",
+        "src/trace_processor/metrics/sql/android/startup/launches.sql",
+        "src/trace_processor/metrics/sql/android/thread_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
+        "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
+        "src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_processes.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_thread_slice_with_cpu_time.sql",
+        "src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql",
+        "src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql",
+        "src/trace_processor/metrics/sql/chrome/estimated_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql",
+        "src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/gesture_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/rail_modes.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql",
+        "src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql",
+        "src/trace_processor/metrics/sql/chrome/touch_flow_event.sql",
+        "src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql",
+        "src/trace_processor/metrics/sql/chrome/touch_jank.sql",
+        "src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql",
+        "src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql",
+        "src/trace_processor/metrics/sql/experimental/frame_times.sql",
+        "src/trace_processor/metrics/sql/experimental/media_metric.sql",
+        "src/trace_processor/metrics/sql/experimental/reported_by_page.sql",
+        "src/trace_processor/metrics/sql/trace_metadata.sql",
+        "src/trace_processor/metrics/sql/trace_stats.sql",
+        "src/trace_processor/metrics/sql/webview/webview_power_usage.sql",
+    ],
+    outs = [
+        "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h",
+    ],
+    cmd = "$(location gen_amalgamated_sql_metrics_py) --cpp_out=$@ $(SRCS)",
+    exec_tools = [
+        ":gen_amalgamated_sql_metrics_py",
+    ],
+)
+
 perfetto_cc_proto_descriptor(
     name = "src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
     deps = [
         ":protos_perfetto_metrics_chrome_descriptor",
     ],
     outs = [
-        "src/trace_processor/metrics/chrome/all_chrome_metrics.descriptor.h",
+        "src/trace_processor/metrics/all_chrome_metrics.descriptor.h",
     ],
 )
 
@@ -1018,110 +1121,9 @@
     ],
 )
 
-perfetto_genrule(
-    name = "src_trace_processor_metrics_gen_merged_sql_metrics",
-    srcs = [
-        "src/trace_processor/metrics/android/android_batt.sql",
-        "src/trace_processor/metrics/android/android_camera.sql",
-        "src/trace_processor/metrics/android/android_cpu.sql",
-        "src/trace_processor/metrics/android/android_cpu_agg.sql",
-        "src/trace_processor/metrics/android/android_cpu_raw_metrics_per_core.sql",
-        "src/trace_processor/metrics/android/android_dma_heap.sql",
-        "src/trace_processor/metrics/android/android_fastrpc.sql",
-        "src/trace_processor/metrics/android/android_gpu.sql",
-        "src/trace_processor/metrics/android/android_hwcomposer.sql",
-        "src/trace_processor/metrics/android/android_hwui_metric.sql",
-        "src/trace_processor/metrics/android/android_hwui_threads.sql",
-        "src/trace_processor/metrics/android/android_ion.sql",
-        "src/trace_processor/metrics/android/android_jank.sql",
-        "src/trace_processor/metrics/android/android_lmk.sql",
-        "src/trace_processor/metrics/android/android_lmk_reason.sql",
-        "src/trace_processor/metrics/android/android_mem.sql",
-        "src/trace_processor/metrics/android/android_mem_unagg.sql",
-        "src/trace_processor/metrics/android/android_multiuser.sql",
-        "src/trace_processor/metrics/android/android_multiuser_populator.sql",
-        "src/trace_processor/metrics/android/android_package_list.sql",
-        "src/trace_processor/metrics/android/android_powrails.sql",
-        "src/trace_processor/metrics/android/android_proxy_power.sql",
-        "src/trace_processor/metrics/android/android_simpleperf.sql",
-        "src/trace_processor/metrics/android/android_startup.sql",
-        "src/trace_processor/metrics/android/android_surfaceflinger.sql",
-        "src/trace_processor/metrics/android/android_sysui_cuj.sql",
-        "src/trace_processor/metrics/android/android_sysui_cuj_jank_query.sql",
-        "src/trace_processor/metrics/android/android_task_names.sql",
-        "src/trace_processor/metrics/android/android_thread_time_in_state.sql",
-        "src/trace_processor/metrics/android/android_trace_quality.sql",
-        "src/trace_processor/metrics/android/composer_execution.sql",
-        "src/trace_processor/metrics/android/composition_layers.sql",
-        "src/trace_processor/metrics/android/cpu_info.sql",
-        "src/trace_processor/metrics/android/display_metrics.sql",
-        "src/trace_processor/metrics/android/frame_missed.sql",
-        "src/trace_processor/metrics/android/g2d.sql",
-        "src/trace_processor/metrics/android/g2d_duration.sql",
-        "src/trace_processor/metrics/android/global_counter_span_view.sql",
-        "src/trace_processor/metrics/android/gpu_counter_span_view.sql",
-        "src/trace_processor/metrics/android/java_heap_histogram.sql",
-        "src/trace_processor/metrics/android/java_heap_stats.sql",
-        "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
-        "src/trace_processor/metrics/android/power_drain_in_watts.sql",
-        "src/trace_processor/metrics/android/power_profile_data.sql",
-        "src/trace_processor/metrics/android/process_counter_span_view.sql",
-        "src/trace_processor/metrics/android/process_mem.sql",
-        "src/trace_processor/metrics/android/process_metadata.sql",
-        "src/trace_processor/metrics/android/process_oom_score.sql",
-        "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
-        "src/trace_processor/metrics/android/profiler_smaps.sql",
-        "src/trace_processor/metrics/android/span_view_stats.sql",
-        "src/trace_processor/metrics/android/startup/hsc.sql",
-        "src/trace_processor/metrics/android/startup/launches.sql",
-        "src/trace_processor/metrics/android/thread_counter_span_view.sql",
-        "src/trace_processor/metrics/android/unsymbolized_frames.sql",
-        "src/trace_processor/metrics/chrome/actual_power_by_category.sql",
-        "src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql",
-        "src/trace_processor/metrics/chrome/chrome_event_metadata.sql",
-        "src/trace_processor/metrics/chrome/chrome_processes.sql",
-        "src/trace_processor/metrics/chrome/chrome_thread_slice_with_cpu_time.sql",
-        "src/trace_processor/metrics/chrome/cpu_time_by_category.sql",
-        "src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql",
-        "src/trace_processor/metrics/chrome/estimated_power_by_category.sql",
-        "src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql",
-        "src/trace_processor/metrics/chrome/gesture_flow_event.sql",
-        "src/trace_processor/metrics/chrome/gesture_flow_event_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/gesture_jank.sql",
-        "src/trace_processor/metrics/chrome/rail_modes.sql",
-        "src/trace_processor/metrics/chrome/scroll_flow_event.sql",
-        "src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_task.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_touch_move.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_get_bitmap.sql",
-        "src/trace_processor/metrics/chrome/scroll_jank_cause_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/test_chrome_metric.sql",
-        "src/trace_processor/metrics/chrome/touch_flow_event.sql",
-        "src/trace_processor/metrics/chrome/touch_flow_event_queuing_delay.sql",
-        "src/trace_processor/metrics/chrome/touch_jank.sql",
-        "src/trace_processor/metrics/experimental/blink_gc_metric.sql",
-        "src/trace_processor/metrics/experimental/chrome_dropped_frames.sql",
-        "src/trace_processor/metrics/experimental/frame_times.sql",
-        "src/trace_processor/metrics/experimental/media_metric.sql",
-        "src/trace_processor/metrics/experimental/reported_by_page.sql",
-        "src/trace_processor/metrics/trace_metadata.sql",
-        "src/trace_processor/metrics/trace_stats.sql",
-        "src/trace_processor/metrics/webview/webview_power_usage.sql",
-    ],
-    outs = [
-        "src/trace_processor/metrics/sql_metrics.h",
-    ],
-    cmd = "$(location gen_merged_sql_metrics_py) --cpp_out=$@ $(SRCS)",
-    exec_tools = [
-        ":gen_merged_sql_metrics_py",
-    ],
-)
-
-# GN target: //src/trace_processor/metrics:lib
+# GN target: //src/trace_processor/metrics:metrics
 perfetto_filegroup(
-    name = "src_trace_processor_metrics_lib",
+    name = "src_trace_processor_metrics_metrics",
     srcs = [
         "src/trace_processor/metrics/metrics.cc",
         "src/trace_processor/metrics/metrics.h",
@@ -1152,6 +1154,8 @@
 perfetto_filegroup(
     name = "src_trace_processor_sqlite_sqlite",
     srcs = [
+        "src/trace_processor/sqlite/create_function.cc",
+        "src/trace_processor/sqlite/create_function.h",
         "src/trace_processor/sqlite/db_sqlite_table.cc",
         "src/trace_processor/sqlite/db_sqlite_table.h",
         "src/trace_processor/sqlite/query_cache.h",
@@ -3681,7 +3685,7 @@
         ":src_trace_processor_importers_memory_tracker_graph_processor",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
-        ":src_trace_processor_metrics_lib",
+        ":src_trace_processor_metrics_metrics",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
@@ -3750,7 +3754,7 @@
            PERFETTO_CONFIG.deps.sqlite +
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
            PERFETTO_CONFIG.deps.zlib + [
-        ":cc_merged_sql_metrics",
+        ":cc_amalgamated_sql_metrics",
     ],
     linkstatic = True,
 )
@@ -3780,7 +3784,7 @@
         ":src_trace_processor_importers_memory_tracker_graph_processor",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
-        ":src_trace_processor_metrics_lib",
+        ":src_trace_processor_metrics_metrics",
         ":src_trace_processor_rpc_httpd",
         ":src_trace_processor_rpc_rpc",
         ":src_trace_processor_sqlite_sqlite",
@@ -3847,7 +3851,7 @@
            PERFETTO_CONFIG.deps.sqlite +
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
            PERFETTO_CONFIG.deps.zlib + [
-        ":cc_merged_sql_metrics",
+        ":cc_amalgamated_sql_metrics",
     ],
 )
 
@@ -3964,7 +3968,7 @@
         ":src_trace_processor_importers_memory_tracker_graph_processor",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
-        ":src_trace_processor_metrics_lib",
+        ":src_trace_processor_metrics_metrics",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
@@ -4030,7 +4034,7 @@
            PERFETTO_CONFIG.deps.sqlite +
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
            PERFETTO_CONFIG.deps.zlib + [
-        ":cc_merged_sql_metrics",
+        ":cc_amalgamated_sql_metrics",
     ],
 )
 
@@ -4047,16 +4051,16 @@
 )
 
 perfetto_cc_library(
-    name = "cc_merged_sql_metrics",
-    hdrs = ["src/trace_processor/metrics/sql_metrics.h"],
+    name = "cc_amalgamated_sql_metrics",
+    hdrs = ["src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"],
 )
 
 perfetto_py_binary(
-    name = "gen_merged_sql_metrics_py",
+    name = "gen_amalgamated_sql_metrics_py",
     srcs = [
-        "tools/gen_merged_sql_metrics.py",
+        "tools/gen_amalgamated_sql_metrics.py",
     ],
-    main = "tools/gen_merged_sql_metrics.py",
+    main = "tools/gen_amalgamated_sql_metrics.py",
     python_version = "PY3",
 )
 
diff --git a/BUILD.extras b/BUILD.extras
index e546216..28bec08 100644
--- a/BUILD.extras
+++ b/BUILD.extras
@@ -9,16 +9,16 @@
 )
 
 perfetto_cc_library(
-    name = "cc_merged_sql_metrics",
-    hdrs = ["src/trace_processor/metrics/sql_metrics.h"],
+    name = "cc_amalgamated_sql_metrics",
+    hdrs = ["src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"],
 )
 
 perfetto_py_binary(
-    name = "gen_merged_sql_metrics_py",
+    name = "gen_amalgamated_sql_metrics_py",
     srcs = [
-        "tools/gen_merged_sql_metrics.py",
+        "tools/gen_amalgamated_sql_metrics.py",
     ],
-    main = "tools/gen_merged_sql_metrics.py",
+    main = "tools/gen_amalgamated_sql_metrics.py",
     python_version = "PY3",
 )
 
diff --git a/CHANGELOG b/CHANGELOG
index 8718f0c..e316684 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,8 @@
       This behavior affects only standalone builds and can be changed by setting
       enable_perfetto_x64_cpu_opt=false in the GN args.
   Trace Processor:
+    * Changed LIKE comparisions to be case-senstive. This may break existing
+      queries but was a necessary from a performance perspective.
     * Changed compiler flags, assume recent x64 CPUs (see above).
   UI:
     *
diff --git a/docs/analysis/metrics.md b/docs/analysis/metrics.md
index d5c181c..9e14b89 100644
--- a/docs/analysis/metrics.md
+++ b/docs/analysis/metrics.md
@@ -90,6 +90,55 @@
 
 There are several useful helpers functions which are available when writing a metric.
 
+### CREATE_FUNCTION.
+`CREATE_FUNCTION` allows you to define a parameterized SQL statement which
+is executable as a function. The inspiration from this function is the
+`CREATE FUNCTION` syntax which is available in other SQL engines (e.g.
+[Postgres](https://www.postgresql.org/docs/current/sql-createfunction.html)).
+
+NOTE: CREATE_FUNCTION only supports returning *exactly* a single value (i.e.
+single row and single column). For returning multiple a single row with
+multiple columns or multiples rows, see `CREATE_VIEW_FUNCTION` instead.
+
+Usage of `CREATE_FUNCTION` is as follows:
+```sql
+-- First, we define the function we'll use in the following statement.
+SELECT CREATE_FUNCTION(
+  -- First argument: prototype of the function; this is very similar to
+  -- function definitions in other languages - you set the function name
+  -- (IS_TS_IN_RANGE in this example) and the arguments
+  -- (ts, begin_ts and end_ts) along with their types (LONG for all
+  -- arguments here).
+  'IS_TS_IN_RANGE(ts LONG, begin_ts LONG, end_ts LONG)',
+  -- Second argument: the return type of the function. Only single values
+  -- can be returned in CREATE_FUNCTION. See CREATE_VIEW_FUNCTION for defining
+  -- a function returning multiple rows/columns.
+  'BOOL',
+  -- Third argument: the SQL body of the function. This should always be a
+  -- SELECT statement (even if you're not selecting from a table as in this
+  -- example). Arguments can be accessed by prefixing argument names
+  -- with $ (e.g. $ts, $begin_ts, $end_ts).
+  'SELECT $ts >= $begin_ts AND $ts <= $end_ts'
+);
+
+-- Now we can actually use the function in queries as if it was any other
+-- function.
+
+-- For example, it can appear in the SELECT to produce a column:
+SELECT ts, IS_TS_IN_RANGE(slice.ts, 100000, 200000) AS in_range
+FROM slice
+
+-- It can also appear in a where clause:
+SELECT ts
+FROM counter
+WHERE IS_TS_IN_RANGE(counter.ts, 100000, 200000) AS in_range
+
+-- It can even appear in a join on clause:
+SELECT slice.ts
+FROM launches
+JOIN slice ON IS_TS_IN_RANGE(slice.ts, launches.ts, launches.end_ts)
+```
+
 ### RUN_METRIC
 `RUN_METRIC` allows you to run another metric file. This allows you to use views
 or tables defined in that file without repeatition.
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index f3b9113..34e09de 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -17,6 +17,7 @@
 source_set("base") {
   sources = [
     "android_utils.h",
+    "base64.h",
     "circular_queue.h",
     "container_annotations.h",
     "crash_keys.h",
diff --git a/include/perfetto/ext/base/base64.h b/include/perfetto/ext/base/base64.h
new file mode 100644
index 0000000..030a83d
--- /dev/null
+++ b/include/perfetto/ext/base/base64.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+#define INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+
+#include <string>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/utils.h"  // For ssize_t.
+
+namespace perfetto {
+namespace base {
+
+// Returns the length of the destination string (included '=' padding).
+// Does NOT include the size of the string null terminator.
+inline size_t Base64EncSize(size_t src_size) {
+  return (src_size + 2) / 3 * 4;
+}
+
+// Returns the upper bound on the length of the destination buffer.
+// The actual decoded length might be <= the number returned here.
+inline size_t Base64DecSize(size_t src_size) {
+  return (src_size + 3) / 4 * 3;
+}
+
+// Does NOT null-terminate |dst|.
+ssize_t Base64Encode(const void* src,
+                     size_t src_size,
+                     char* dst,
+                     size_t dst_size);
+
+std::string Base64Encode(const void* src, size_t src_size);
+
+inline std::string Base64Encode(StringView sv) {
+  return Base64Encode(sv.data(), sv.size());
+}
+
+// Returns -1 in case of failure.
+ssize_t Base64Decode(const char* src,
+                     size_t src_size,
+                     uint8_t* dst,
+                     size_t dst_size);
+
+Optional<std::string> Base64Decode(const char* src, size_t src_size);
+
+inline Optional<std::string> Base64Decode(StringView sv) {
+  return Base64Decode(sv.data(), sv.size());
+}
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
diff --git a/include/perfetto/ext/base/circular_queue.h b/include/perfetto/ext/base/circular_queue.h
index 80574c2..82f4394 100644
--- a/include/perfetto/ext/base/circular_queue.h
+++ b/include/perfetto/ext/base/circular_queue.h
@@ -261,11 +261,7 @@
     size_t malloc_size = new_capacity * sizeof(T);
     PERFETTO_CHECK(malloc_size > new_capacity);
 
-    void* new_mem = nullptr;
-    // posix_memalign() wants at least void* alignment.
-    static constexpr size_t alignment = AlignUp<sizeof(void*)>(alignof(T));
-    PERFETTO_CHECK(posix_memalign(&new_mem, alignment, malloc_size) == 0);
-    T* new_vec = static_cast<T*>(new_mem);
+    T* new_vec = static_cast<T*>(AlignedAlloc(alignof(T), malloc_size));
 
     // Move all elements in the expanded array.
     size_t new_size = 0;
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index 2508aec..58444a8 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -125,7 +125,6 @@
                        const std::string& to_replace,
                        const std::string& replacement);
 std::string TrimLeading(const std::string& str);
-std::string Base64Encode(const void* raw, size_t size);
 
 // 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/utils.h b/include/perfetto/ext/base/utils.h
index a0aa2aa..cedf0ec 100644
--- a/include/perfetto/ext/base/utils.h
+++ b/include/perfetto/ext/base/utils.h
@@ -109,6 +109,8 @@
   return err == EAGAIN || err == EWOULDBLOCK;
 }
 
+void* AlignedAlloc(size_t alignment, size_t size);
+
 // setenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
 void SetEnv(const std::string& key, const std::string& value);
 
diff --git a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
index a7b966c..df33b13 100644
--- a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+++ b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
@@ -35,7 +35,7 @@
   }
 
   // Only when using |event_id|.
-  optional uint32 event_duration_ns = 3;
+  optional uint64 event_duration_ns = 3;
 
   // Only when using |counter_id|.
   optional int32 counter_value = 4;
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 36b2589..1c58fbc 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -7542,7 +7542,7 @@
   }
 
   // Only when using |event_id|.
-  optional uint32 event_duration_ns = 3;
+  optional uint64 event_duration_ns = 3;
 
   // Only when using |counter_id|.
   optional int32 counter_value = 4;
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 156c98b..f6d86b0 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -32,6 +32,7 @@
   ]
   sources = [
     "android_utils.cc",
+    "base64.cc",
     "crash_keys.cc",
     "ctrl_c_handler.cc",
     "event_fd.cc",
@@ -159,6 +160,7 @@
   ]
 
   sources = [
+    "base64_unittest.cc",
     "circular_queue_unittest.cc",
     "flat_set_unittest.cc",
     "getopt_compat_unittest.cc",
diff --git a/src/base/base64.cc b/src/base/base64.cc
new file mode 100644
index 0000000..437ad67
--- /dev/null
+++ b/src/base/base64.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/base64.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+constexpr char kPadding = '=';
+
+constexpr char kEncTable[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static_assert(sizeof(kEncTable) == (1u << 6) + sizeof('\0'), "Bad table size");
+
+// Maps an ASCII character to its 6-bit value. It only contains translations
+// from '+' to 'z'. Supports the standard (+/) and URL-safe (-_) alphabets.
+constexpr uint8_t kX = 0xff;  // Value used for invalid characters
+constexpr uint8_t kDecTable[] = {
+    62, kX, 62, kX, 63, 52, 53, 54, 55, 56,  // 00 - 09
+    57, 58, 59, 60, 61, kX, kX, kX, 0,  kX,  // 10 - 19
+    kX, kX, 0,  1,  2,  3,  4,  5,  6,  7,   // 20 - 29
+    8,  9,  10, 11, 12, 13, 14, 15, 16, 17,  // 30 - 39
+    18, 19, 20, 21, 22, 23, 24, 25, kX, kX,  // 40 - 49
+    kX, kX, 63, kX, 26, 27, 28, 29, 30, 31,  // 50 - 59
+    32, 33, 34, 35, 36, 37, 38, 39, 40, 41,  // 60 - 69
+    42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  // 70 - 79
+};
+constexpr char kMinDecChar = '+';
+constexpr char kMaxDecChar = 'z';
+static_assert(kMaxDecChar - kMinDecChar <= sizeof(kDecTable), "Bad table size");
+
+inline uint8_t DecodeChar(char c) {
+  if (c < kMinDecChar || c > kMaxDecChar)
+    return kX;
+  return kDecTable[c - kMinDecChar];
+}
+
+}  // namespace
+
+ssize_t Base64Encode(const void* src,
+                     size_t src_size,
+                     char* dst,
+                     size_t dst_size) {
+  const size_t padded_dst_size = Base64EncSize(src_size);
+  if (dst_size < padded_dst_size)
+    return -1;  // Not enough space in output.
+
+  const uint8_t* rd = static_cast<const uint8_t*>(src);
+  const uint8_t* const end = rd + src_size;
+  size_t wr_size = 0;
+  while (rd < end) {
+    uint8_t s[3]{};
+    s[0] = *(rd++);
+    dst[wr_size++] = kEncTable[s[0] >> 2];
+
+    uint8_t carry0 = static_cast<uint8_t>((s[0] & 0x03) << 4);
+    if (PERFETTO_LIKELY(rd < end)) {
+      s[1] = *(rd++);
+      dst[wr_size++] = kEncTable[carry0 | (s[1] >> 4)];
+    } else {
+      dst[wr_size++] = kEncTable[carry0];
+      dst[wr_size++] = kPadding;
+      dst[wr_size++] = kPadding;
+      break;
+    }
+
+    uint8_t carry1 = static_cast<uint8_t>((s[1] & 0x0f) << 2);
+    if (PERFETTO_LIKELY(rd < end)) {
+      s[2] = *(rd++);
+      dst[wr_size++] = kEncTable[carry1 | (s[2] >> 6)];
+    } else {
+      dst[wr_size++] = kEncTable[carry1];
+      dst[wr_size++] = kPadding;
+      break;
+    }
+
+    dst[wr_size++] = kEncTable[s[2] & 0x3f];
+  }
+  PERFETTO_DCHECK(wr_size == padded_dst_size);
+  return static_cast<ssize_t>(padded_dst_size);
+}
+
+std::string Base64Encode(const void* src, size_t src_size) {
+  std::string dst;
+  dst.resize(Base64EncSize(src_size));
+  auto res = Base64Encode(src, src_size, &dst[0], dst.size());
+  PERFETTO_CHECK(res == static_cast<ssize_t>(dst.size()));
+  return dst;
+}
+
+ssize_t Base64Decode(const char* src,
+                     size_t src_size,
+                     uint8_t* dst,
+                     size_t dst_size) {
+  const size_t min_dst_size = Base64DecSize(src_size);
+  if (dst_size < min_dst_size)
+    return -1;
+
+  const char* rd = src;
+  const char* const end = src + src_size;
+  size_t wr_size = 0;
+
+  char s[4]{};
+  while (rd < end) {
+    uint8_t d[4];
+    for (uint32_t j = 0; j < 4; j++) {
+      // Padding is only feasible for the last 2 chars of each group of 4.
+      s[j] = rd < end ? *(rd++) : (j < 2 ? '\0' : kPadding);
+      d[j] = DecodeChar(s[j]);
+      if (d[j] == kX)
+        return -1;  // Invalid input char.
+    }
+    dst[wr_size] = static_cast<uint8_t>((d[0] << 2) | (d[1] >> 4));
+    dst[wr_size + 1] = static_cast<uint8_t>((d[1] << 4) | (d[2] >> 2));
+    dst[wr_size + 2] = static_cast<uint8_t>((d[2] << 6) | (d[3]));
+    wr_size += 3;
+  }
+
+  PERFETTO_CHECK(wr_size <= dst_size);
+  wr_size -= (s[3] == kPadding ? 1 : 0) + (s[2] == kPadding ? 1 : 0);
+  return static_cast<ssize_t>(wr_size);
+}
+
+Optional<std::string> Base64Decode(const char* src, size_t src_size) {
+  std::string dst;
+  dst.resize(Base64DecSize(src_size));
+  auto res = Base64Decode(src, src_size, reinterpret_cast<uint8_t*>(&dst[0]),
+                          dst.size());
+  if (res < 0)
+    return nullopt;  // Decoding error.
+
+  PERFETTO_CHECK(res <= static_cast<ssize_t>(dst.size()));
+  dst.resize(static_cast<size_t>(res));
+  return make_optional(dst);
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/base64_unittest.cc b/src/base/base64_unittest.cc
new file mode 100644
index 0000000..fb131d9
--- /dev/null
+++ b/src/base/base64_unittest.cc
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/base64.h"
+
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/utils.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace base {
+namespace {
+
+struct TestPattern {
+  size_t decoded_len;
+  const char* decoded;
+  const char* encoded;
+};
+
+TestPattern kPatterns[] = {
+
+    // Basic bit patterns;
+    // values obtained with "echo -n '...' | uuencode -m test"
+
+    {1, "\000", "AA=="},
+    {1, "\001", "AQ=="},
+    {1, "\002", "Ag=="},
+    {1, "\004", "BA=="},
+    {1, "\010", "CA=="},
+    {1, "\020", "EA=="},
+    {1, "\040", "IA=="},
+    {1, "\100", "QA=="},
+    {1, "\200", "gA=="},
+
+    {1, "\377", "/w=="},
+    {1, "\376", "/g=="},
+    {1, "\375", "/Q=="},
+    {1, "\373", "+w=="},
+    {1, "\367", "9w=="},
+    {1, "\357", "7w=="},
+    {1, "\337", "3w=="},
+    {1, "\277", "vw=="},
+    {1, "\177", "fw=="},
+    {2, "\000\000", "AAA="},
+    {2, "\000\001", "AAE="},
+    {2, "\000\002", "AAI="},
+    {2, "\000\004", "AAQ="},
+    {2, "\000\010", "AAg="},
+    {2, "\000\020", "ABA="},
+    {2, "\000\040", "ACA="},
+    {2, "\000\100", "AEA="},
+    {2, "\000\200", "AIA="},
+    {2, "\001\000", "AQA="},
+    {2, "\002\000", "AgA="},
+    {2, "\004\000", "BAA="},
+    {2, "\010\000", "CAA="},
+    {2, "\020\000", "EAA="},
+    {2, "\040\000", "IAA="},
+    {2, "\100\000", "QAA="},
+    {2, "\200\000", "gAA="},
+
+    {2, "\377\377", "//8="},
+    {2, "\377\376", "//4="},
+    {2, "\377\375", "//0="},
+    {2, "\377\373", "//s="},
+    {2, "\377\367", "//c="},
+    {2, "\377\357", "/+8="},
+    {2, "\377\337", "/98="},
+    {2, "\377\277", "/78="},
+    {2, "\377\177", "/38="},
+    {2, "\376\377", "/v8="},
+    {2, "\375\377", "/f8="},
+    {2, "\373\377", "+/8="},
+    {2, "\367\377", "9/8="},
+    {2, "\357\377", "7/8="},
+    {2, "\337\377", "3/8="},
+    {2, "\277\377", "v/8="},
+    {2, "\177\377", "f/8="},
+
+    {3, "\000\000\000", "AAAA"},
+    {3, "\000\000\001", "AAAB"},
+    {3, "\000\000\002", "AAAC"},
+    {3, "\000\000\004", "AAAE"},
+    {3, "\000\000\010", "AAAI"},
+    {3, "\000\000\020", "AAAQ"},
+    {3, "\000\000\040", "AAAg"},
+    {3, "\000\000\100", "AABA"},
+    {3, "\000\000\200", "AACA"},
+    {3, "\000\001\000", "AAEA"},
+    {3, "\000\002\000", "AAIA"},
+    {3, "\000\004\000", "AAQA"},
+    {3, "\000\010\000", "AAgA"},
+    {3, "\000\020\000", "ABAA"},
+    {3, "\000\040\000", "ACAA"},
+    {3, "\000\100\000", "AEAA"},
+    {3, "\000\200\000", "AIAA"},
+    {3, "\001\000\000", "AQAA"},
+    {3, "\002\000\000", "AgAA"},
+    {3, "\004\000\000", "BAAA"},
+    {3, "\010\000\000", "CAAA"},
+    {3, "\020\000\000", "EAAA"},
+    {3, "\040\000\000", "IAAA"},
+    {3, "\100\000\000", "QAAA"},
+    {3, "\200\000\000", "gAAA"},
+
+    {3, "\377\377\377", "////"},
+    {3, "\377\377\376", "///+"},
+    {3, "\377\377\375", "///9"},
+    {3, "\377\377\373", "///7"},
+    {3, "\377\377\367", "///3"},
+    {3, "\377\377\357", "///v"},
+    {3, "\377\377\337", "///f"},
+    {3, "\377\377\277", "//+/"},
+    {3, "\377\377\177", "//9/"},
+    {3, "\377\376\377", "//7/"},
+    {3, "\377\375\377", "//3/"},
+    {3, "\377\373\377", "//v/"},
+    {3, "\377\367\377", "//f/"},
+    {3, "\377\357\377", "/+//"},
+    {3, "\377\337\377", "/9//"},
+    {3, "\377\277\377", "/7//"},
+    {3, "\377\177\377", "/3//"},
+    {3, "\376\377\377", "/v//"},
+    {3, "\375\377\377", "/f//"},
+    {3, "\373\377\377", "+///"},
+    {3, "\367\377\377", "9///"},
+    {3, "\357\377\377", "7///"},
+    {3, "\337\377\377", "3///"},
+    {3, "\277\377\377", "v///"},
+    {3, "\177\377\377", "f///"},
+
+    // Random numbers: values obtained with
+    //
+    //  #! /bin/bash
+    //  dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
+    //  od -N $1 -t o1 /tmp/bar.random
+    //  uuencode -m test < /tmp/bar.random
+    //
+    // where $1 is the number of bytes (2, 3)
+
+    {2, "\243\361", "o/E="},
+    {2, "\024\167", "FHc="},
+    {2, "\313\252", "y6o="},
+    {2, "\046\041", "JiE="},
+    {2, "\145\236", "ZZ4="},
+    {2, "\254\325", "rNU="},
+    {2, "\061\330", "Mdg="},
+    {2, "\245\032", "pRo="},
+    {2, "\006\000", "BgA="},
+    {2, "\375\131", "/Vk="},
+    {2, "\303\210", "w4g="},
+    {2, "\040\037", "IB8="},
+    {2, "\261\372", "sfo="},
+    {2, "\335\014", "3Qw="},
+    {2, "\233\217", "m48="},
+    {2, "\373\056", "+y4="},
+    {2, "\247\232", "p5o="},
+    {2, "\107\053", "Rys="},
+    {2, "\204\077", "hD8="},
+    {2, "\276\211", "vok="},
+    {2, "\313\110", "y0g="},
+    {2, "\363\376", "8/4="},
+    {2, "\251\234", "qZw="},
+    {2, "\103\262", "Q7I="},
+    {2, "\142\312", "Yso="},
+    {2, "\067\211", "N4k="},
+    {2, "\220\001", "kAE="},
+    {2, "\152\240", "aqA="},
+    {2, "\367\061", "9zE="},
+    {2, "\133\255", "W60="},
+    {2, "\176\035", "fh0="},
+    {2, "\032\231", "Gpk="},
+
+    {3, "\013\007\144", "Cwdk"},
+    {3, "\030\112\106", "GEpG"},
+    {3, "\047\325\046", "J9Um"},
+    {3, "\310\160\022", "yHAS"},
+    {3, "\131\100\237", "WUCf"},
+    {3, "\064\342\134", "NOJc"},
+    {3, "\010\177\004", "CH8E"},
+    {3, "\345\147\205", "5WeF"},
+    {3, "\300\343\360", "wOPw"},
+    {3, "\061\240\201", "MaCB"},
+    {3, "\225\333\044", "ldsk"},
+    {3, "\215\137\352", "jV/q"},
+    {3, "\371\147\160", "+Wdw"},
+    {3, "\030\320\051", "GNAp"},
+    {3, "\044\174\241", "JHyh"},
+    {3, "\260\127\037", "sFcf"},
+    {3, "\111\045\033", "SSUb"},
+    {3, "\202\114\107", "gkxH"},
+    {3, "\057\371\042", "L/ki"},
+    {3, "\223\247\244", "k6ek"},
+    {3, "\047\216\144", "J45k"},
+    {3, "\203\070\327", "gzjX"},
+    {3, "\247\140\072", "p2A6"},
+    {3, "\124\115\116", "VE1O"},
+    {3, "\157\162\050", "b3Io"},
+    {3, "\357\223\004", "75ME"},
+    {3, "\052\117\156", "Kk9u"},
+    {3, "\347\154\000", "52wA"},
+    {3, "\303\012\142", "wwpi"},
+    {3, "\060\035\362", "MB3y"},
+    {3, "\130\226\361", "WJbx"},
+    {3, "\173\013\071", "ews5"},
+    {3, "\336\004\027", "3gQX"},
+    {3, "\357\366\234", "7/ac"},
+    {3, "\353\304\111", "68RJ"},
+    {3, "\024\264\131", "FLRZ"},
+    {3, "\075\114\251", "PUyp"},
+    {3, "\315\031\225", "zRmV"},
+    {3, "\154\201\276", "bIG+"},
+    {3, "\200\066\072", "gDY6"},
+    {3, "\142\350\267", "Yui3"},
+    {3, "\033\000\166", "GwB2"},
+    {3, "\210\055\077", "iC0/"},
+    {3, "\341\037\124", "4R9U"},
+    {3, "\161\103\152", "cUNq"},
+    {3, "\270\142\131", "uGJZ"},
+    {3, "\337\076\074", "3z48"},
+    {3, "\375\106\362", "/Uby"},
+    {3, "\227\301\127", "l8FX"},
+    {3, "\340\002\234", "4AKc"},
+    {3, "\121\064\033", "UTQb"},
+    {3, "\157\134\143", "b1xj"},
+    {3, "\247\055\327", "py3X"},
+    {3, "\340\142\005", "4GIF"},
+    {3, "\060\260\143", "MLBj"},
+    {3, "\075\203\170", "PYN4"},
+    {3, "\143\160\016", "Y3AO"},
+    {3, "\313\013\063", "ywsz"},
+    {3, "\174\236\135", "fJ5d"},
+    {3, "\103\047\026", "QycW"},
+    {3, "\365\005\343", "9QXj"},
+    {3, "\271\160\223", "uXCT"},
+    {3, "\362\255\172", "8q16"},
+    {3, "\113\012\015", "SwoN"},
+
+    // various lengths, generated by this python script:
+    //
+    // from string import lowercase as lc
+    // for i in range(27):
+    //   print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
+    //                                     lc[:i].encode('base64').strip())
+
+    {0, "abcdefghijklmnopqrstuvwxyz", ""},
+    {1, "abcdefghijklmnopqrstuvwxyz", "YQ=="},
+    {2, "abcdefghijklmnopqrstuvwxyz", "YWI="},
+    {3, "abcdefghijklmnopqrstuvwxyz", "YWJj"},
+    {4, "abcdefghijklmnopqrstuvwxyz", "YWJjZA=="},
+    {5, "abcdefghijklmnopqrstuvwxyz", "YWJjZGU="},
+    {6, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVm"},
+    {7, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZw=="},
+    {8, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2g="},
+    {9, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hp"},
+    {10, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpag=="},
+    {11, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpams="},
+    {12, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamts"},
+    {13, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbQ=="},
+    {14, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW4="},
+    {15, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5v"},
+    {16, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcA=="},
+    {17, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHE="},
+    {18, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFy"},
+    {19, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFycw=="},
+    {20, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q="},
+    {21, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1"},
+    {22, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg=="},
+    {23, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc="},
+    {24, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4"},
+    {25, "abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ=="},
+    {26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="},
+};
+
+TEST(Base64Test, Encode) {
+  EXPECT_EQ(Base64Encode(""), "");
+  EXPECT_EQ(Base64Encode("f"), "Zg==");
+  EXPECT_EQ(Base64Encode("fo"), "Zm8=");
+  EXPECT_EQ(Base64Encode("foo"), "Zm9v");
+  EXPECT_EQ(Base64Encode("foob"), "Zm9vYg==");
+  EXPECT_EQ(Base64Encode("fooba"), "Zm9vYmE=");
+  EXPECT_EQ(Base64Encode("foobar"), "Zm9vYmFy");
+  EXPECT_EQ(Base64Encode("\xff"), "/w==");
+  EXPECT_EQ(Base64Encode("\xff\xfe"), "//4=");
+  EXPECT_EQ(Base64Encode("\xff\xfe\xfd"), "//79");
+  EXPECT_EQ(Base64Encode("\xff\xfe\xfd\xfc"), "//79/A==");
+
+  for (size_t i = 0; i < ArraySize(kPatterns); ++i) {
+    const auto& p = kPatterns[i];
+    std::string res = Base64Encode(StringView(p.decoded, p.decoded_len));
+    EXPECT_EQ(p.encoded, res);
+  }
+
+  // Error cases
+  char buf[4];
+  EXPECT_EQ(0, Base64Encode("", 0, buf, 0));
+  EXPECT_EQ(0, Base64Encode("", 0, buf, 1));
+  EXPECT_EQ(-1, Base64Encode("a", 1, buf, 0));
+  EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 0));
+  EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 1));
+  EXPECT_EQ(-1, Base64Encode("abc", 3, buf, 3));
+  EXPECT_EQ(4, Base64Encode("abc", 3, buf, 4));
+}
+
+TEST(Base64Test, Decode) {
+  EXPECT_EQ(Base64Decode(""), "");
+  EXPECT_EQ(Base64Decode("Zg=="), "f");
+  EXPECT_EQ(Base64Decode("Zg="), "f");
+  EXPECT_EQ(Base64Decode("Zg"), "f");
+  EXPECT_EQ(Base64Decode("Zm8="), "fo");
+  EXPECT_EQ(Base64Decode("Zm8"), "fo");
+  EXPECT_EQ(Base64Decode("Zm9v"), "foo");
+  EXPECT_EQ(Base64Decode("Zm9vYg=="), "foob");
+  EXPECT_EQ(Base64Decode("Zm9vYg="), "foob");
+  EXPECT_EQ(Base64Decode("Zm9vYg"), "foob");
+  EXPECT_EQ(Base64Decode("Zm9vYmE="), "fooba");
+  EXPECT_EQ(Base64Decode("Zm9vYmE"), "fooba");
+  EXPECT_EQ(Base64Decode("Zm9vYmFy"), "foobar");
+  EXPECT_EQ(Base64Decode("/w=="), "\xff");
+  EXPECT_EQ(Base64Decode("/w="), "\xff");
+  EXPECT_EQ(Base64Decode("/w"), "\xff");
+  EXPECT_EQ(Base64Decode("//4="), "\xff\xfe");
+  EXPECT_EQ(Base64Decode("//4"), "\xff\xfe");
+  EXPECT_EQ(Base64Decode("//79"), "\xff\xfe\xfd");
+  EXPECT_EQ(Base64Decode("//79/A=="), "\xff\xfe\xfd\xfc");
+  EXPECT_EQ(Base64Decode("//79/A="), "\xff\xfe\xfd\xfc");
+  EXPECT_EQ(Base64Decode("//79/A"), "\xff\xfe\xfd\xfc");
+
+  for (size_t i = 0; i < ArraySize(kPatterns); ++i) {
+    const auto& p = kPatterns[i];
+    Optional<std::string> dec = Base64Decode(StringView(p.encoded));
+    EXPECT_TRUE(dec.has_value());
+    EXPECT_EQ(dec.value(), StringView(p.decoded, p.decoded_len).ToStdString());
+  }
+
+  // Error cases:
+  EXPECT_EQ(Base64Decode("Z"), nullopt);
+  EXPECT_EQ(Base64Decode("Zm9vY"), nullopt);
+
+  uint8_t buf[4];
+  EXPECT_EQ(Base64Decode("", 0, buf, 2), 0);       // Valid, 0 len.
+  EXPECT_EQ(Base64Decode("Z", 1, buf, 1), -1);     // Invalid input.
+  EXPECT_EQ(Base64Decode("Zg==", 4, buf, 1), -1);  // Not enough dst space.
+}
+
+}  // namespace
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/getopt_compat_unittest.cc b/src/base/getopt_compat_unittest.cc
index aeeb2ed..0ae3721 100644
--- a/src/base/getopt_compat_unittest.cc
+++ b/src/base/getopt_compat_unittest.cc
@@ -113,7 +113,6 @@
   sops = "h";
   this->SetCmdline({"argv0", "-h"});
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'h');
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 2);
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), -1);
   EXPECT_EQ(t.optind, 2);
@@ -137,7 +136,6 @@
   sops = "abc";
   this->SetCmdline({"argv0", "-c", "-a", "-b"});
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'c');
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 2);
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'a');
   EXPECT_EQ(t.optind, 3);
@@ -149,25 +147,19 @@
   sops = "abc";
   this->SetCmdline({"argv0", "-c", "-a", "--", "nonopt"});
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'c');
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 2);
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'a');
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 3);
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), -1);
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 4);
 
   sops = "abc";
   this->SetCmdline({"argv0", "-cb"});
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'c');
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 1);
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'b');
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 2);
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), -1);
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.optind, 2);
 
   sops = "abc";
@@ -232,7 +224,6 @@
     const char* sops = "";
     this->SetCmdline({"argv0"});
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), -1);
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 1);
   }
 
@@ -243,7 +234,6 @@
     const char* sops = "";
     this->SetCmdline({"argv0", "--unknown"});
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), '?');
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 2);
   }
 
@@ -256,13 +246,10 @@
     const char* sops = "";
     this->SetCmdline({"argv0", "--two", "--one"});
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), 2);
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 2);
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), 1);
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 3);
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), -1);
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 3);
   }
 
@@ -275,13 +262,10 @@
     const char* sops = "";
     this->SetCmdline({"argv0", "--two", "--one", "--not-an-opt"});
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), 2);
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 2);
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), 1);
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 3);
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), '?');
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 4);
   }
 
@@ -355,7 +339,6 @@
 
     this->SetCmdline({"argv0"});
     EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), -1);
-    EXPECT_EQ(t.optarg, nullptr);
     EXPECT_EQ(t.optind, 1);
 
     this->SetCmdline({"argv0", "-13", "--two", "--three", "--", "--one"});
@@ -393,7 +376,6 @@
   const char* sops = "ab:";
   this->SetCmdline({"argv0", "-a", "-c", "-b"});
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'a');
-  EXPECT_EQ(t.optarg, nullptr);
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), '?');
   EXPECT_EQ(t.optopt, 'c');
   EXPECT_EQ(t.getopt(this->argc, this->argv, sops), '?');
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index af663b8..5efca77 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -33,11 +33,6 @@
 
 namespace perfetto {
 namespace base {
-namespace {
-constexpr char kBase64Table[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-    "abcdefghijklmnopqrstuvwxyz0123456789+/";
-}
 
 // Locale-independant as possible version of strtod.
 double StrToD(const char* nptr, char** endptr) {
@@ -215,44 +210,6 @@
   return idx == std::string::npos ? str : str.substr(idx);
 }
 
-std::string Base64Encode(const void* raw, size_t size) {
-  // The following three cases are based on the tables in the example
-  // section in https://en.wikipedia.org/wiki/Base64. We process three
-  // input bytes at a time, emitting 4 output bytes at a time.
-  const uint8_t* ptr = static_cast<const uint8_t*>(raw);
-  size_t ii = 0;
-
-  std::string out;
-  out.reserve((size + 2) * 4 / 3);
-
-  // While possible, process three input bytes.
-  for (; ii + 3 <= size; ii += 3) {
-    uint32_t twentyfour_bits =
-        (uint32_t(ptr[ii]) << 16) | (uint32_t(ptr[ii + 1]) << 8) | ptr[ii + 2];
-    out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
-    out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
-    out.push_back(kBase64Table[(twentyfour_bits >> 6) & 0x3f]);
-    out.push_back(kBase64Table[twentyfour_bits & 0x3f]);
-  }
-  if (ii + 2 <= size) {  // Process two input bytes.
-    uint32_t twentyfour_bits =
-        (uint32_t(ptr[ii]) << 16) | (uint32_t(ptr[ii + 1]) << 8);
-    out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
-    out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
-    out.push_back(kBase64Table[(twentyfour_bits >> 6) & 0x3f]);
-    out.push_back('=');  // Emit padding.
-    return out;
-  }
-  if (ii + 1 <= size) {  // Process a single input byte.
-    uint32_t twentyfour_bits = (uint32_t(ptr[ii]) << 16);
-    out.push_back(kBase64Table[(twentyfour_bits >> 18)]);
-    out.push_back(kBase64Table[(twentyfour_bits >> 12) & 0x3f]);
-    out.push_back('=');  // Emit padding.
-    out.push_back('=');  // Emit padding.
-  }
-  return out;
-}
-
 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 0787802..39803ff 100644
--- a/src/base/string_utils_unittest.cc
+++ b/src/base/string_utils_unittest.cc
@@ -303,29 +303,6 @@
   EXPECT_EQ(TrimLeading(" aaaaa     "), "aaaaa     ");
 }
 
-TEST(StringUtilsTest, Base64Encode) {
-  auto base64_encode = [](const std::string& str) {
-    return Base64Encode(str.c_str(), str.size());
-  };
-
-  EXPECT_EQ(base64_encode(""), "");
-  EXPECT_EQ(base64_encode("f"), "Zg==");
-  EXPECT_EQ(base64_encode("fo"), "Zm8=");
-  EXPECT_EQ(base64_encode("foo"), "Zm9v");
-  EXPECT_EQ(base64_encode("foob"), "Zm9vYg==");
-  EXPECT_EQ(base64_encode("fooba"), "Zm9vYmE=");
-  EXPECT_EQ(base64_encode("foobar"), "Zm9vYmFy");
-
-  EXPECT_EQ(Base64Encode("foo\0bar", 7), "Zm9vAGJhcg==");
-
-  std::vector<uint8_t> buffer = {0x04, 0x53, 0x42, 0x35,
-                                 0x32, 0xFF, 0x00, 0xFE};
-  EXPECT_EQ(Base64Encode(buffer.data(), buffer.size()), "BFNCNTL/AP4=");
-
-  buffer = {0xfb, 0xf0, 0x3e, 0x07, 0xfc};
-  EXPECT_EQ(Base64Encode(buffer.data(), buffer.size()), "+/A+B/w=");
-}
-
 TEST(StringUtilsTest, StringCopy) {
   // Nothing should be written when |dst_size| = 0.
   {
diff --git a/src/base/utils.cc b/src/base/utils.cc
index 732117a..c080388 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -39,6 +39,7 @@
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <Windows.h>
 #include <io.h>
+#include <malloc.h>  // For _aligned_malloc().
 #endif
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
@@ -241,5 +242,21 @@
   return path;
 }
 
+void* AlignedAlloc(size_t alignment, size_t size) {
+  void* res = nullptr;
+  alignment = AlignUp<sizeof(void*)>(alignment);  // At least pointer size.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // Window's _aligned_malloc() has a nearly identically signature to Unix's
+  // aligned_alloc() but its arguments are obviously swapped.
+  res = _aligned_malloc(size, alignment);
+#else
+  // aligned_alloc() has been introduced in Android only in API 28.
+  // Also NaCl and Fuchsia seems to have only posix_memalign().
+  ignore_result(posix_memalign(&res, alignment, size));
+#endif
+  PERFETTO_CHECK(res);
+  return res;
+}
+
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 02231ce..65f84e6 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -337,7 +337,7 @@
       "analysis",
       "db",
       "importers/common",
-      "metrics:lib",
+      "metrics",
       "sqlite",
       "storage",
       "tables",
@@ -365,7 +365,7 @@
       "../../src/profiling/symbolizer",
       "../../src/profiling/symbolizer:symbolize_database",
       "../base",
-      "metrics:lib",
+      "metrics",
       "util",
     ]
     if (enable_perfetto_trace_processor_linenoise) {
diff --git a/src/trace_processor/importers/proto/metadata_module.cc b/src/trace_processor/importers/proto/metadata_module.cc
index b811320..cfb644b 100644
--- a/src/trace_processor/importers/proto/metadata_module.cc
+++ b/src/trace_processor/importers/proto/metadata_module.cc
@@ -16,7 +16,7 @@
 
 #include "src/trace_processor/importers/proto/metadata_module.h"
 
-#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/base64.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index f212645..0c3f315 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -372,8 +372,9 @@
       name_id = context_->storage->InternString(event.event_name());
     }
     TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-    context_->slice_tracker->Scoped(ts, track_id, cat_id, name_id,
-                                    event.event_duration_ns(), args_fn);
+    context_->slice_tracker->Scoped(
+        ts, track_id, cat_id, name_id,
+        static_cast<int64_t>(event.event_duration_ns()), args_fn);
   } else if (event.has_counter_id() || event.has_counter_name()) {
     if (event.has_counter_id()) {
       auto cid = event.counter_id();
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index d9106a1..8731f78 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -13,159 +13,21 @@
 # limitations under the License.
 
 import("../../../gn/perfetto.gni")
+import("../../../gn/perfetto_cc_proto_descriptor.gni")
 import("../../../gn/test.gni")
 
-sql_files = [
-  "trace_metadata.sql",
-  "trace_stats.sql",
-  "android/android_batt.sql",
-  "android/android_camera.sql",
-  "android/android_cpu.sql",
-  "android/android_surfaceflinger.sql",
-  "android/android_cpu_agg.sql",
-  "android/android_cpu_raw_metrics_per_core.sql",
-  "android/android_dma_heap.sql",
-  "android/android_fastrpc.sql",
-  "android/android_gpu.sql",
-  "android/android_hwui_threads.sql",
-  "android/android_mem.sql",
-  "android/android_mem_unagg.sql",
-  "android/android_ion.sql",
-  "android/composer_execution.sql",
-  "android/composition_layers.sql",
-  "android/frame_missed.sql",
-  "android/android_jank.sql",
-  "android/android_lmk_reason.sql",
-  "android/android_lmk.sql",
-  "android/android_powrails.sql",
-  "android/android_proxy_power.sql",
-  "android/android_simpleperf.sql",
-  "android/android_startup.sql",
-  "android/android_package_list.sql",
-  "android/android_task_names.sql",
-  "android/android_thread_time_in_state.sql",
-  "android/android_trace_quality.sql",
-  "android/cpu_info.sql",
-  "android/display_metrics.sql",
-  "android/g2d.sql",
-  "android/g2d_duration.sql",
-  "android/android_hwcomposer.sql",
-  "android/android_hwui_metric.sql",
-  "android/java_heap_histogram.sql",
-  "android/java_heap_stats.sql",
-  "android/power_drain_in_watts.sql",
-  "android/power_profile_data.sql",
-  "android/process_unagg_mem_view.sql",
-  "android/process_mem.sql",
-  "android/process_metadata.sql",
-  "android/process_oom_score.sql",
-  "android/profiler_smaps.sql",
-  "android/mem_stats_priority_breakdown.sql",
-  "android/android_multiuser.sql",
-  "android/android_multiuser_populator.sql",
-  "android/span_view_stats.sql",
-  "android/android_sysui_cuj.sql",
-  "android/android_sysui_cuj_jank_query.sql",
-  "android/process_counter_span_view.sql",
-  "android/global_counter_span_view.sql",
-  "android/gpu_counter_span_view.sql",
-  "android/thread_counter_span_view.sql",
-  "android/unsymbolized_frames.sql",
-  "android/startup/launches.sql",
-  "android/startup/hsc.sql",
-  "chrome/actual_power_by_category.sql",
-  "chrome/actual_power_by_rail_mode.sql",
-  "chrome/chrome_event_metadata.sql",
-  "chrome/chrome_processes.sql",
-  "chrome/chrome_thread_slice_with_cpu_time.sql",
-  "chrome/cpu_time_by_category.sql",
-  "chrome/cpu_time_by_rail_mode.sql",
-  "chrome/estimated_power_by_category.sql",
-  "chrome/estimated_power_by_rail_mode.sql",
-  "chrome/gesture_jank.sql",
-  "chrome/gesture_flow_event.sql",
-  "chrome/gesture_flow_event_queuing_delay.sql",
-  "chrome/rail_modes.sql",
-  "chrome/scroll_jank.sql",
-  "chrome/scroll_jank_cause.sql",
-  "chrome/scroll_jank_cause_blocking_task.sql",
-  "chrome/scroll_jank_cause_blocking_touch_move.sql",
-  "chrome/scroll_jank_cause_get_bitmap.sql",
-  "chrome/scroll_jank_cause_queuing_delay.sql",
-  "chrome/scroll_flow_event.sql",
-  "chrome/scroll_flow_event_queuing_delay.sql",
-  "chrome/test_chrome_metric.sql",
-  "chrome/touch_flow_event.sql",
-  "chrome/touch_flow_event_queuing_delay.sql",
-  "chrome/touch_jank.sql",
-  "experimental/blink_gc_metric.sql",
-  "experimental/chrome_dropped_frames.sql",
-  "experimental/frame_times.sql",
-  "experimental/media_metric.sql",
-  "experimental/reported_by_page.sql",
-  "webview/webview_power_usage.sql",
-]
-
-config("gen_config") {
-  include_dirs = [ "${root_gen_dir}/${perfetto_root_path}" ]
-}
-
-action("gen_merged_sql_metrics") {
-  script = "../../../tools/gen_merged_sql_metrics.py"
-  generated_header = "${target_gen_dir}/sql_metrics.h"
-  args = rebase_path(sql_files, root_build_dir) + [
-           "--cpp_out",
-           rebase_path(generated_header, root_build_dir),
-         ]
-  inputs = sql_files
-  outputs = [ generated_header ]
-  public_configs = [ ":gen_config" ]
-}
-
-action("gen_cc_metrics_descriptor") {
+perfetto_cc_proto_descriptor("gen_cc_metrics_descriptor") {
+  descriptor_name = "metrics.descriptor"
   descriptor_target = "../../../protos/perfetto/metrics:descriptor"
-  generated_header = "${target_gen_dir}/metrics.descriptor.h"
-
-  descriptor_file_path = get_label_info(descriptor_target, "target_gen_dir") +
-                         "/metrics.descriptor"
-
-  script = "../../../tools/gen_cc_proto_descriptor.py"
-  deps = [ descriptor_target ]
-  args = [
-    "--gen_dir",
-    rebase_path(root_gen_dir, root_build_dir),
-    "--cpp_out",
-    rebase_path(generated_header, root_build_dir),
-    rebase_path(descriptor_file_path, root_build_dir),
-  ]
-  inputs = [ descriptor_file_path ]
-  outputs = [ generated_header ]
-  public_configs = [ ":gen_config" ]
 }
 
-action("gen_cc_all_chrome_metrics_descriptor") {
+perfetto_cc_proto_descriptor("gen_cc_all_chrome_metrics_descriptor") {
+  descriptor_name = "all_chrome_metrics.descriptor"
   descriptor_target = "../../../protos/perfetto/metrics/chrome:descriptor"
-  generated_header = "${target_gen_dir}/chrome/all_chrome_metrics.descriptor.h"
-
-  descriptor_file_path = get_label_info(descriptor_target, "target_gen_dir") +
-                         "/all_chrome_metrics.descriptor"
-
-  script = "../../../tools/gen_cc_proto_descriptor.py"
-  deps = [ descriptor_target ]
-  args = [
-    "--gen_dir",
-    rebase_path(root_gen_dir, root_build_dir),
-    "--cpp_out",
-    rebase_path(generated_header, root_build_dir),
-    rebase_path(descriptor_file_path, root_build_dir),
-  ]
-  inputs = [ descriptor_file_path ]
-  outputs = [ generated_header ]
-  public_configs = [ ":gen_config" ]
 }
 
 if (enable_perfetto_trace_processor_sqlite) {
-  source_set("lib") {
+  source_set("metrics") {
     sources = [
       "metrics.cc",
       "metrics.h",
@@ -184,9 +46,9 @@
     public_deps = [
       ":gen_cc_all_chrome_metrics_descriptor",
       ":gen_cc_metrics_descriptor",
-      ":gen_merged_sql_metrics",
       "../util",
       "../util:descriptors",
+      "sql:gen_amalgamated_sql_metrics",
     ]
   }
 
@@ -194,7 +56,7 @@
     testonly = true
     sources = [ "metrics_unittest.cc" ]
     deps = [
-      ":lib",
+      ":metrics",
       "..:lib",
       "../../../gn:default_deps",
       "../../../gn:gtest_and_gmock",
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index 3bfff9b..6926cff 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -24,7 +24,6 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
-#include "src/trace_processor/metrics/sql_metrics.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 #include "src/trace_processor/tp_metatrace.h"
 #include "src/trace_processor/util/status_macros.h"
diff --git a/src/trace_processor/metrics/protos/README.md b/src/trace_processor/metrics/protos/README.md
new file mode 100644
index 0000000..8b6ab1a
--- /dev/null
+++ b/src/trace_processor/metrics/protos/README.md
@@ -0,0 +1,11 @@
+This empty folder exists to harmonize "built-in" metrics
+work with the "metric extension" functionality of trace processor.
+
+Essentially by adding a protos folder, we can pass
+src/trace_processor/metrics as an extension path to shell to
+override all built-in metrics. This means we can change the SQL
+files *without* needing to recompile trace processor.
+
+In the future, we might also move the protos here but that would
+be a bigger change which would need coordination across repos (
+e.g. Chrome because of the metrics autoroller for Chrome metrics).
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
new file mode 100644
index 0000000..2759d24
--- /dev/null
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -0,0 +1,122 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../gn/perfetto.gni")
+
+sql_files = [
+  "trace_metadata.sql",
+  "trace_stats.sql",
+  "android/android_batt.sql",
+  "android/android_camera.sql",
+  "android/android_cpu.sql",
+  "android/android_surfaceflinger.sql",
+  "android/android_cpu_agg.sql",
+  "android/android_cpu_raw_metrics_per_core.sql",
+  "android/android_dma_heap.sql",
+  "android/android_fastrpc.sql",
+  "android/android_gpu.sql",
+  "android/android_hwui_threads.sql",
+  "android/android_mem.sql",
+  "android/android_mem_unagg.sql",
+  "android/android_ion.sql",
+  "android/composer_execution.sql",
+  "android/composition_layers.sql",
+  "android/frame_missed.sql",
+  "android/android_jank.sql",
+  "android/android_lmk_reason.sql",
+  "android/android_lmk.sql",
+  "android/android_powrails.sql",
+  "android/android_proxy_power.sql",
+  "android/android_simpleperf.sql",
+  "android/android_startup.sql",
+  "android/android_package_list.sql",
+  "android/android_task_names.sql",
+  "android/android_thread_time_in_state.sql",
+  "android/android_trace_quality.sql",
+  "android/cpu_info.sql",
+  "android/display_metrics.sql",
+  "android/g2d.sql",
+  "android/g2d_duration.sql",
+  "android/android_hwcomposer.sql",
+  "android/android_hwui_metric.sql",
+  "android/java_heap_histogram.sql",
+  "android/java_heap_stats.sql",
+  "android/power_drain_in_watts.sql",
+  "android/power_profile_data.sql",
+  "android/process_unagg_mem_view.sql",
+  "android/process_mem.sql",
+  "android/process_metadata.sql",
+  "android/process_oom_score.sql",
+  "android/profiler_smaps.sql",
+  "android/mem_stats_priority_breakdown.sql",
+  "android/android_multiuser.sql",
+  "android/android_multiuser_populator.sql",
+  "android/span_view_stats.sql",
+  "android/android_sysui_cuj.sql",
+  "android/android_sysui_cuj_jank_query.sql",
+  "android/process_counter_span_view.sql",
+  "android/global_counter_span_view.sql",
+  "android/gpu_counter_span_view.sql",
+  "android/thread_counter_span_view.sql",
+  "android/unsymbolized_frames.sql",
+  "android/startup/launches.sql",
+  "android/startup/hsc.sql",
+  "chrome/actual_power_by_category.sql",
+  "chrome/actual_power_by_rail_mode.sql",
+  "chrome/chrome_event_metadata.sql",
+  "chrome/chrome_processes.sql",
+  "chrome/chrome_thread_slice_with_cpu_time.sql",
+  "chrome/cpu_time_by_category.sql",
+  "chrome/cpu_time_by_rail_mode.sql",
+  "chrome/estimated_power_by_category.sql",
+  "chrome/estimated_power_by_rail_mode.sql",
+  "chrome/gesture_jank.sql",
+  "chrome/gesture_flow_event.sql",
+  "chrome/gesture_flow_event_queuing_delay.sql",
+  "chrome/rail_modes.sql",
+  "chrome/scroll_jank.sql",
+  "chrome/scroll_jank_cause.sql",
+  "chrome/scroll_jank_cause_blocking_task.sql",
+  "chrome/scroll_jank_cause_blocking_touch_move.sql",
+  "chrome/scroll_jank_cause_get_bitmap.sql",
+  "chrome/scroll_jank_cause_queuing_delay.sql",
+  "chrome/scroll_flow_event.sql",
+  "chrome/scroll_flow_event_queuing_delay.sql",
+  "chrome/test_chrome_metric.sql",
+  "chrome/touch_flow_event.sql",
+  "chrome/touch_flow_event_queuing_delay.sql",
+  "chrome/touch_jank.sql",
+  "experimental/blink_gc_metric.sql",
+  "experimental/chrome_dropped_frames.sql",
+  "experimental/frame_times.sql",
+  "experimental/media_metric.sql",
+  "experimental/reported_by_page.sql",
+  "webview/webview_power_usage.sql",
+]
+
+config("gen_config") {
+  include_dirs = [ "${root_gen_dir}/${perfetto_root_path}" ]
+}
+
+action("gen_amalgamated_sql_metrics") {
+  script = "../../../../tools/gen_amalgamated_sql_metrics.py"
+  generated_header = "${target_gen_dir}/amalgamated_sql_metrics.h"
+  args = rebase_path(sql_files, root_build_dir) + [
+           "--cpp_out",
+           rebase_path(generated_header, root_build_dir),
+         ]
+  inputs = sql_files
+  outputs = [ generated_header ]
+  public_configs = [ ":gen_config" ]
+}
diff --git a/src/trace_processor/metrics/android/OWNERS b/src/trace_processor/metrics/sql/android/OWNERS
similarity index 100%
rename from src/trace_processor/metrics/android/OWNERS
rename to src/trace_processor/metrics/sql/android/OWNERS
diff --git a/src/trace_processor/metrics/android/android_batt.sql b/src/trace_processor/metrics/sql/android/android_batt.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_batt.sql
rename to src/trace_processor/metrics/sql/android/android_batt.sql
diff --git a/src/trace_processor/metrics/android/android_camera.sql b/src/trace_processor/metrics/sql/android/android_camera.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_camera.sql
rename to src/trace_processor/metrics/sql/android/android_camera.sql
diff --git a/src/trace_processor/metrics/android/android_cpu.sql b/src/trace_processor/metrics/sql/android/android_cpu.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_cpu.sql
rename to src/trace_processor/metrics/sql/android/android_cpu.sql
diff --git a/src/trace_processor/metrics/android/android_cpu_agg.sql b/src/trace_processor/metrics/sql/android/android_cpu_agg.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_cpu_agg.sql
rename to src/trace_processor/metrics/sql/android/android_cpu_agg.sql
diff --git a/src/trace_processor/metrics/android/android_cpu_raw_metrics_per_core.sql b/src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_cpu_raw_metrics_per_core.sql
rename to src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql
diff --git a/src/trace_processor/metrics/android/android_dma_heap.sql b/src/trace_processor/metrics/sql/android/android_dma_heap.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_dma_heap.sql
rename to src/trace_processor/metrics/sql/android/android_dma_heap.sql
diff --git a/src/trace_processor/metrics/android/android_fastrpc.sql b/src/trace_processor/metrics/sql/android/android_fastrpc.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_fastrpc.sql
rename to src/trace_processor/metrics/sql/android/android_fastrpc.sql
diff --git a/src/trace_processor/metrics/android/android_gpu.sql b/src/trace_processor/metrics/sql/android/android_gpu.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_gpu.sql
rename to src/trace_processor/metrics/sql/android/android_gpu.sql
diff --git a/src/trace_processor/metrics/android/android_hwcomposer.sql b/src/trace_processor/metrics/sql/android/android_hwcomposer.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_hwcomposer.sql
rename to src/trace_processor/metrics/sql/android/android_hwcomposer.sql
diff --git a/src/trace_processor/metrics/android/android_hwui_metric.sql b/src/trace_processor/metrics/sql/android/android_hwui_metric.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_hwui_metric.sql
rename to src/trace_processor/metrics/sql/android/android_hwui_metric.sql
diff --git a/src/trace_processor/metrics/android/android_hwui_threads.sql b/src/trace_processor/metrics/sql/android/android_hwui_threads.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_hwui_threads.sql
rename to src/trace_processor/metrics/sql/android/android_hwui_threads.sql
diff --git a/src/trace_processor/metrics/android/android_ion.sql b/src/trace_processor/metrics/sql/android/android_ion.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_ion.sql
rename to src/trace_processor/metrics/sql/android/android_ion.sql
diff --git a/src/trace_processor/metrics/android/android_jank.sql b/src/trace_processor/metrics/sql/android/android_jank.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_jank.sql
rename to src/trace_processor/metrics/sql/android/android_jank.sql
diff --git a/src/trace_processor/metrics/android/android_lmk.sql b/src/trace_processor/metrics/sql/android/android_lmk.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_lmk.sql
rename to src/trace_processor/metrics/sql/android/android_lmk.sql
diff --git a/src/trace_processor/metrics/android/android_lmk_reason.sql b/src/trace_processor/metrics/sql/android/android_lmk_reason.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_lmk_reason.sql
rename to src/trace_processor/metrics/sql/android/android_lmk_reason.sql
diff --git a/src/trace_processor/metrics/android/android_mem.sql b/src/trace_processor/metrics/sql/android/android_mem.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_mem.sql
rename to src/trace_processor/metrics/sql/android/android_mem.sql
diff --git a/src/trace_processor/metrics/android/android_mem_unagg.sql b/src/trace_processor/metrics/sql/android/android_mem_unagg.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_mem_unagg.sql
rename to src/trace_processor/metrics/sql/android/android_mem_unagg.sql
diff --git a/src/trace_processor/metrics/android/android_multiuser.sql b/src/trace_processor/metrics/sql/android/android_multiuser.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_multiuser.sql
rename to src/trace_processor/metrics/sql/android/android_multiuser.sql
diff --git a/src/trace_processor/metrics/android/android_multiuser_populator.sql b/src/trace_processor/metrics/sql/android/android_multiuser_populator.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_multiuser_populator.sql
rename to src/trace_processor/metrics/sql/android/android_multiuser_populator.sql
diff --git a/src/trace_processor/metrics/android/android_package_list.sql b/src/trace_processor/metrics/sql/android/android_package_list.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_package_list.sql
rename to src/trace_processor/metrics/sql/android/android_package_list.sql
diff --git a/src/trace_processor/metrics/android/android_powrails.sql b/src/trace_processor/metrics/sql/android/android_powrails.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_powrails.sql
rename to src/trace_processor/metrics/sql/android/android_powrails.sql
diff --git a/src/trace_processor/metrics/android/android_proxy_power.sql b/src/trace_processor/metrics/sql/android/android_proxy_power.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_proxy_power.sql
rename to src/trace_processor/metrics/sql/android/android_proxy_power.sql
diff --git a/src/trace_processor/metrics/android/android_simpleperf.sql b/src/trace_processor/metrics/sql/android/android_simpleperf.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_simpleperf.sql
rename to src/trace_processor/metrics/sql/android/android_simpleperf.sql
diff --git a/src/trace_processor/metrics/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
similarity index 89%
rename from src/trace_processor/metrics/android/android_startup.sql
rename to src/trace_processor/metrics/sql/android/android_startup.sql
index f561d52..20eaefe 100644
--- a/src/trace_processor/metrics/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -301,6 +301,15 @@
 FROM long_binder_transactions s
 LEFT JOIN binder_to_destination_process bdp USING(slice_id);
 
+SELECT CREATE_FUNCTION(
+  'MAIN_PROCESS_SLICE_PROTO(launch_id LONG, name STRING)',
+  'PROTO', '
+    SELECT slice_proto
+    FROM main_process_slice s
+    WHERE s.launch_id = $launch_id AND name LIKE $name
+    LIMIT 1
+  ');
+
 DROP VIEW IF EXISTS startup_view;
 CREATE VIEW startup_view AS
 SELECT
@@ -430,41 +439,18 @@
         FROM launching_events l
         WHERE l.ts BETWEEN launches.ts AND launches.ts + launches.dur
       ),
-      'time_post_fork', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'PostFork'
-      ),
-      'time_activity_thread_main', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'ActivityThreadMain'
-      ),
-      'time_bind_application', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'bindApplication'
-      ),
-      'time_activity_start', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'activityStart'
-      ),
-      'time_activity_resume', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'activityResume'
-      ),
-      'time_activity_restart', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'activityRestart'
-      ),
-      'time_choreographer', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name LIKE 'Choreographer#doFrame%'
-      ),
+      'time_post_fork', MAIN_PROCESS_SLICE_PROTO(launches.id, 'PostFork'),
+      'time_activity_thread_main', MAIN_PROCESS_SLICE_PROTO(launches.id, 'ActivityThreadMain'),
+      'time_bind_application', MAIN_PROCESS_SLICE_PROTO(launches.id, 'bindApplication'),
+      'time_activity_start', MAIN_PROCESS_SLICE_PROTO(launches.id, 'activityStart'),
+      'time_activity_resume', MAIN_PROCESS_SLICE_PROTO(launches.id, 'activityResume'),
+      'time_activity_restart', MAIN_PROCESS_SLICE_PROTO(launches.id, 'activityRestart'),
+      'time_choreographer', MAIN_PROCESS_SLICE_PROTO(launches.id, 'Choreographer#doFrame%'),
+      'time_inflate', MAIN_PROCESS_SLICE_PROTO(launches.id, 'inflate'),
+      'time_get_resources', MAIN_PROCESS_SLICE_PROTO(launches.id, 'ResourcesManager#getResources'),
+      'time_dex_open', MAIN_PROCESS_SLICE_PROTO(launches.id, 'OpenDexFilesFromOat'),
+      'time_verify_class', MAIN_PROCESS_SLICE_PROTO(launches.id, 'VerifyClass'),
+      'time_gc_total', MAIN_PROCESS_SLICE_PROTO(launches.id, 'GC'),
       'time_before_start_process', (
         SELECT AndroidStartupMetric_Slice(
           'dur_ns', ts - launches.ts,
@@ -481,27 +467,6 @@
         FROM zygote_forks_by_id z
         WHERE z.id = launches.id
       ),
-      'time_inflate', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'inflate'
-      ),
-      'time_get_resources', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id
-        AND name = 'ResourcesManager#getResources'
-      ),
-      'time_dex_open', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'OpenDexFilesFromOat'
-      ),
-      'time_verify_class', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'VerifyClass'
-      ),
       'jit_compiled_methods', (
         SELECT count
         FROM jit_compiled_methods_materialized s
@@ -516,11 +481,6 @@
         FROM launch_threads_cpu_materialized
         WHERE launch_id = launches.id
       ),
-      'time_gc_total', (
-        SELECT slice_proto
-        FROM main_process_slice s
-        WHERE s.launch_id = launches.id AND name = 'GC'
-      ),
       'time_gc_on_cpu', (
         SELECT
           NULL_IF_EMPTY(AndroidStartupMetric_Slice(
diff --git a/src/trace_processor/metrics/android/android_surfaceflinger.sql b/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_surfaceflinger.sql
rename to src/trace_processor/metrics/sql/android/android_surfaceflinger.sql
diff --git a/src/trace_processor/metrics/android/android_sysui_cuj.sql b/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_sysui_cuj.sql
rename to src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
diff --git a/src/trace_processor/metrics/android/android_sysui_cuj_jank_query.sql b/src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_sysui_cuj_jank_query.sql
rename to src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql
diff --git a/src/trace_processor/metrics/android/android_task_names.sql b/src/trace_processor/metrics/sql/android/android_task_names.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_task_names.sql
rename to src/trace_processor/metrics/sql/android/android_task_names.sql
diff --git a/src/trace_processor/metrics/android/android_thread_time_in_state.sql b/src/trace_processor/metrics/sql/android/android_thread_time_in_state.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_thread_time_in_state.sql
rename to src/trace_processor/metrics/sql/android/android_thread_time_in_state.sql
diff --git a/src/trace_processor/metrics/android/android_trace_quality.sql b/src/trace_processor/metrics/sql/android/android_trace_quality.sql
similarity index 100%
rename from src/trace_processor/metrics/android/android_trace_quality.sql
rename to src/trace_processor/metrics/sql/android/android_trace_quality.sql
diff --git a/src/trace_processor/metrics/android/composer_execution.sql b/src/trace_processor/metrics/sql/android/composer_execution.sql
similarity index 100%
rename from src/trace_processor/metrics/android/composer_execution.sql
rename to src/trace_processor/metrics/sql/android/composer_execution.sql
diff --git a/src/trace_processor/metrics/android/composition_layers.sql b/src/trace_processor/metrics/sql/android/composition_layers.sql
similarity index 100%
rename from src/trace_processor/metrics/android/composition_layers.sql
rename to src/trace_processor/metrics/sql/android/composition_layers.sql
diff --git a/src/trace_processor/metrics/android/cpu_info.sql b/src/trace_processor/metrics/sql/android/cpu_info.sql
similarity index 100%
rename from src/trace_processor/metrics/android/cpu_info.sql
rename to src/trace_processor/metrics/sql/android/cpu_info.sql
diff --git a/src/trace_processor/metrics/android/display_metrics.sql b/src/trace_processor/metrics/sql/android/display_metrics.sql
similarity index 100%
rename from src/trace_processor/metrics/android/display_metrics.sql
rename to src/trace_processor/metrics/sql/android/display_metrics.sql
diff --git a/src/trace_processor/metrics/android/frame_missed.sql b/src/trace_processor/metrics/sql/android/frame_missed.sql
similarity index 100%
rename from src/trace_processor/metrics/android/frame_missed.sql
rename to src/trace_processor/metrics/sql/android/frame_missed.sql
diff --git a/src/trace_processor/metrics/android/g2d.sql b/src/trace_processor/metrics/sql/android/g2d.sql
similarity index 100%
rename from src/trace_processor/metrics/android/g2d.sql
rename to src/trace_processor/metrics/sql/android/g2d.sql
diff --git a/src/trace_processor/metrics/android/g2d_duration.sql b/src/trace_processor/metrics/sql/android/g2d_duration.sql
similarity index 100%
rename from src/trace_processor/metrics/android/g2d_duration.sql
rename to src/trace_processor/metrics/sql/android/g2d_duration.sql
diff --git a/src/trace_processor/metrics/android/global_counter_span_view.sql b/src/trace_processor/metrics/sql/android/global_counter_span_view.sql
similarity index 100%
rename from src/trace_processor/metrics/android/global_counter_span_view.sql
rename to src/trace_processor/metrics/sql/android/global_counter_span_view.sql
diff --git a/src/trace_processor/metrics/android/gpu_counter_span_view.sql b/src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql
similarity index 100%
rename from src/trace_processor/metrics/android/gpu_counter_span_view.sql
rename to src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql
diff --git a/src/trace_processor/metrics/android/java_heap_histogram.sql b/src/trace_processor/metrics/sql/android/java_heap_histogram.sql
similarity index 100%
rename from src/trace_processor/metrics/android/java_heap_histogram.sql
rename to src/trace_processor/metrics/sql/android/java_heap_histogram.sql
diff --git a/src/trace_processor/metrics/android/java_heap_stats.sql b/src/trace_processor/metrics/sql/android/java_heap_stats.sql
similarity index 100%
rename from src/trace_processor/metrics/android/java_heap_stats.sql
rename to src/trace_processor/metrics/sql/android/java_heap_stats.sql
diff --git a/src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql b/src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql
similarity index 100%
rename from src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql
rename to src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql
diff --git a/src/trace_processor/metrics/android/power_drain_in_watts.sql b/src/trace_processor/metrics/sql/android/power_drain_in_watts.sql
similarity index 100%
rename from src/trace_processor/metrics/android/power_drain_in_watts.sql
rename to src/trace_processor/metrics/sql/android/power_drain_in_watts.sql
diff --git a/src/trace_processor/metrics/android/power_profile_data.sql b/src/trace_processor/metrics/sql/android/power_profile_data.sql
similarity index 100%
rename from src/trace_processor/metrics/android/power_profile_data.sql
rename to src/trace_processor/metrics/sql/android/power_profile_data.sql
diff --git a/src/trace_processor/metrics/android/process_counter_span_view.sql b/src/trace_processor/metrics/sql/android/process_counter_span_view.sql
similarity index 100%
rename from src/trace_processor/metrics/android/process_counter_span_view.sql
rename to src/trace_processor/metrics/sql/android/process_counter_span_view.sql
diff --git a/src/trace_processor/metrics/android/process_mem.sql b/src/trace_processor/metrics/sql/android/process_mem.sql
similarity index 100%
rename from src/trace_processor/metrics/android/process_mem.sql
rename to src/trace_processor/metrics/sql/android/process_mem.sql
diff --git a/src/trace_processor/metrics/android/process_metadata.sql b/src/trace_processor/metrics/sql/android/process_metadata.sql
similarity index 100%
rename from src/trace_processor/metrics/android/process_metadata.sql
rename to src/trace_processor/metrics/sql/android/process_metadata.sql
diff --git a/src/trace_processor/metrics/android/process_oom_score.sql b/src/trace_processor/metrics/sql/android/process_oom_score.sql
similarity index 100%
rename from src/trace_processor/metrics/android/process_oom_score.sql
rename to src/trace_processor/metrics/sql/android/process_oom_score.sql
diff --git a/src/trace_processor/metrics/android/process_unagg_mem_view.sql b/src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql
similarity index 100%
rename from src/trace_processor/metrics/android/process_unagg_mem_view.sql
rename to src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql
diff --git a/src/trace_processor/metrics/android/profiler_smaps.sql b/src/trace_processor/metrics/sql/android/profiler_smaps.sql
similarity index 100%
rename from src/trace_processor/metrics/android/profiler_smaps.sql
rename to src/trace_processor/metrics/sql/android/profiler_smaps.sql
diff --git a/src/trace_processor/metrics/android/span_view_stats.sql b/src/trace_processor/metrics/sql/android/span_view_stats.sql
similarity index 100%
rename from src/trace_processor/metrics/android/span_view_stats.sql
rename to src/trace_processor/metrics/sql/android/span_view_stats.sql
diff --git a/src/trace_processor/metrics/android/startup/hsc.sql b/src/trace_processor/metrics/sql/android/startup/hsc.sql
similarity index 100%
rename from src/trace_processor/metrics/android/startup/hsc.sql
rename to src/trace_processor/metrics/sql/android/startup/hsc.sql
diff --git a/src/trace_processor/metrics/android/startup/launches.sql b/src/trace_processor/metrics/sql/android/startup/launches.sql
similarity index 100%
rename from src/trace_processor/metrics/android/startup/launches.sql
rename to src/trace_processor/metrics/sql/android/startup/launches.sql
diff --git a/src/trace_processor/metrics/android/thread_counter_span_view.sql b/src/trace_processor/metrics/sql/android/thread_counter_span_view.sql
similarity index 100%
rename from src/trace_processor/metrics/android/thread_counter_span_view.sql
rename to src/trace_processor/metrics/sql/android/thread_counter_span_view.sql
diff --git a/src/trace_processor/metrics/android/unsymbolized_frames.sql b/src/trace_processor/metrics/sql/android/unsymbolized_frames.sql
similarity index 100%
rename from src/trace_processor/metrics/android/unsymbolized_frames.sql
rename to src/trace_processor/metrics/sql/android/unsymbolized_frames.sql
diff --git a/src/trace_processor/metrics/chrome/OWNERS b/src/trace_processor/metrics/sql/chrome/OWNERS
similarity index 100%
rename from src/trace_processor/metrics/chrome/OWNERS
rename to src/trace_processor/metrics/sql/chrome/OWNERS
diff --git a/src/trace_processor/metrics/chrome/actual_power_by_category.sql b/src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/actual_power_by_category.sql
rename to src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql
diff --git a/src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql b/src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/actual_power_by_rail_mode.sql
rename to src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql
diff --git a/src/trace_processor/metrics/chrome/chrome_event_metadata.sql b/src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/chrome_event_metadata.sql
rename to src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql
diff --git a/src/trace_processor/metrics/chrome/chrome_processes.sql b/src/trace_processor/metrics/sql/chrome/chrome_processes.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/chrome_processes.sql
rename to src/trace_processor/metrics/sql/chrome/chrome_processes.sql
diff --git a/src/trace_processor/metrics/chrome/chrome_thread_slice_with_cpu_time.sql b/src/trace_processor/metrics/sql/chrome/chrome_thread_slice_with_cpu_time.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/chrome_thread_slice_with_cpu_time.sql
rename to src/trace_processor/metrics/sql/chrome/chrome_thread_slice_with_cpu_time.sql
diff --git a/src/trace_processor/metrics/chrome/cpu_time_by_category.sql b/src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/cpu_time_by_category.sql
rename to src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql
diff --git a/src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql b/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/cpu_time_by_rail_mode.sql
rename to src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
diff --git a/src/trace_processor/metrics/chrome/estimated_power_by_category.sql b/src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/estimated_power_by_category.sql
rename to src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql
diff --git a/src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql b/src/trace_processor/metrics/sql/chrome/estimated_power_by_rail_mode.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/estimated_power_by_rail_mode.sql
rename to src/trace_processor/metrics/sql/chrome/estimated_power_by_rail_mode.sql
diff --git a/src/trace_processor/metrics/chrome/gesture_flow_event.sql b/src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/gesture_flow_event.sql
rename to src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql
diff --git a/src/trace_processor/metrics/chrome/gesture_flow_event_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/gesture_flow_event_queuing_delay.sql
rename to src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql
diff --git a/src/trace_processor/metrics/chrome/gesture_jank.sql b/src/trace_processor/metrics/sql/chrome/gesture_jank.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/gesture_jank.sql
rename to src/trace_processor/metrics/sql/chrome/gesture_jank.sql
diff --git a/src/trace_processor/metrics/chrome/rail_modes.sql b/src/trace_processor/metrics/sql/chrome/rail_modes.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/rail_modes.sql
rename to src/trace_processor/metrics/sql/chrome/rail_modes.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_flow_event.sql b/src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_flow_event.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_flow_event_queuing_delay.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_jank.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_jank.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_jank.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_jank_cause.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_jank_cause.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_task.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_task.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_touch_move.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_jank_cause_blocking_touch_move.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_jank_cause_get_bitmap.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_jank_cause_get_bitmap.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql
diff --git a/src/trace_processor/metrics/chrome/scroll_jank_cause_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/scroll_jank_cause_queuing_delay.sql
rename to src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
diff --git a/src/trace_processor/metrics/chrome/test_chrome_metric.sql b/src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/test_chrome_metric.sql
rename to src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql
diff --git a/src/trace_processor/metrics/chrome/touch_flow_event.sql b/src/trace_processor/metrics/sql/chrome/touch_flow_event.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/touch_flow_event.sql
rename to src/trace_processor/metrics/sql/chrome/touch_flow_event.sql
diff --git a/src/trace_processor/metrics/chrome/touch_flow_event_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/touch_flow_event_queuing_delay.sql
rename to src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql
diff --git a/src/trace_processor/metrics/chrome/touch_jank.sql b/src/trace_processor/metrics/sql/chrome/touch_jank.sql
similarity index 100%
rename from src/trace_processor/metrics/chrome/touch_jank.sql
rename to src/trace_processor/metrics/sql/chrome/touch_jank.sql
diff --git a/src/trace_processor/metrics/experimental/OWNERS b/src/trace_processor/metrics/sql/experimental/OWNERS
similarity index 100%
rename from src/trace_processor/metrics/experimental/OWNERS
rename to src/trace_processor/metrics/sql/experimental/OWNERS
diff --git a/src/trace_processor/metrics/experimental/blink_gc_metric.sql b/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql
similarity index 100%
rename from src/trace_processor/metrics/experimental/blink_gc_metric.sql
rename to src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql
diff --git a/src/trace_processor/metrics/experimental/chrome_dropped_frames.sql b/src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql
similarity index 100%
rename from src/trace_processor/metrics/experimental/chrome_dropped_frames.sql
rename to src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql
diff --git a/src/trace_processor/metrics/experimental/frame_times.sql b/src/trace_processor/metrics/sql/experimental/frame_times.sql
similarity index 100%
rename from src/trace_processor/metrics/experimental/frame_times.sql
rename to src/trace_processor/metrics/sql/experimental/frame_times.sql
diff --git a/src/trace_processor/metrics/experimental/media_metric.sql b/src/trace_processor/metrics/sql/experimental/media_metric.sql
similarity index 100%
rename from src/trace_processor/metrics/experimental/media_metric.sql
rename to src/trace_processor/metrics/sql/experimental/media_metric.sql
diff --git a/src/trace_processor/metrics/experimental/reported_by_page.sql b/src/trace_processor/metrics/sql/experimental/reported_by_page.sql
similarity index 100%
rename from src/trace_processor/metrics/experimental/reported_by_page.sql
rename to src/trace_processor/metrics/sql/experimental/reported_by_page.sql
diff --git a/src/trace_processor/metrics/trace_metadata.sql b/src/trace_processor/metrics/sql/trace_metadata.sql
similarity index 100%
rename from src/trace_processor/metrics/trace_metadata.sql
rename to src/trace_processor/metrics/sql/trace_metadata.sql
diff --git a/src/trace_processor/metrics/trace_stats.sql b/src/trace_processor/metrics/sql/trace_stats.sql
similarity index 100%
rename from src/trace_processor/metrics/trace_stats.sql
rename to src/trace_processor/metrics/sql/trace_stats.sql
diff --git a/src/trace_processor/metrics/webview/webview_power_usage.sql b/src/trace_processor/metrics/sql/webview/webview_power_usage.sql
similarity index 100%
rename from src/trace_processor/metrics/webview/webview_power_usage.sql
rename to src/trace_processor/metrics/sql/webview/webview_power_usage.sql
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index af1adbe..7863cc5 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -17,6 +17,8 @@
 if (enable_perfetto_trace_processor_sqlite) {
   source_set("sqlite") {
     sources = [
+      "create_function.cc",
+      "create_function.h",
       "db_sqlite_table.cc",
       "db_sqlite_table.h",
       "query_cache.h",
diff --git a/src/trace_processor/sqlite/create_function.cc b/src/trace_processor/sqlite/create_function.cc
new file mode 100644
index 0000000..692018e
--- /dev/null
+++ b/src/trace_processor/sqlite/create_function.cc
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/sqlite/create_function.h"
+
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+bool IsValidName(base::StringView str) {
+  auto pred = [](char c) { return !(isalnum(c) || c == '_'); };
+  return std::find_if(str.begin(), str.end(), pred) == str.end();
+}
+
+base::Optional<SqlValue::Type> ParseType(base::StringView str) {
+  if (str == "INT" || str == "LONG" || str == "BOOL") {
+    return SqlValue::Type::kLong;
+  } else if (str == "DOUBLE" || str == "FLOAT") {
+    return SqlValue::Type::kDouble;
+  } else if (str == "STRING") {
+    return SqlValue::Type::kString;
+  } else if (str == "PROTO" || str == "BYTES") {
+    return SqlValue::Type::kBytes;
+  }
+  return base::nullopt;
+}
+
+const char* SqliteTypeToFriendlyString(SqlValue::Type type) {
+  switch (type) {
+    case SqlValue::Type::kNull:
+      return "NULL";
+    case SqlValue::Type::kLong:
+      return "INT/LONG/BOOL";
+    case SqlValue::Type::kDouble:
+      return "FLOAT/DOUBLE";
+    case SqlValue::Type::kString:
+      return "STRING";
+    case SqlValue::Type::kBytes:
+      return "BYTES/PROTO";
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+base::Status TypeCheckSqliteValue(sqlite3_value* value,
+                                  SqlValue::Type expected_type) {
+  SqlValue::Type actual_type =
+      sqlite_utils::SqliteTypeToSqlValueType(sqlite3_value_type(value));
+  if (actual_type != SqlValue::Type::kNull && actual_type != expected_type) {
+    return base::ErrStatus(
+        "does not have expected type: expected %s, actual %s",
+        SqliteTypeToFriendlyString(expected_type),
+        SqliteTypeToFriendlyString(actual_type));
+  }
+  return base::OkStatus();
+}
+
+struct Prototype {
+  struct Argument {
+    std::string dollar_name;
+    SqlValue::Type type;
+
+    bool operator==(const Argument& other) const {
+      return dollar_name == other.dollar_name && type == other.type;
+    }
+  };
+  std::string function_name;
+  std::vector<Argument> arguments;
+
+  bool operator==(const Prototype& other) const {
+    return function_name == other.function_name && arguments == other.arguments;
+  }
+  bool operator!=(const Prototype& other) const { return !(*this == other); }
+};
+
+base::Status ParsePrototype(base::StringView raw, Prototype& out) {
+  // Examples of function prototypes:
+  // ANDROID_SDK_LEVEL()
+  // STARTUP_SLICE(dur_ns INT)
+  // FIND_NEXT_SLICE_WITH_NAME(ts INT, name STRING)
+
+  size_t function_name_end = raw.find('(');
+  if (function_name_end == base::StringView::npos) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION[prototype=%s]: unable to find bracket starting "
+        "argument list",
+        raw.ToStdString().c_str());
+  }
+
+  base::StringView function_name = raw.substr(0, function_name_end);
+  if (!IsValidName(function_name)) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION[prototype=%s]: function name %s is not alphanumeric",
+        raw.ToStdString().c_str(), function_name.ToStdString().c_str());
+  }
+
+  size_t args_start = function_name_end + 1;
+  size_t args_end = raw.find(')', function_name_end);
+  if (args_end == base::StringView::npos) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION[prototype=%s]: unable to find bracket ending "
+        "argument list",
+        raw.ToStdString().c_str());
+  }
+
+  base::StringView args_str = raw.substr(args_start, args_end - args_start);
+  for (const auto& arg : base::SplitString(args_str.ToStdString(), ",")) {
+    const auto& arg_name_and_type = base::SplitString(arg, " ");
+    if (arg_name_and_type.size() != 2) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s, arg=%s]: argument in function "
+          "prototye should be of the form `name type`",
+          raw.ToStdString().c_str(), arg.c_str());
+    }
+
+    const auto& arg_name = arg_name_and_type[0];
+    const auto& arg_type_str = arg_name_and_type[1];
+    if (!IsValidName(base::StringView(arg_name))) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s, arg=%s]: argument is not alphanumeric",
+          raw.ToStdString().c_str(), arg.c_str());
+    }
+
+    auto opt_arg_type = ParseType(base::StringView(arg_type_str));
+    if (!opt_arg_type) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s, arg=%s]: unknown arg type",
+          raw.ToStdString().c_str(), arg.c_str());
+    }
+
+    SqlValue::Type arg_type = *opt_arg_type;
+    PERFETTO_DCHECK(arg_type != SqlValue::Type::kNull);
+    out.arguments.push_back({"$" + arg_name, arg_type});
+  }
+
+  out.function_name = function_name.ToStdString();
+  return base::OkStatus();
+}
+
+struct CreatedFunction : public SqlFunction {
+  struct Context {
+    sqlite3* db;
+    Prototype prototype;
+    SqlValue::Type return_type;
+    std::string sql;
+    sqlite3_stmt* stmt;
+  };
+
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+  static base::Status Cleanup(Context*);
+};
+
+base::Status SqliteRetToStatus(CreatedFunction::Context* ctx, int ret) {
+  if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
+    return base::ErrStatus("%s: SQLite error while executing function body: %s",
+                           ctx->prototype.function_name.c_str(),
+                           sqlite3_errmsg(ctx->db));
+  }
+  return base::OkStatus();
+}
+
+base::Status CreatedFunction::Run(CreatedFunction::Context* ctx,
+                                  size_t argc,
+                                  sqlite3_value** argv,
+                                  SqlValue& out,
+                                  Destructors&) {
+  if (argc != ctx->prototype.arguments.size()) {
+    return base::ErrStatus(
+        "%s: invalid number of args; expected %zu, received %zu",
+        ctx->prototype.function_name.c_str(), ctx->prototype.arguments.size(),
+        argc);
+  }
+
+  // Type check all the arguments.
+  for (size_t i = 0; i < argc; ++i) {
+    sqlite3_value* arg = argv[i];
+    base::Status status =
+        TypeCheckSqliteValue(arg, ctx->prototype.arguments[i].type);
+    if (!status.ok()) {
+      return base::ErrStatus("%s[arg=%s]: argument %zu %s",
+                             ctx->prototype.function_name.c_str(),
+                             sqlite3_value_text(arg), i, status.c_message());
+    }
+  }
+
+  // Bind all the arguments to the appropriate places in the function.
+  for (size_t i = 0; i < argc; ++i) {
+    const auto& arg = ctx->prototype.arguments[i];
+    int index =
+        sqlite3_bind_parameter_index(ctx->stmt, arg.dollar_name.c_str());
+
+    // If the argument is not in the query, this just means its an unused
+    // argument which we can just ignore.
+    if (index == 0)
+      continue;
+
+    int ret = sqlite3_bind_value(ctx->stmt, index, argv[i]);
+    if (ret != SQLITE_OK) {
+      return base::ErrStatus(
+          "%s: SQLite error while binding value to argument %zu: %s",
+          ctx->prototype.function_name.c_str(), i, sqlite3_errmsg(ctx->db));
+    }
+  }
+
+  int ret = sqlite3_step(ctx->stmt);
+  RETURN_IF_ERROR(SqliteRetToStatus(ctx, ret));
+  if (ret == SQLITE_DONE)
+    // No return value means we just return don't set |out|.
+    return base::OkStatus();
+
+  PERFETTO_DCHECK(ret == SQLITE_ROW);
+  size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt));
+  if (col_count != 1) {
+    return base::ErrStatus(
+        "%s: SQL definition should only return one column: returned %zu "
+        "columns",
+        ctx->prototype.function_name.c_str(), col_count);
+  }
+
+  out = sqlite_utils::SqliteValueToSqlValue(sqlite3_column_value(ctx->stmt, 0));
+  return base::OkStatus();
+}
+
+base::Status CreatedFunction::Cleanup(CreatedFunction::Context* ctx) {
+  int ret = sqlite3_step(ctx->stmt);
+  RETURN_IF_ERROR(SqliteRetToStatus(ctx, ret));
+  if (ret == SQLITE_ROW) {
+    return base::ErrStatus(
+        "%s: multiple values were returned when executing function body",
+        ctx->prototype.function_name.c_str());
+  }
+  PERFETTO_DCHECK(ret == SQLITE_DONE);
+
+  // Make sure to reset the statement to remove any bindings.
+  ret = sqlite3_reset(ctx->stmt);
+  if (ret != SQLITE_OK) {
+    return base::ErrStatus("%s: error while resetting metric",
+                           ctx->prototype.function_name.c_str());
+  }
+  return base::OkStatus();
+}
+
+}  // namespace
+
+size_t CreateFunction::NameAndArgc::Hasher::operator()(
+    const NameAndArgc& s) const noexcept {
+  base::Hash hash;
+  hash.Update(s.name.data(), s.name.size());
+  hash.Update(s.argc);
+  return static_cast<size_t>(hash.digest());
+}
+
+base::Status CreateFunction::Run(CreateFunction::Context* ctx,
+                                 size_t argc,
+                                 sqlite3_value** argv,
+                                 SqlValue&,
+                                 Destructors&) {
+  if (argc != 3) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION: invalid number of args; expected %u, received %zu",
+        3u, argc);
+  }
+
+  sqlite3_value* prototype_value = argv[0];
+  sqlite3_value* return_type_value = argv[1];
+  sqlite3_value* sql_defn_value = argv[2];
+
+  // Type check all the arguments.
+  {
+    auto type_check = [prototype_value](sqlite3_value* value,
+                                        SqlValue::Type type, const char* desc) {
+      base::Status status = TypeCheckSqliteValue(value, type);
+      if (!status.ok()) {
+        return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s %s",
+                               sqlite3_value_text(prototype_value), desc,
+                               status.c_message());
+      }
+      return base::OkStatus();
+    };
+
+    RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
+                               "function name (first argument)"));
+    RETURN_IF_ERROR(type_check(return_type_value, SqlValue::Type::kString,
+                               "return type (second argument)"));
+    RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
+                               "SQL definition (third argument)"));
+  }
+
+  // Extract the arguments from the value wrappers.
+  auto extract_string = [](sqlite3_value* value) -> base::StringView {
+    return reinterpret_cast<const char*>(sqlite3_value_text(value));
+  };
+  base::StringView prototype_str = extract_string(prototype_value);
+  base::StringView return_type_str = extract_string(return_type_value);
+  std::string sql_defn_str = extract_string(sql_defn_value).ToStdString();
+
+  // Parse all the arguments into a more friendly form.
+  Prototype prototype;
+  RETURN_IF_ERROR(ParsePrototype(prototype_str, prototype));
+
+  // Parse the return type into a enum format.
+  auto opt_return_type = ParseType(return_type_str);
+  if (!opt_return_type) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION[prototype=%s, return=%s]: unknown return type "
+        "specified",
+        prototype_str.ToStdString().c_str(),
+        return_type_str.ToStdString().c_str());
+  }
+  SqlValue::Type return_type = *opt_return_type;
+
+  int created_argc = static_cast<int>(prototype.arguments.size());
+  NameAndArgc key{prototype.function_name, created_argc};
+  auto it = ctx->state->find(key);
+  if (it != ctx->state->end()) {
+    // If the function already exists, just verify that the prototype, return
+    // type and SQL matches exactly with what we already had registered. By
+    // doing this, we can avoid the problem plaguing C++ macros where macro
+    // ordering determines which one gets run.
+    auto* created_ctx = static_cast<CreatedFunction::Context*>(
+        it->second.created_functon_context);
+
+    if (created_ctx->prototype != prototype) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s]: function prototype changed",
+          prototype_str.ToStdString().c_str());
+    }
+
+    if (created_ctx->return_type != return_type) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s]: return type changed from %s to %s",
+          prototype_str.ToStdString().c_str(),
+          SqliteTypeToFriendlyString(created_ctx->return_type),
+          return_type_str.ToStdString().c_str());
+    }
+
+    if (created_ctx->sql != sql_defn_str) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s]: function SQL changed from %s to %s",
+          prototype_str.ToStdString().c_str(), created_ctx->sql.c_str(),
+          sql_defn_str.c_str());
+    }
+    return base::OkStatus();
+  }
+
+  // Prepare the SQL definition as a statement using SQLite.
+  ScopedStmt stmt;
+  sqlite3_stmt* stmt_raw = nullptr;
+  int ret = sqlite3_prepare_v2(ctx->db, sql_defn_str.data(),
+                               static_cast<int>(sql_defn_str.size()), &stmt_raw,
+                               nullptr);
+  if (ret != SQLITE_OK) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION[prototype=%s]: SQLite error when preparing "
+        "statement "
+        "%s",
+        prototype_str.ToStdString().c_str(), sqlite3_errmsg(ctx->db));
+  }
+  stmt.reset(stmt_raw);
+
+  std::unique_ptr<CreatedFunction::Context> created(
+      new CreatedFunction::Context{ctx->db, std::move(prototype), return_type,
+                                   std::move(sql_defn_str), stmt.get()});
+  CreatedFunction::Context* created_ptr = created.get();
+  RETURN_IF_ERROR(RegisterSqlFunction<CreatedFunction>(
+      ctx->db, key.name.c_str(), created_argc, std::move(created)));
+  ctx->state->emplace(key, PerFunctionState{std::move(stmt), created_ptr});
+
+  // CREATE_FUNCTION doesn't have a return value so just don't sent |out|.
+  return base::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/create_function.h b/src/trace_processor/sqlite/create_function.h
new file mode 100644
index 0000000..c6778c2
--- /dev/null
+++ b/src/trace_processor/sqlite/create_function.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_CREATE_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_CREATE_FUNCTION_H_
+
+#include <sqlite3.h>
+#include <unordered_map>
+
+#include "src/trace_processor/sqlite/register_function.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Implementation of CREATE_FUNCTION SQL function.
+// See https://perfetto.dev/docs/analysis/metrics#metric-helper-functions for
+// usage of this function.
+struct CreateFunction : public SqlFunction {
+  struct PerFunctionState {
+    ScopedStmt stmt;
+    // void* to avoid leaking state.
+    void* created_functon_context;
+  };
+  struct NameAndArgc {
+    std::string name;
+    int argc;
+
+    struct Hasher {
+      std::size_t operator()(const NameAndArgc& s) const noexcept;
+    };
+    bool operator==(const NameAndArgc& other) const {
+      return name == other.name && argc == other.argc;
+    }
+  };
+  using State = std::unordered_map<NameAndArgc,
+                                   CreateFunction::PerFunctionState,
+                                   NameAndArgc::Hasher>;
+
+  struct Context {
+    sqlite3* db;
+    State* state;
+  };
+
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_CREATE_FUNCTION_H_
diff --git a/src/trace_processor/sqlite/register_function.h b/src/trace_processor/sqlite/register_function.h
index 0df7dcc..1761af4 100644
--- a/src/trace_processor/sqlite/register_function.h
+++ b/src/trace_processor/sqlite/register_function.h
@@ -18,13 +18,8 @@
 #define SRC_TRACE_PROCESSOR_SQLITE_REGISTER_FUNCTION_H_
 
 #include <sqlite3.h>
-#include <cstddef>
 #include <memory>
-#include <set>
 
-#include "perfetto/base/status.h"
-#include "perfetto/trace_processor/basic_types.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 
 namespace perfetto {
diff --git a/src/trace_processor/sqlite/sqlite_utils.h b/src/trace_processor/sqlite/sqlite_utils.h
index 66a3ed6..9bb26d7 100644
--- a/src/trace_processor/sqlite/sqlite_utils.h
+++ b/src/trace_processor/sqlite/sqlite_utils.h
@@ -20,12 +20,6 @@
 #include <math.h>
 #include <sqlite3.h>
 
-#include <functional>
-#include <limits>
-#include <string>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/sqlite/scoped_db.h"
diff --git a/src/trace_processor/tp_metatrace.h b/src/trace_processor/tp_metatrace.h
index 7245ffc..7ca1865 100644
--- a/src/trace_processor/tp_metatrace.h
+++ b/src/trace_processor/tp_metatrace.h
@@ -48,7 +48,7 @@
   uint64_t timestamp_ns;
 
   // Duration of the event.
-  uint32_t duration_ns;
+  uint64_t duration_ns;
 
   // The name of the event.
   // This is assumed to be a static/long lived string.
@@ -145,7 +145,7 @@
     if (RingBuffer::GetInstance()->HasOverwritten(record_idx_))
       return;
     auto now = TraceTimeNowNs();
-    record_->duration_ns = static_cast<uint32_t>(now - record_->timestamp_ns);
+    record_->duration_ns = now - record_->timestamp_ns;
   }
 
   ScopedEvent(ScopedEvent&& value) {
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index fa61596..c6c8095 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -384,13 +384,13 @@
 // with opening the same trace with ninjatracing + chrome://tracing.
 TEST_F(TraceProcessorIntegrationTest, NinjaLog) {
   ASSERT_TRUE(LoadTrace("ninja_log", 1024).ok());
-  auto it = Query("select count(*) from process where name like 'build';");
+  auto it = Query("select count(*) from process where name like 'Build';");
   ASSERT_TRUE(it.Next());
   ASSERT_EQ(it.Get(0).long_value, 2);
 
   it = Query(
       "select count(*) from thread left join process using(upid) where "
-      "thread.name like 'worker%' and process.pid=1");
+      "thread.name like 'Worker%' and process.pid=1");
   ASSERT_TRUE(it.Next());
   ASSERT_EQ(it.Get(0).long_value, 14);
 
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index f942406..70213e4 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -47,6 +47,7 @@
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
 #include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
 #include "src/trace_processor/iterator_impl.h"
+#include "src/trace_processor/sqlite/create_function.h"
 #include "src/trace_processor/sqlite/register_function.h"
 #include "src/trace_processor/sqlite/span_join_operator_table.h"
 #include "src/trace_processor/sqlite/sql_stats_table.h"
@@ -65,10 +66,10 @@
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
-#include "src/trace_processor/metrics/chrome/all_chrome_metrics.descriptor.h"
+#include "src/trace_processor/metrics/all_chrome_metrics.descriptor.h"
 #include "src/trace_processor/metrics/metrics.descriptor.h"
 #include "src/trace_processor/metrics/metrics.h"
-#include "src/trace_processor/metrics/sql_metrics.h"
+#include "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"
 
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <cxxabi.h>
@@ -109,6 +110,11 @@
   if (error) {
     PERFETTO_FATAL("Error setting pragma temp_store: %s", error);
   }
+  sqlite3_exec(db, "PRAGMA case_sensitive_like = 1", 0, 0, &error);
+  if (error) {
+    PERFETTO_FATAL("Error setting pragma case_sensitive_like: %s", error);
+  }
+
   sqlite3_str_split_init(db);
 // In Android tree builds, we don't have the percentile module.
 // Just don't include it.
@@ -742,6 +748,10 @@
   RegisterFunction<ExportJson>(db, "EXPORT_JSON", 1, context_.storage.get(),
                                false);
   RegisterFunction<ExtractArg>(db, "EXTRACT_ARG", 2, context_.storage.get());
+  RegisterFunction<CreateFunction>(
+      db, "CREATE_FUNCTION", 3,
+      std::unique_ptr<CreateFunction::Context>(
+          new CreateFunction::Context{db_.get(), &create_function_state_}));
 
   // Old style function registration.
   // TODO(lalitm): migrate this over to using RegisterFunction once aggregate
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index f94e6d5..90a4393 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -21,6 +21,7 @@
 
 #include <atomic>
 #include <functional>
+#include <map>
 #include <string>
 #include <vector>
 
@@ -28,6 +29,7 @@
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/status.h"
 #include "perfetto/trace_processor/trace_processor.h"
+#include "src/trace_processor/sqlite/create_function.h"
 #include "src/trace_processor/sqlite/db_sqlite_table.h"
 #include "src/trace_processor/sqlite/query_cache.h"
 #include "src/trace_processor/sqlite/scoped_db.h"
@@ -46,6 +48,12 @@
  public:
   explicit TraceProcessorImpl(const Config&);
 
+  TraceProcessorImpl(const TraceProcessorImpl&) = delete;
+  TraceProcessorImpl& operator=(const TraceProcessorImpl&) = delete;
+
+  TraceProcessorImpl(TraceProcessorImpl&&) = delete;
+  TraceProcessorImpl& operator=(TraceProcessorImpl&&) = delete;
+
   ~TraceProcessorImpl() override;
 
   // TraceProcessorStorage implementation:
@@ -104,7 +112,15 @@
   }
 
   bool IsRootMetricField(const std::string& metric_name);
+
+  // Keep this first: we need this to be destroyed after we clean up
+  // everything else.
   ScopedDb db_;
+
+  // State necessary for CREATE_FUNCTION invocations. We store this here as we
+  // need to finalize any prepared statements *before* we destroy the database.
+  CreateFunction::State create_function_state_;
+
   std::unique_ptr<QueryCache> query_cache_;
 
   DescriptorPool pool_;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 5454b7e..3fe3486 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -42,7 +42,7 @@
 
 #include "perfetto/trace_processor/read_trace.h"
 #include "perfetto/trace_processor/trace_processor.h"
-#include "src/trace_processor/metrics/chrome/all_chrome_metrics.descriptor.h"
+#include "src/trace_processor/metrics/all_chrome_metrics.descriptor.h"
 #include "src/trace_processor/metrics/metrics.descriptor.h"
 #include "src/trace_processor/metrics/metrics.h"
 #include "src/trace_processor/util/proto_to_json.h"
diff --git a/src/tracebox/tracebox.cc b/src/tracebox/tracebox.cc
index 4c40558..ed75b31 100644
--- a/src/tracebox/tracebox.cc
+++ b/src/tracebox/tracebox.cc
@@ -136,9 +136,9 @@
   // traced will write "1" and close the FD when the IPC socket is listening
   // (or traced crashed).
   base::Pipe traced_sync_pipe = base::Pipe::Create();
-  int wr_fd = *traced_sync_pipe.wr;
-  base::SetEnv("TRACED_NOTIFY_FD", std::to_string(wr_fd));
-  traced.args.preserve_fds.emplace_back(wr_fd);
+  int traced_fd = *traced_sync_pipe.wr;
+  base::SetEnv("TRACED_NOTIFY_FD", std::to_string(traced_fd));
+  traced.args.preserve_fds.emplace_back(traced_fd);
   // Create a new process group so CTRL-C is delivered only to the cmdline
   // process (the tracebox one) and not to traced. traced will still exit once
   // the main process exits, but this allows graceful stopping of the trace
@@ -161,8 +161,28 @@
   // Put traced_probes in the same process group as traced. Same reason (CTRL+C)
   // but it's not worth creating a new group.
   traced_probes.args.posix_proc_group_id = traced.pid();
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // |traced_probes_sync_pipe| is used to synchronize with traced socket
+  // creation. traced will write "1" and close the FD when the IPC socket is
+  // listening (or traced crashed).
+  base::Pipe traced_probes_sync_pipe = base::Pipe::Create();
+  int traced_probes_fd = *traced_probes_sync_pipe.wr;
+  base::SetEnv("TRACED_PROBES_NOTIFY_FD", std::to_string(traced_probes_fd));
+  traced_probes.args.preserve_fds.emplace_back(traced_probes_fd);
+#endif
   traced_probes.Start();
 
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  traced_probes_sync_pipe.wr.reset();
+
+  std::string traced_probes_notify_msg;
+  base::ReadPlatformHandle(*traced_probes_sync_pipe.rd,
+                           &traced_probes_notify_msg);
+  if (traced_probes_notify_msg != "1")
+    PERFETTO_FATAL(
+        "The traced_proces service failed unexpectedly. Check the logs");
+#endif
+
   perfetto_cmd.ConnectToServiceRunAndMaybeNotify();
   return 0;
 }
diff --git a/src/traced/probes/probes.cc b/src/traced/probes/probes.cc
index 75ba277..e13e56b 100644
--- a/src/traced/probes/probes.cc
+++ b/src/traced/probes/probes.cc
@@ -20,6 +20,7 @@
 #include <unistd.h>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/getopt.h"
 #include "perfetto/ext/base/unix_task_runner.h"
 #include "perfetto/ext/base/utils.h"
@@ -116,6 +117,18 @@
 
   base::UnixTaskRunner task_runner;
   ProbesProducer producer;
+  // If the TRACED_PROBES_NOTIFY_FD env var is set, write 1 and close the FD,
+  // when all data sources have been registered. This is used for //src/tracebox
+  // --background-wait, to make sure that the data sources are registered before
+  // waiting for them to be started.
+  const char* env_notif = getenv("TRACED_PROBES_NOTIFY_FD");
+  if (env_notif) {
+    int notif_fd = atoi(env_notif);
+    producer.SetAllDataSourcesRegisteredCb([notif_fd] {
+      PERFETTO_CHECK(base::WriteAll(notif_fd, "1", 1) == 1);
+      PERFETTO_CHECK(base::CloseFile(notif_fd) == 0);
+    });
+  }
   producer.ConnectWithRetries(GetProducerSocket(), &task_runner);
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 9a8cd2b..e33a2a0 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -122,6 +122,11 @@
       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() {
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index eb4df63..dda1759 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACED_PROBES_PROBES_PRODUCER_H_
 #define SRC_TRACED_PROBES_PROBES_PRODUCER_H_
 
+#include <functional>
 #include <memory>
 #include <unordered_map>
 #include <utility>
@@ -100,6 +101,11 @@
       const DataSourceConfig& config);
   void ActivateTrigger(std::string trigger);
 
+  // Calls `cb` when all data sources have been registered.
+  void SetAllDataSourcesRegisteredCb(std::function<void()> cb) {
+    all_data_sources_registered_cb_ = cb;
+  }
+
  private:
   static ProbesProducer* instance_;
 
@@ -139,6 +145,8 @@
   std::unordered_multimap<FlushRequestID, DataSourceInstanceID>
       pending_flushes_;
 
+  std::function<void()> all_data_sources_registered_cb_;
+
   std::unordered_map<DataSourceInstanceID, base::Watchdog::Timer> watchdogs_;
   LRUInodeCache cache_{kLRUInodeCacheSize};
   std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>
diff --git a/test/trace_processor/parsing/android_log_counts.sql b/test/trace_processor/parsing/android_log_counts.sql
index f22fea1..09fc250 100644
--- a/test/trace_processor/parsing/android_log_counts.sql
+++ b/test/trace_processor/parsing/android_log_counts.sql
@@ -18,5 +18,5 @@
 select count(*) as cnt from android_logs where prio > 4 union all
 select count(*) as cnt from android_logs where tag = 'screen_toggled' union all
 select count(*) as cnt from android_logs where tag like '%_pss' union all
-select count(*) as cnt from android_logs where msg like '%i2c_write%' union all
-select count(*) as cnt from android_logs where ts >= 1510113924391 and ts < 1512610021879;
\ No newline at end of file
+select count(*) as cnt from android_logs where msg like '%i2c_write%' or msg like '%I2C_Write%' union all
+select count(*) as cnt from android_logs where ts >= 1510113924391 and ts < 1512610021879;
diff --git a/test/trace_processor/parsing/android_log_msgs.sql b/test/trace_processor/parsing/android_log_msgs.sql
index 02093ca..9b46f43 100644
--- a/test/trace_processor/parsing/android_log_msgs.sql
+++ b/test/trace_processor/parsing/android_log_msgs.sql
@@ -17,7 +17,11 @@
 
 create view v2 as select tag, count(*) from android_logs group by tag order by 2 asc limit 5;
 
-create view v3 as select tag, count(*) from android_logs where msg like '%wakelock%' group by tag;
+create view v3 as
+select tag, count(*)
+from android_logs
+where msg like '%wakelock%' or msg like '%Wakelock%' or msg like '%WakeLock%' or msg like '%wakeLock%'
+group by tag;
 
 create view v4 as select msg, 1 from android_logs limit 10;
 
diff --git a/tools/gen_merged_sql_metrics.py b/tools/gen_amalgamated_sql_metrics.py
similarity index 100%
rename from tools/gen_merged_sql_metrics.py
rename to tools/gen_amalgamated_sql_metrics.py
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 53dfbc0..af7d95d 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -744,14 +744,14 @@
   return source_module
 
 
-def create_merged_sql_metrics_module(blueprint, target):
+def create_amalgamated_sql_metrics_module(blueprint, target):
   bp_module_name = label_to_module_name(target.name)
   module = Module('genrule', bp_module_name, target.name)
   module.tool_files = [
-      'tools/gen_merged_sql_metrics.py',
+      'tools/gen_amalgamated_sql_metrics.py',
   ]
   module.cmd = ' '.join([
-      '$(location tools/gen_merged_sql_metrics.py)',
+      '$(location tools/gen_amalgamated_sql_metrics.py)',
       '--cpp_out=$(out)',
       '$(in)',
   ])
@@ -843,8 +843,8 @@
     if module is None:
       return None
   elif target.type == 'action':
-    if 'gen_merged_sql_metrics' in target.name:
-      module = create_merged_sql_metrics_module(blueprint, target)
+    if 'gen_amalgamated_sql_metrics' in target.name:
+      module = create_amalgamated_sql_metrics_module(blueprint, target)
     elif re.match('.*gen_cc_.*_descriptor$', name_without_toolchain):
       module = create_cc_proto_descriptor_module(blueprint, target)
     elif target.type == 'action' and \
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 3a1b8ae..73b4e45 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -108,19 +108,19 @@
         'PERFETTO_CONFIG.deps.sqlite_ext_percentile'
     ],
     '//gn:zlib': ['PERFETTO_CONFIG.deps.zlib'],
-    '//src/trace_processor/metrics:gen_merged_sql_metrics': [[
-        ':cc_merged_sql_metrics'
+    '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics': [[
+        ':cc_amalgamated_sql_metrics'
     ]],
     gn_utils.GEN_VERSION_TARGET: ['PERFETTO_CONFIG.deps.version_header'],
 }
 
 
-def gen_sql_metrics(target):
+def gen_amalgamated_sql_metrics(target):
   label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule')
   label.srcs += [re.sub('^//', '', x) for x in sorted(target.inputs)]
   label.outs += target.outputs
-  label.cmd = r'$(location gen_merged_sql_metrics_py) --cpp_out=$@ $(SRCS)'
-  label.exec_tools += [':gen_merged_sql_metrics_py']
+  label.cmd = r'$(location gen_amalgamated_sql_metrics_py) --cpp_out=$@ $(SRCS)'
+  label.exec_tools += [':gen_amalgamated_sql_metrics_py']
   return [label]
 
 
@@ -144,7 +144,8 @@
 
 custom_actions = {
     gn_utils.GEN_VERSION_TARGET: gen_version_header,
-    '//src/trace_processor/metrics:gen_merged_sql_metrics': gen_sql_metrics,
+    '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics':
+        gen_amalgamated_sql_metrics,
 }
 
 # ------------------------------------------------------------------------------
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 3b9cf21..5d2b388 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -71,12 +71,12 @@
 // 6: Common PivotTableConfig and pivot table specific PivotTableState.
 // 7: Split Chrome categories in two and add 'symbolize ksyms' flag.
 // 8: Rename several variables
+// "[...]HeapProfileFlamegraph[...]" -> "[...]Flamegraph[...]".
 // 9: Add a field to track last loaded recording profile name
 // 10: Change last loaded profile tracking type to accommodate auto-save.
 // 11: Rename updateChromeCategories to fetchChromeCategories.
 // 12: Add a field to cache mapping from UI track ID to trace track ID in order
 //     to speed up flow arrows rendering.
-// "[...]HeapProfileFlamegraph[...]" -> "[...]Flamegraph[...]".
 export const STATE_VERSION = 12;
 
 export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';