Merge "Add note to download_changed_screenshots.py about running test_data"
diff --git a/Android.bp b/Android.bp
index 0d0ad96..0153be7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1980,6 +1980,7 @@
         ":perfetto_src_trace_processor_sqlite_functions_functions",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
+        ":perfetto_src_trace_processor_stdlib_stdlib",
         ":perfetto_src_trace_processor_storage_full",
         ":perfetto_src_trace_processor_storage_minimal",
         ":perfetto_src_trace_processor_storage_storage",
@@ -2171,6 +2172,7 @@
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
     ],
     defaults: [
         "perfetto_defaults",
@@ -9456,12 +9458,12 @@
         "src/trace_processor/metrics/sql/trace_stats.sql",
         "src/trace_processor/metrics/sql/webview/webview_power_usage.sql",
     ],
-    cmd: "$(location tools/gen_amalgamated_sql_metrics.py) --cpp_out=$(out) $(in)",
+    cmd: "$(location tools/gen_amalgamated_sql.py) --type=METRICS --cpp_out=$(out) $(in)",
     out: [
         "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h",
     ],
     tool_files: [
-        "tools/gen_amalgamated_sql_metrics.py",
+        "tools/gen_amalgamated_sql.py",
     ],
 }
 
@@ -9505,6 +9507,7 @@
         "src/trace_processor/sqlite/functions/create_function.cc",
         "src/trace_processor/sqlite/functions/create_function_internal.cc",
         "src/trace_processor/sqlite/functions/create_view_function.cc",
+        "src/trace_processor/sqlite/functions/import.cc",
         "src/trace_processor/sqlite/functions/pprof_functions.cc",
         "src/trace_processor/sqlite/functions/register_function.cc",
         "src/trace_processor/sqlite/functions/sqlite3_str_split.cc",
@@ -9553,6 +9556,26 @@
     ],
 }
 
+// GN: //src/trace_processor/stdlib:gen_amalgamated_stdlib
+genrule {
+    name: "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
+    srcs: [
+        "src/trace_processor/stdlib/android/binder.sql",
+    ],
+    cmd: "$(location tools/gen_amalgamated_sql.py) --type=LIB --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/stdlib/amalgamated_stdlib.h",
+    ],
+    tool_files: [
+        "tools/gen_amalgamated_sql.py",
+    ],
+}
+
+// GN: //src/trace_processor/stdlib:stdlib
+filegroup {
+    name: "perfetto_src_trace_processor_stdlib_stdlib",
+}
+
 // GN: //src/trace_processor:storage_full
 filegroup {
     name: "perfetto_src_trace_processor_storage_full",
@@ -11102,6 +11125,7 @@
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
         ":perfetto_src_trace_processor_sqlite_unittests",
+        ":perfetto_src_trace_processor_stdlib_stdlib",
         ":perfetto_src_trace_processor_storage_full",
         ":perfetto_src_trace_processor_storage_minimal",
         ":perfetto_src_trace_processor_storage_storage",
@@ -11322,6 +11346,7 @@
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
         "perfetto_src_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",
@@ -11736,6 +11761,7 @@
         ":perfetto_src_trace_processor_sqlite_functions_functions",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
+        ":perfetto_src_trace_processor_stdlib_stdlib",
         ":perfetto_src_trace_processor_storage_full",
         ":perfetto_src_trace_processor_storage_minimal",
         ":perfetto_src_trace_processor_storage_storage",
@@ -11804,6 +11830,7 @@
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
     ],
     defaults: [
         "perfetto_defaults",
@@ -11924,6 +11951,7 @@
         ":perfetto_src_trace_processor_sqlite_functions_functions",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
+        ":perfetto_src_trace_processor_stdlib_stdlib",
         ":perfetto_src_trace_processor_storage_full",
         ":perfetto_src_trace_processor_storage_minimal",
         ":perfetto_src_trace_processor_storage_storage",
@@ -11996,6 +12024,7 @@
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
         "perfetto_src_traceconv_gen_cc_trace_descriptor",
     ],
     defaults: [
diff --git a/BUILD b/BUILD
index 780a477..241398f 100644
--- a/BUILD
+++ b/BUILD
@@ -1379,9 +1379,9 @@
     outs = [
         "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h",
     ],
-    cmd = "$(location gen_amalgamated_sql_metrics_py) --cpp_out=$@ $(SRCS)",
+    cmd = "$(location gen_amalgamated_sql_py) --type=METRICS --cpp_out=$@ $(SRCS)",
     exec_tools = [
-        ":gen_amalgamated_sql_metrics_py",
+        ":gen_amalgamated_sql_py",
     ],
 )
 
@@ -1444,6 +1444,8 @@
         "src/trace_processor/sqlite/functions/create_function_internal.h",
         "src/trace_processor/sqlite/functions/create_view_function.cc",
         "src/trace_processor/sqlite/functions/create_view_function.h",
+        "src/trace_processor/sqlite/functions/import.cc",
+        "src/trace_processor/sqlite/functions/import.h",
         "src/trace_processor/sqlite/functions/pprof_functions.cc",
         "src/trace_processor/sqlite/functions/pprof_functions.h",
         "src/trace_processor/sqlite/functions/register_function.cc",
@@ -1490,6 +1492,28 @@
     ],
 )
 
+perfetto_genrule(
+    name = "src_trace_processor_stdlib_gen_amalgamated_stdlib",
+    srcs = [
+        "src/trace_processor/stdlib/android/binder.sql",
+    ],
+    outs = [
+        "src/trace_processor/stdlib/amalgamated_stdlib.h",
+    ],
+    cmd = "$(location gen_amalgamated_sql_py) --type=LIB --cpp_out=$@ $(SRCS)",
+    exec_tools = [
+        ":gen_amalgamated_sql_py",
+    ],
+)
+
+# GN target: //src/trace_processor/stdlib:stdlib
+perfetto_filegroup(
+    name = "src_trace_processor_stdlib_stdlib",
+    srcs = [
+        "src/trace_processor/stdlib/utils.h",
+    ],
+)
+
 # GN target: //src/trace_processor/storage:storage
 perfetto_filegroup(
     name = "src_trace_processor_storage_storage",
@@ -4254,6 +4278,7 @@
         ":src_trace_processor_sqlite_functions_functions",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_sqlite_sqlite_minimal",
+        ":src_trace_processor_stdlib_stdlib",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_storage_storage",
@@ -4339,6 +4364,8 @@
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
            PERFETTO_CONFIG.deps.zlib + [
         ":cc_amalgamated_sql_metrics",
+    ] + [
+        ":cc_amalgamated_stdlib",
     ] +
            PERFETTO_CONFIG.deps.demangle_wrapper,
     linkstatic = True,
@@ -4383,6 +4410,7 @@
         ":src_trace_processor_sqlite_functions_functions",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_sqlite_sqlite_minimal",
+        ":src_trace_processor_stdlib_stdlib",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_storage_storage",
@@ -4461,6 +4489,8 @@
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
            PERFETTO_CONFIG.deps.zlib + [
         ":cc_amalgamated_sql_metrics",
+    ] + [
+        ":cc_amalgamated_stdlib",
     ] +
            PERFETTO_CONFIG.deps.demangle_wrapper,
 )
@@ -4567,6 +4597,7 @@
         ":src_trace_processor_sqlite_functions_functions",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_sqlite_sqlite_minimal",
+        ":src_trace_processor_stdlib_stdlib",
         ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_storage_storage",
@@ -4644,6 +4675,8 @@
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
            PERFETTO_CONFIG.deps.zlib + [
         ":cc_amalgamated_sql_metrics",
+    ] + [
+        ":cc_amalgamated_stdlib",
     ] +
            PERFETTO_CONFIG.deps.demangle_wrapper,
 )
@@ -4695,12 +4728,17 @@
     hdrs = ["src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"],
 )
 
+perfetto_cc_library(
+    name = "cc_amalgamated_stdlib",
+    hdrs = ["src/trace_processor/stdlib/amalgamated_stdlib.h"],
+)
+
 perfetto_py_binary(
-    name = "gen_amalgamated_sql_metrics_py",
+    name = "gen_amalgamated_sql_py",
     srcs = [
-        "tools/gen_amalgamated_sql_metrics.py",
+        "tools/gen_amalgamated_sql.py",
     ],
-    main = "tools/gen_amalgamated_sql_metrics.py",
+    main = "tools/gen_amalgamated_sql.py",
     python_version = "PY3",
 )
 
diff --git a/BUILD.extras b/BUILD.extras
index f870aa6..5a11740 100644
--- a/BUILD.extras
+++ b/BUILD.extras
@@ -13,12 +13,17 @@
     hdrs = ["src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"],
 )
 
+perfetto_cc_library(
+    name = "cc_amalgamated_stdlib",
+    hdrs = ["src/trace_processor/stdlib/amalgamated_stdlib.h"],
+)
+
 perfetto_py_binary(
-    name = "gen_amalgamated_sql_metrics_py",
+    name = "gen_amalgamated_sql_py",
     srcs = [
-        "tools/gen_amalgamated_sql_metrics.py",
+        "tools/gen_amalgamated_sql.py",
     ],
-    main = "tools/gen_amalgamated_sql_metrics.py",
+    main = "tools/gen_amalgamated_sql.py",
     python_version = "PY3",
 )
 
diff --git a/bazel/rules.bzl b/bazel/rules.bzl
index c2a649b..b699471 100644
--- a/bazel/rules.bzl
+++ b/bazel/rules.bzl
@@ -22,7 +22,7 @@
 def default_cc_args():
     return {
         "deps": PERFETTO_CONFIG.deps.build_config,
-        "copts": [
+        "copts": PERFETTO_CONFIG.default_copts + [
             "-Wno-pragma-system-header-outside-header",
         ],
         "includes": ["include"],
diff --git a/bazel/standalone/perfetto_cfg.bzl b/bazel/standalone/perfetto_cfg.bzl
index d1b77e4..ce2764e 100644
--- a/bazel/standalone/perfetto_cfg.bzl
+++ b/bazel/standalone/perfetto_cfg.bzl
@@ -125,4 +125,9 @@
 
         go_proto_library = None,
     ),
+
+    # The default copts which we use to compile C++ code.
+    default_copts = [
+        "-std=c++17",
+    ]
 )
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 99627a2..023fa7d 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -705,11 +705,15 @@
     ]
 
     configs -= [
-      "//gn/standalone:c++11",
       "//gn/standalone:extra_warnings",
       "//gn/standalone:no_exceptions",
       "//gn/standalone:no_rtti",
     ]
+    if (perfetto_cpp11_until_q1_2023) {
+      configs -= [ "//gn/standalone:c++11" ]
+    } else {
+      configs -= [ "//gn/standalone:c++17" ]
+    }
     configs += [
       ":libc++config",
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
@@ -804,11 +808,15 @@
       configs += [ ":libc++winver" ]
     }
     configs -= [
-      "//gn/standalone:c++11",
       "//gn/standalone:extra_warnings",
       "//gn/standalone:no_exceptions",
       "//gn/standalone:no_rtti",
     ]
+    if (perfetto_cpp11_until_q1_2023) {
+      configs -= [ "//gn/standalone:c++11" ]
+    } else {
+      configs -= [ "//gn/standalone:c++17" ]
+    }
     if ((is_android || is_mac) && !custom_libcxx_is_static) {
       # Use libc++_perfetto to avoid conflicting with system libc++
       output_name = "libc++_perfettto"
@@ -1190,17 +1198,18 @@
   }
   configs -= [
     "//gn/standalone:extra_warnings",
-    "//gn/standalone:c++11",
     "//gn/standalone:visibility_hidden",
   ]
   cflags = [ "-DFAKE_LOG_DEVICE=1" ]
   if (!is_win) {
     cflags += [ "-Wno-deprecated-declarations" ]
   }
-  public_configs = [
-    ":libunwindstack_config",
-    "//gn/standalone:c++17",
-  ]
+  public_configs = [ ":libunwindstack_config" ]
+
+  if (perfetto_cpp11_until_q1_2023) {
+    configs -= [ "//gn/standalone:c++11" ]
+    public_configs += [ "//gn/standalone:c++17" ]
+  }
 }
 
 config("bionic_kernel_uapi_headers") {
@@ -1306,18 +1315,19 @@
   include_dirs = [ "llvm-project/llvm/include" ]
 }
 
-# NB: this is built under c++14 and linked into code that is c++11 by default.
-# We rely on the ABIs being compatible for this to be sane. At the time of
-# writing, the only c++14 specific code is behind an #ifndef NDEBUG, so we
-# could keep building as c++11 in non-debug builds, but we always use c++14 for
-# consistency.
 source_set("llvm_demangle") {
   visibility = _buildtools_visibility
-  configs -= [
-    "//gn/standalone:extra_warnings",
-    "//gn/standalone:c++11",
-  ]
-  configs += [ "//gn/standalone:c++14" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+
+  # NB: this is built under c++14 and linked into code that is c++11 by default.
+  # We rely on the ABIs being compatible for this to be sane. At the time of
+  # writing, the only c++14 specific code is behind an #ifndef NDEBUG, so we
+  # could keep building as c++11 in non-debug builds, but we always use c++14 for
+  # consistency.
+  if (perfetto_cpp11_until_q1_2023) {
+    configs -= [ "//gn/standalone:c++11" ]
+    configs += [ "//gn/standalone:c++14" ]
+  }
   public_configs = [ ":llvm_demangle_config" ]
   sources = [
     "llvm-project/llvm/include/llvm/Demangle/Demangle.h",
diff --git a/docs/README.md b/docs/README.md
index 9152c0a..1ce43f8 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -82,7 +82,7 @@
 to the Linux/Android tracing daemon through a UNIX socket, allowing to combine
 app-specific instrumentation points with system-wide tracing events.
 
-The SDK is based on portable C++11 code [tested](/docs/contributing/testing.md)
+The SDK is based on portable C++17 code [tested](/docs/contributing/testing.md)
 with the major C++ sanitizers (ASan, TSan, MSan, LSan). It doesn't rely on
 run-time code modifications or compiler plugins.
 
@@ -109,7 +109,7 @@
 dedicated project for importing, parsing and querying new and legacy trace
 formats, [Trace Processor](/docs/analysis/trace-processor.md).
 
-Trace Processor is a portable C++11 library that provides column-oriented
+Trace Processor is a portable C++17 library that provides column-oriented
 table storage, designed ad-hoc for efficiently holding hours of trace data
 into memory and exposes a SQL query interface based on the popular SQLite query
 engine.
diff --git a/docs/instrumentation/tracing-sdk.md b/docs/instrumentation/tracing-sdk.md
index 6bba47b..43261fb 100644
--- a/docs/instrumentation/tracing-sdk.md
+++ b/docs/instrumentation/tracing-sdk.md
@@ -1,6 +1,6 @@
 # Tracing SDK
 
-The Perfetto Tracing SDK is a C++11 library that allows userspace applications
+The Perfetto Tracing SDK is a C++17 library that allows userspace applications
 to emit trace events and add more app-specific context to a Perfetto trace.
 
 When using the Tracing SDK there are two main aspects to consider:
@@ -35,7 +35,7 @@
 
 The SDK consists of two files, `sdk/perfetto.h` and `sdk/perfetto.cc`. These are
 an amalgamation of the Client API designed to easy to integrate to existing
-build systems. The sources are self-contained and require only a C++11 compliant
+build systems. The sources are self-contained and require only a C++17 compliant
 standard library.
 
 For example, to add the SDK to a CMake project, edit your CMakeLists.txt:
diff --git a/examples/sdk/README.md b/examples/sdk/README.md
index 31e7145..e25c308 100644
--- a/examples/sdk/README.md
+++ b/examples/sdk/README.md
@@ -8,7 +8,7 @@
 Dependencies:
 
 - [CMake](https://cmake.org/)
-- C++11
+- C++17
 
 ## Building
 
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index 0d17328..bd21589 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -111,6 +111,10 @@
 }
 
 config("c++11") {
+  if (!perfetto_cpp11_until_q1_2023) {
+    visibility = []
+  }
+
   # C++11 is the default on Windows.
   if (!is_win) {
     cflags_cc = [ "-std=c++11" ]
@@ -118,11 +122,16 @@
     # Enable standards-conforming compiler behavior.
     cflags_cc = [ "/permissive-" ]
   }
+  defines = [ "PERFETTO_ALLOW_SUB_CPP17" ]
 }
 
 # Used in buildtools dependencies for standalone builds.
 config("c++14") {
-  visibility = [ "//buildtools:llvm_demangle" ]
+  if (perfetto_cpp11_until_q1_2023) {
+    visibility = [ "//buildtools:llvm_demangle" ]
+  } else {
+    visibility = []
+  }
   if (is_win) {
     cflags_cc = [ "/std:c++14" ]
   } else {
@@ -132,7 +141,9 @@
 
 # Used in buildtools dependencies for standalone builds.
 config("c++17") {
-  visibility = [ "//buildtools:libunwindstack" ]
+  if (perfetto_cpp11_until_q1_2023) {
+    visibility = [ "//buildtools:libunwindstack" ]
+  }
   if (is_win) {
     cflags_cc = [ "/std:c++17" ]
   } else {
@@ -196,6 +207,7 @@
       "/Gy",  # Enable function-level linking.
       "/FS",  # Preserve previous PDB behavior.
       "/utf-8",  # Assume UTF-8 by default to avoid code page dependencies.
+      "/Zc:__cplusplus",  # Allow use of __cplusplus macro.
     ]
     defines += [
       "_CRT_NONSTDC_NO_WARNINGS",
@@ -237,9 +249,11 @@
     not_needed([ "hermetic_clang_suppressions" ])
   }
 
-  if (is_clang && !is_hermetic_clang && !is_wasm) {
-    cflags_cc += [ "-stdlib=" + non_hermetic_clang_stdlib ]
-    ldflags += [ "-stdlib=" + non_hermetic_clang_stdlib ]
+  if (non_hermetic_clang_stdlib != "") {
+    if (is_clang && !is_hermetic_clang && !is_wasm) {
+      cflags_cc += [ "-stdlib=" + non_hermetic_clang_stdlib ]
+      ldflags += [ "-stdlib=" + non_hermetic_clang_stdlib ]
+    }
   }
 
   if (is_lto) {
diff --git a/gn/standalone/BUILDCONFIG.gn b/gn/standalone/BUILDCONFIG.gn
index 6f32686..772161b 100644
--- a/gn/standalone/BUILDCONFIG.gn
+++ b/gn/standalone/BUILDCONFIG.gn
@@ -17,6 +17,7 @@
   is_clang = true
   is_system_compiler = false
   is_lto = false
+  perfetto_cpp11_until_q1_2023 = false
 
   # This is defined here because it's needed below for determining the value of
   # |is_cross_compiling|.
@@ -65,7 +66,6 @@
 default_configs = [
   "//gn/standalone:debug_symbols",
   "//gn/standalone:default",
-  "//gn/standalone:c++11",
   "//gn/standalone:extra_warnings",
   "//gn/standalone:no_exceptions",
   "//gn/standalone:no_rtti",
@@ -74,6 +74,12 @@
   "//gn/standalone/sanitizers:sanitizers_cflags",
 ]
 
+if (perfetto_cpp11_until_q1_2023) {
+  default_configs += [ "//gn/standalone:c++11" ]
+} else {
+  default_configs += [ "//gn/standalone:c++17" ]
+}
+
 if (is_win) {
   default_configs += [ "//gn/standalone:win32_lean_and_mean" ]
 }
diff --git a/gn/standalone/toolchain/llvm.gni b/gn/standalone/toolchain/llvm.gni
index 3726411..13bae47 100644
--- a/gn/standalone/toolchain/llvm.gni
+++ b/gn/standalone/toolchain/llvm.gni
@@ -16,11 +16,7 @@
 
 declare_args() {
   is_hermetic_clang = is_clang && (is_linux_host || is_win_host)
-  if (is_linux_host) {
-    non_hermetic_clang_stdlib = "libstdc++"
-  } else {
-    non_hermetic_clang_stdlib = "libc++"
-  }
+  non_hermetic_clang_stdlib = ""
 }
 
 assert(!is_hermetic_clang || is_clang, "is_hermetic_clang requires is_clang")
diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h
index e95b676..c67e315 100644
--- a/include/perfetto/base/compiler.h
+++ b/include/perfetto/base/compiler.h
@@ -23,6 +23,22 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/public/compiler.h"
 
+#if __cplusplus >= 201703
+#define PERFETTO_IS_AT_LEAST_CPP17() 1
+#else
+#define PERFETTO_IS_AT_LEAST_CPP17() 0
+#endif
+
+#if !PERFETTO_IS_AT_LEAST_CPP17() && !defined(PERFETTO_ALLOW_SUB_CPP17)
+#error Perfetto is exploring a switch to C++17 in v34 (Feb 2023). During this \
+transitionary period, we are throwing an error when compiling Perfetto \
+with a standard less than C++17. Please reach out to \
+perfetto-dev@googlegroups.com if you have objections or thoughts on \
+this move. To continue compiling this release of Perfetto with \
+C++11/14, specify the define PERFETTO_ALLOW_SUB_CPP17. \
+*Note*: this define *will* stop working in v34 (Feb 2023).
+#endif
+
 // __has_attribute is supported only by clang and recent versions of GCC.
 // Add a layer to wrap the __has_attribute macro.
 #if defined(__has_attribute)
diff --git a/include/perfetto/trace_processor/basic_types.h b/include/perfetto/trace_processor/basic_types.h
index 7cfff9d..fd5539b 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -77,7 +77,8 @@
 enum class DropFtraceDataBefore {
   // Drops ftrace data before timestmap specified by the
   // TracingServiceEvent::tracing_started packet. If this packet is not in the
-  // trace, no data is dropped.
+  // trace, no data is dropped. If preserve_ftrace_buffer (from the trace
+  // config) is set, no data is dropped.
   // Note: this event was introduced in S+ so no data will be dropped on R-
   // traces.
   // This is the default approach.
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index d4e1017..3e91f96 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -144,6 +144,14 @@
     }                                                                          \
   } while (false)
 
+// C++17 doesn't like a move constructor being defined for the EventFinalizer
+// class but C++11 doesn't compile without it being defined so support both.
+#if PERFETTO_IS_AT_LEAST_CPP17()
+#define PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD delete
+#else
+#define PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD default
+#endif
+
 #define PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ...)             \
   struct PERFETTO_UID(ScopedEvent) {                                          \
     struct EventFinalizer {                                                   \
@@ -160,7 +168,8 @@
       EventFinalizer(const EventFinalizer&) = delete;                         \
       inline EventFinalizer& operator=(const EventFinalizer&) = delete;       \
                                                                               \
-      EventFinalizer(EventFinalizer&&) = default;                             \
+      EventFinalizer(EventFinalizer&&) =                                      \
+          PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD;                          \
       EventFinalizer& operator=(EventFinalizer&&) = delete;                   \
     } finalizer;                                                              \
   } PERFETTO_UID(scoped_event) {                                              \
diff --git a/infra/ci/config.py b/infra/ci/config.py
index ba8bf93..7a10502 100755
--- a/infra/ci/config.py
+++ b/infra/ci/config.py
@@ -66,10 +66,10 @@
 # Only variables starting with PERFETTO_ are propagated into the sandbox.
 JOB_CONFIGS = {
     'linux-clang-x86_64-debug': {
-        'PERFETTO_TEST_GN_ARGS':
-            'is_debug=true is_hermetic_clang=false non_hermetic_clang_stdlib="libc++"',
-        'PERFETTO_TEST_SCRIPT':
-            'test/ci/linux_tests.sh',
+        'PERFETTO_TEST_GN_ARGS': 'is_debug=true is_hermetic_clang=false '
+                                 'non_hermetic_clang_stdlib="libc++" '
+                                 'perfetto_cpp11_until_q1_2023=true',
+        'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
     },
     'linux-clang-x86_64-tsan': {
         'PERFETTO_TEST_GN_ARGS': 'is_debug=false is_tsan=true',
diff --git a/meson.build b/meson.build
index 5295c52..5ef6fce 100644
--- a/meson.build
+++ b/meson.build
@@ -19,7 +19,7 @@
 project(
     'perfetto',
     ['cpp'],
-    default_options: ['cpp_std=c++11']
+    default_options: ['cpp_std=c++17']
 )
 
 fs = import('fs')
diff --git a/protos/perfetto/config/ftrace/ftrace_config.proto b/protos/perfetto/config/ftrace/ftrace_config.proto
index 24727ed..f1267d0 100644
--- a/protos/perfetto/config/ftrace/ftrace_config.proto
+++ b/protos/perfetto/config/ftrace/ftrace_config.proto
@@ -18,7 +18,7 @@
 
 package perfetto.protos;
 
-// Next id: 23.
+// Next id: 24.
 message FtraceConfig {
   repeated string ftrace_events = 1;
   repeated string atrace_categories = 2;
@@ -152,4 +152,11 @@
   // * Available only on debuggable builds.
   // * Introduced in: Android U.
   repeated string function_graph_roots = 21;
+
+  // If true, does not clear ftrace buffers before the start of the program.
+  // This makes sense only if this is the first ftrace data source instance
+  // created after the daemon has been started. Can be useful for gathering boot
+  // traces, if ftrace has been separately configured (e.g. via kernel
+  // commandline).
+  optional bool preserve_ftrace_buffer = 23;
 }
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index d991280..a30527c 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -466,7 +466,7 @@
 
 // Begin of protos/perfetto/config/ftrace/ftrace_config.proto
 
-// Next id: 23.
+// Next id: 24.
 message FtraceConfig {
   repeated string ftrace_events = 1;
   repeated string atrace_categories = 2;
@@ -600,6 +600,13 @@
   // * Available only on debuggable builds.
   // * Introduced in: Android U.
   repeated string function_graph_roots = 21;
+
+  // If true, does not clear ftrace buffers before the start of the program.
+  // This makes sense only if this is the first ftrace data source instance
+  // created after the daemon has been started. Can be useful for gathering boot
+  // traces, if ftrace has been separately configured (e.g. via kernel
+  // commandline).
+  optional bool preserve_ftrace_buffer = 23;
 }
 
 // End of protos/perfetto/config/ftrace/ftrace_config.proto
diff --git a/protos/perfetto/trace/ftrace/ftrace_stats.proto b/protos/perfetto/trace/ftrace/ftrace_stats.proto
index 9f26fa3..fac68b0 100644
--- a/protos/perfetto/trace/ftrace/ftrace_stats.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_stats.proto
@@ -92,4 +92,8 @@
   // failed to enable due to permissions, or due to a conflicting option
   // (currently FtraceConfig.disable_generic_events).
   repeated string failed_ftrace_events = 7;
+
+  // The data source was configured to preserve existing events in the ftrace
+  // buffer before the start of the trace.
+  optional bool preserve_ftrace_buffer = 8;
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 6e01ded..251c12b 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -466,7 +466,7 @@
 
 // Begin of protos/perfetto/config/ftrace/ftrace_config.proto
 
-// Next id: 23.
+// Next id: 24.
 message FtraceConfig {
   repeated string ftrace_events = 1;
   repeated string atrace_categories = 2;
@@ -600,6 +600,13 @@
   // * Available only on debuggable builds.
   // * Introduced in: Android U.
   repeated string function_graph_roots = 21;
+
+  // If true, does not clear ftrace buffers before the start of the program.
+  // This makes sense only if this is the first ftrace data source instance
+  // created after the daemon has been started. Can be useful for gathering boot
+  // traces, if ftrace has been separately configured (e.g. via kernel
+  // commandline).
+  optional bool preserve_ftrace_buffer = 23;
 }
 
 // End of protos/perfetto/config/ftrace/ftrace_config.proto
@@ -7933,6 +7940,10 @@
   // failed to enable due to permissions, or due to a conflicting option
   // (currently FtraceConfig.disable_generic_events).
   repeated string failed_ftrace_events = 7;
+
+  // The data source was configured to preserve existing events in the ftrace
+  // buffer before the start of the trace.
+  optional bool preserve_ftrace_buffer = 8;
 }
 
 // End of protos/perfetto/trace/ftrace/ftrace_stats.proto
diff --git a/src/base/metatrace.cc b/src/base/metatrace.cc
index c676d80..1464ed3 100644
--- a/src/base/metatrace.cc
+++ b/src/base/metatrace.cc
@@ -29,7 +29,6 @@
 std::atomic<uint64_t> g_enabled_timestamp{0};
 
 // static members
-constexpr size_t RingBuffer::kCapacity;
 std::array<Record, RingBuffer::kCapacity> RingBuffer::records_;
 std::atomic<bool> RingBuffer::read_task_queued_;
 std::atomic<uint64_t> RingBuffer::wr_index_;
@@ -37,9 +36,12 @@
 std::atomic<bool> RingBuffer::has_overruns_;
 Record RingBuffer::bankruptcy_record_;
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
+constexpr size_t RingBuffer::kCapacity;
 constexpr uint16_t Record::kTypeMask;
 constexpr uint16_t Record::kTypeCounter;
 constexpr uint16_t Record::kTypeEvent;
+#endif
 
 namespace {
 
diff --git a/src/protozero/packed_repeated_fields.cc b/src/protozero/packed_repeated_fields.cc
index b0e23c5..16d4539 100644
--- a/src/protozero/packed_repeated_fields.cc
+++ b/src/protozero/packed_repeated_fields.cc
@@ -20,8 +20,10 @@
 
 namespace protozero {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr size_t PackedBufferBase::kOnStackStorageSize;
+#endif
 
 void PackedBufferBase::GrowSlowpath() {
   size_t write_off = static_cast<size_t>(write_ptr_ - storage_begin_);
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 9b1cc09..d815d04 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -367,6 +367,7 @@
       "metrics",
       "sqlite",
       "sqlite/functions",
+      "stdlib",
       "storage",
       "tables",
       "types",
diff --git a/src/trace_processor/containers/string_pool.cc b/src/trace_processor/containers/string_pool.cc
index 0189054..0ca5d0b 100644
--- a/src/trace_processor/containers/string_pool.cc
+++ b/src/trace_processor/containers/string_pool.cc
@@ -24,6 +24,7 @@
 namespace perfetto {
 namespace trace_processor {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr size_t StringPool::kNumBlockIndexBits;
 // static
@@ -38,6 +39,7 @@
 constexpr size_t StringPool::kBlockSizeBytes;
 // static
 constexpr size_t StringPool::kMinLargeStringSizeBytes;
+#endif
 
 StringPool::StringPool() {
   static_assert(
diff --git a/src/trace_processor/importers/common/args_translation_table.cc b/src/trace_processor/importers/common/args_translation_table.cc
index 1e4e22f..b35f427 100644
--- a/src/trace_processor/importers/common/args_translation_table.cc
+++ b/src/trace_processor/importers/common/args_translation_table.cc
@@ -49,6 +49,7 @@
 
 }  // namespace
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr char ArgsTranslationTable::kChromeHistogramHashKey[];
 constexpr char ArgsTranslationTable::kChromeHistogramNameKey[];
 
@@ -65,6 +66,7 @@
 constexpr char ArgsTranslationTable::kMojoMethodRelPcKey[];
 constexpr char ArgsTranslationTable::kMojoMethodNameKey[];
 constexpr char ArgsTranslationTable::kMojoIntefaceTagKey[];
+#endif
 
 ArgsTranslationTable::ArgsTranslationTable(TraceStorage* storage)
     : storage_(storage),
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 691f170..766c345 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -502,6 +502,9 @@
     auto error_str_id = storage->InternString(base::StringView(error_str));
     context_->metadata_tracker->SetMetadata(metadata::ftrace_setup_errors,
                                             Variadic::String(error_str_id));
+    if (evt.preserve_ftrace_buffer()) {
+      preserve_ftrace_buffer_ = true;
+    }
   }
 }
 
@@ -1002,7 +1005,10 @@
   if (PERFETTO_LIKELY(has_seen_first_ftrace_packet_)) {
     return;
   }
-  DropFtraceDataBefore drop_before = context_->config.drop_ftrace_data_before;
+
+  DropFtraceDataBefore drop_before =
+      preserve_ftrace_buffer_ ? DropFtraceDataBefore::kNoDrop
+                              : context_->config.drop_ftrace_data_before;
   switch (drop_before) {
     case DropFtraceDataBefore::kNoDrop: {
       drop_ftrace_data_before_ts_ = 0;
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 65a66d1..361d31f 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -367,6 +367,9 @@
   // Stores information about the timestamp from the metadata table which is
   // used to filter ftrace packets which happen before this point.
   int64_t drop_ftrace_data_before_ts_ = 0;
+
+  // Does not skip any ftrace events.
+  bool preserve_ftrace_buffer_ = false;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/track_event_tracker.cc b/src/trace_processor/importers/proto/track_event_tracker.cc
index 6497df1..c37eccd 100644
--- a/src/trace_processor/importers/proto/track_event_tracker.cc
+++ b/src/trace_processor/importers/proto/track_event_tracker.cc
@@ -24,8 +24,10 @@
 namespace perfetto {
 namespace trace_processor {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr uint64_t TrackEventTracker::kDefaultDescriptorTrackUuid;
+#endif
 
 TrackEventTracker::TrackEventTracker(TraceProcessorContext* context)
     : source_key_(context->storage->InternString("source")),
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index fe0d4a0..28ba43c 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -169,7 +169,7 @@
   "chrome/touch_jank.sql",
 ]
 
-experiemntal_sql_files = [
+experimental_sql_files = [
   "experimental/blink_gc_metric.sql",
   "experimental/chrome_dropped_frames.sql",
   "experimental/chrome_long_latency.sql",
@@ -185,7 +185,7 @@
   "webview/webview_power_usage.sql",
 ]
 
-sql_files = android_sql_files + chrome_sql_files + experiemntal_sql_files +
+sql_files = android_sql_files + chrome_sql_files + experimental_sql_files +
             other_sql_files
 
 config("gen_config") {
@@ -193,9 +193,10 @@
 }
 
 action("gen_amalgamated_sql_metrics") {
-  script = "../../../../tools/gen_amalgamated_sql_metrics.py"
+  script = "../../../../tools/gen_amalgamated_sql.py"
   generated_header = "${target_gen_dir}/amalgamated_sql_metrics.h"
   args = rebase_path(sql_files, root_build_dir) + [
+           "--type=METRICS",
            "--cpp_out",
            rebase_path(generated_header, root_build_dir),
          ]
diff --git a/src/trace_processor/sqlite/functions/BUILD.gn b/src/trace_processor/sqlite/functions/BUILD.gn
index a4f9e1f..3671615 100644
--- a/src/trace_processor/sqlite/functions/BUILD.gn
+++ b/src/trace_processor/sqlite/functions/BUILD.gn
@@ -23,6 +23,8 @@
       "create_function_internal.h",
       "create_view_function.cc",
       "create_view_function.h",
+      "import.cc",
+      "import.h",
       "pprof_functions.cc",
       "pprof_functions.h",
       "register_function.cc",
@@ -46,6 +48,7 @@
       "../../db",
       "../../dynamic",
       "../../importers/common",
+      "../../stdlib:stdlib",
       "../../storage",
       "../../types",
       "../../util",
diff --git a/src/trace_processor/sqlite/functions/import.cc b/src/trace_processor/sqlite/functions/import.cc
new file mode 100644
index 0000000..971cf0c
--- /dev/null
+++ b/src/trace_processor/sqlite/functions/import.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/sqlite/functions/import.h"
+
+#include <numeric>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/sqlite/functions/create_function_internal.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+base::Status Import::Run(Import::Context* ctx,
+                         size_t argc,
+                         sqlite3_value** argv,
+                         SqlValue&,
+                         Destructors&) {
+  if (argc != 1) {
+    return base::ErrStatus(
+        "IMPORT: invalid number of args; expected 1, received "
+        "%zu",
+        argc);
+  }
+  sqlite3_value* path_val = argv[0];
+
+  // Type check
+  {
+    base::Status status =
+        TypeCheckSqliteValue(path_val, SqlValue::Type::kString);
+    if (!status.ok()) {
+      return base::ErrStatus("IMPORT(%s): %s", sqlite3_value_text(path_val),
+                             status.c_message());
+    }
+  }
+
+  const char* path =
+      reinterpret_cast<const char*>(sqlite3_value_text(path_val));
+
+  auto lib_file = ctx->path_to_lib_file.Find(std::string(path));
+  if (!lib_file) {
+    return base::ErrStatus("IMPORT: Unknown filename provided - %s", path);
+  }
+  // IMPORT is noop for already imported files.
+  if (lib_file->imported) {
+    return base::OkStatus();
+  }
+
+  auto import_iter = ctx->tp->ExecuteQuery(lib_file->sql);
+  bool import_has_more = import_iter.Next();
+  if (import_has_more)
+    return base::ErrStatus("IMPORT: Imported file returning values.");
+  {
+    auto status = import_iter.Status();
+    if (!status.ok())
+      return base::ErrStatus("SQLite error on IMPORT: %s", status.c_message());
+  }
+
+  lib_file->imported = true;
+  return base::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/functions/import.h b/src/trace_processor/sqlite/functions/import.h
new file mode 100644
index 0000000..be20926
--- /dev/null
+++ b/src/trace_processor/sqlite/functions/import.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_FUNCTIONS_IMPORT_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_FUNCTIONS_IMPORT_H_
+
+#include <sqlite3.h>
+#include <string>
+#include <unordered_map>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/trace_processor/trace_processor.h"
+#include "src/trace_processor/sqlite/functions/register_function.h"
+#include "src/trace_processor/stdlib/utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct Import : public SqlFunction {
+  struct Context {
+    sqlite3* db;
+    TraceProcessor* tp;
+    base::FlatHashMap<std::string, stdlib::LibFile> path_to_lib_file;
+  };
+
+  static constexpr bool kVoidReturn = true;
+
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SQLITE_FUNCTIONS_IMPORT_H_
diff --git a/src/trace_processor/sqlite/sqlite_utils_unittest.cc b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
index 5d85383..1d1de13 100644
--- a/src/trace_processor/sqlite/sqlite_utils_unittest.cc
+++ b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
@@ -16,7 +16,6 @@
 
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 
-#include "gtest/gtest.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
diff --git a/src/trace_processor/stdlib/BUILD.gn b/src/trace_processor/stdlib/BUILD.gn
new file mode 100644
index 0000000..7d186ca
--- /dev/null
+++ b/src/trace_processor/stdlib/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../gn/perfetto.gni")
+import("android/sql_files.gni")
+
+config("gen_config") {
+  include_dirs = [ "${root_gen_dir}/${perfetto_root_path}" ]
+}
+
+sql_files = android_sql_files
+
+action("gen_amalgamated_stdlib") {
+  script = "../../../tools/gen_amalgamated_sql.py"
+  generated_header = "${target_gen_dir}/amalgamated_stdlib.h"
+  args = rebase_path(sql_files, root_build_dir) + [
+           "--type=LIB",
+           "--cpp_out",
+           rebase_path(generated_header, root_build_dir),
+         ]
+  inputs = sql_files
+  outputs = [ generated_header ]
+  public_configs = [ ":gen_config" ]
+}
+if (enable_perfetto_trace_processor_sqlite) {
+  source_set("stdlib") {
+    sources = [ "utils.h" ]
+    deps = [ ":gen_amalgamated_stdlib" ]
+  }
+}
diff --git a/src/trace_processor/stdlib/android/binder.sql b/src/trace_processor/stdlib/android/binder.sql
new file mode 100644
index 0000000..261916e
--- /dev/null
+++ b/src/trace_processor/stdlib/android/binder.sql
@@ -0,0 +1,33 @@
+--
+-- Copyright 2022 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+-- Count Binder transactions per process
+DROP VIEW IF EXISTS binder_metrics_by_process;
+CREATE VIEW binder_metrics_by_process AS
+SELECT
+  process.name as process_name,
+  process.pid as pid,
+  slice.name as slice_name,
+  COUNT(*) as event_count
+FROM slice
+  INNER JOIN thread_track ON slice.track_id=thread_track.id
+  INNER JOIN thread ON thread.utid=thread_track.utid
+  INNER JOIN process ON thread.upid=process.upid
+WHERE
+  slice.name glob 'binder*'
+GROUP BY
+  process_name,
+  slice_name;
diff --git a/src/trace_processor/stdlib/android/sql_files.gni b/src/trace_processor/stdlib/android/sql_files.gni
new file mode 100644
index 0000000..4304349
--- /dev/null
+++ b/src/trace_processor/stdlib/android/sql_files.gni
@@ -0,0 +1,16 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Keep this list sorted.
+android_sql_files = [ "android/binder.sql" ]
diff --git a/src/trace_processor/stdlib/utils.h b/src/trace_processor/stdlib/utils.h
new file mode 100644
index 0000000..a61a669
--- /dev/null
+++ b/src/trace_processor/stdlib/utils.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_STDLIB_UTILS_H_
+#define SRC_TRACE_PROCESSOR_STDLIB_UTILS_H_
+
+#include <string>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/stdlib/amalgamated_stdlib.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace stdlib {
+
+struct LibFile {
+  std::string sql;
+  bool imported;
+};
+
+inline std::string ReplaceSlashWithDot(std::string str) {
+  size_t found = str.find('/');
+  while (found != std::string::npos) {
+    str.replace(found, 1, ".");
+    found = str.find('/');
+  }
+  return str;
+}
+
+inline std::string GetImportKey(std::string root, std::string path) {
+  base::StringView path_view(path);
+  auto path_no_extension = path_view.substr(0, path_view.rfind('.'));
+  return ReplaceSlashWithDot(root) + "." +
+         ReplaceSlashWithDot(path_no_extension.ToStdString());
+}
+
+inline base::FlatHashMap<std::string, LibFile> SetupStdLib() {
+  base::FlatHashMap<std::string, LibFile> lib_files;
+  for (const auto& file_to_sql : stdlib::kFileToSql) {
+    lib_files.Insert(GetImportKey(stdlib::kRootPath, file_to_sql.path),
+                     {file_to_sql.sql, false});
+  }
+  return lib_files;
+}
+
+}  // namespace stdlib
+}  // namespace trace_processor
+}  // namespace perfetto
+#endif  // SRC_TRACE_PROCESSOR_STDLIB_UTILS_H_
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 558b27a..4ea7a55 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -52,6 +52,7 @@
 #include "src/trace_processor/iterator_impl.h"
 #include "src/trace_processor/sqlite/functions/create_function.h"
 #include "src/trace_processor/sqlite/functions/create_view_function.h"
+#include "src/trace_processor/sqlite/functions/import.h"
 #include "src/trace_processor/sqlite/functions/pprof_functions.h"
 #include "src/trace_processor/sqlite/functions/register_function.h"
 #include "src/trace_processor/sqlite/functions/sqlite3_str_split.h"
@@ -65,6 +66,7 @@
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 #include "src/trace_processor/sqlite/stats_table.h"
 #include "src/trace_processor/sqlite/window_operator_table.h"
+#include "src/trace_processor/stdlib/utils.h"
 #include "src/trace_processor/tp_metatrace.h"
 #include "src/trace_processor/types/variadic.h"
 #include "src/trace_processor/util/protozero_to_text.h"
@@ -705,6 +707,9 @@
       db, "CREATE_VIEW_FUNCTION", 3,
       std::unique_ptr<CreateViewFunction::Context>(
           new CreateViewFunction::Context{db_.get()}));
+  RegisterFunction<Import>(db, "IMPORT", 1,
+                           std::unique_ptr<Import::Context>(new Import::Context{
+                               db_.get(), this, stdlib::SetupStdLib()}));
 
   // Old style function registration.
   // TODO(lalitm): migrate this over to using RegisterFunction once aggregate
@@ -1001,8 +1006,8 @@
     }
   }
 
-  // Check if the metric with the given path already exists and if it does, just
-  // update the SQL associated with it.
+  // Check if the metric with the given path already exists and if it does,
+  // just update the SQL associated with it.
   auto it = std::find_if(
       sql_metrics_.begin(), sql_metrics_.end(),
       [&path](const metrics::SqlMetricFile& m) { return m.path == path; });
@@ -1040,7 +1045,8 @@
       const auto& prev_path = field_it_and_inserted.first->second;
       PERFETTO_DCHECK(prev_path != path);
       return base::ErrStatus(
-          "RegisterMetric Error: Metric paths %s (which is already registered) "
+          "RegisterMetric Error: Metric paths %s (which is already "
+          "registered) "
           "and %s are both trying to output the proto field %s",
           prev_path.c_str(), path.c_str(), metric.proto_field_name->c_str());
     }
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 7a4ecb8..49192e5 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -29,6 +29,7 @@
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/status.h"
 #include "perfetto/trace_processor/trace_processor.h"
+#include "sqlite/functions/import.h"
 #include "src/trace_processor/sqlite/db_sqlite_table.h"
 #include "src/trace_processor/sqlite/functions/create_function.h"
 #include "src/trace_processor/sqlite/functions/create_view_function.h"
diff --git a/src/trace_processor/trace_sorter_queue_unittest.cc b/src/trace_processor/trace_sorter_queue_unittest.cc
index b233fe7..d013c91 100644
--- a/src/trace_processor/trace_sorter_queue_unittest.cc
+++ b/src/trace_processor/trace_sorter_queue_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 #include "src/trace_processor/trace_sorter_queue.h"
-#include "gtest/gtest.h"
+
 #include "src/trace_processor/types/variadic.h"
 #include "test/gtest_and_gmock.h"
 
diff --git a/src/trace_processor/types/variadic.cc b/src/trace_processor/types/variadic.cc
index 837bfeb..9a26bd5 100644
--- a/src/trace_processor/types/variadic.cc
+++ b/src/trace_processor/types/variadic.cc
@@ -19,7 +19,9 @@
 namespace perfetto {
 namespace trace_processor {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr const char* Variadic::kTypeNames[];
+#endif
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/cpu_reader_benchmark.cc b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
index a22fc1b..fff4e78 100644
--- a/src/traced/probes/ftrace/cpu_reader_benchmark.cc
+++ b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
@@ -575,7 +575,8 @@
                                    base::nullopt,
                                    {},
                                    {},
-                                   false /*symbolize_ksyms*/};
+                                   false /*symbolize_ksyms*/,
+                                   false /*preserve_ftrace_buffer*/};
   if (print_filter.has_value()) {
     ds_config.print_filter =
         FtracePrintFilterConfig::Create(print_filter.value(), table);
diff --git a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
index 04891a3..52a986d 100644
--- a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
+++ b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
@@ -60,7 +60,8 @@
                                    base::nullopt,
                                    {},
                                    {},
-                                   /*symbolize_ksyms=*/false};
+                                   /*symbolize_ksyms=*/false,
+                                   /*preserve_ftrace_buffer=*/false};
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
   ds_config.event_filter.AddEnabledEvent(
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 2f8061f..1b14a82 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -69,7 +69,8 @@
                                 base::nullopt,
                                 {},
                                 {},
-                                false /*symbolize_ksyms*/};
+                                false /*symbolize_ksyms*/,
+                                false /*preserve_ftrace_buffer*/};
 }
 
 constexpr uint64_t kNanoInSecond = 1000 * 1000 * 1000;
@@ -947,7 +948,8 @@
                                    base::nullopt,
                                    {},
                                    {},
-                                   false /* symbolize_ksyms*/};
+                                   false /* symbolize_ksyms*/,
+                                   false /*preserve_ftrace_buffer*/};
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index ff807bc..82a0f11 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -571,14 +571,16 @@
     PERFETTO_DCHECK(active_configs_.empty());
 
     // If someone outside of perfetto is using ftrace give up now.
-    if (is_ftrace_enabled && !IsOldAtrace()) {
+    if (!request.preserve_ftrace_buffer() && is_ftrace_enabled &&
+        !IsOldAtrace()) {
       PERFETTO_ELOG("ftrace in use by non-Perfetto.");
       return 0;
     }
 
     // Setup ftrace, without starting it. Setting buffers can be quite slow
     // (up to hundreds of ms).
-    SetupClock(request);
+    if (!request.preserve_ftrace_buffer())
+      SetupClock(request);
     SetupBufferSize(request);
   } else {
     // Did someone turn ftrace off behind our back? If so give up.
@@ -709,7 +711,8 @@
       std::forward_as_tuple(std::move(filter), std::move(syscall_filter),
                             compact_sched, std::move(ftrace_print_filter),
                             std::move(apps), std::move(categories),
-                            request.symbolize_ksyms()));
+                            request.symbolize_ksyms(),
+                            request.preserve_ftrace_buffer()));
   return id;
 }
 
@@ -720,7 +723,8 @@
   }
 
   if (active_configs_.empty()) {
-    if (ftrace_->IsTracingEnabled() && !IsOldAtrace()) {
+    if (!ds_configs_.at(id).preserve_ftrace_buffer &&
+        ftrace_->IsTracingEnabled() && !IsOldAtrace()) {
       // If someone outside of perfetto is using ftrace give up now.
       PERFETTO_ELOG("ftrace in use by non-Perfetto.");
       return false;
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.h b/src/traced/probes/ftrace/ftrace_config_muxer.h
index 5fe66ae..50a5e4f 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.h
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.h
@@ -47,14 +47,16 @@
                          base::Optional<FtracePrintFilterConfig> _print_filter,
                          std::vector<std::string> _atrace_apps,
                          std::vector<std::string> _atrace_categories,
-                         bool _symbolize_ksyms)
+                         bool _symbolize_ksyms,
+                         bool _preserve_ftrace_buffer)
       : event_filter(std::move(_event_filter)),
         syscall_filter(std::move(_syscall_filter)),
         compact_sched(_compact_sched),
         print_filter(std::move(_print_filter)),
         atrace_apps(std::move(_atrace_apps)),
         atrace_categories(std::move(_atrace_categories)),
-        symbolize_ksyms(_symbolize_ksyms) {}
+        symbolize_ksyms(_symbolize_ksyms),
+        preserve_ftrace_buffer(_preserve_ftrace_buffer) {}
 
   // The event filter allows to quickly check if a certain ftrace event with id
   // x is enabled for this data source.
@@ -77,6 +79,9 @@
 
   // When enabled will turn on the kallsyms symbolizer in CpuReader.
   const bool symbolize_ksyms;
+
+  // Does not clear previous traces.
+  const bool preserve_ftrace_buffer;
 };
 
 // Ftrace is a bunch of globally modifiable persistent state.
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 64ed5db..d7ad539 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -139,9 +139,10 @@
 // static
 std::unique_ptr<FtraceController> FtraceController::Create(
     base::TaskRunner* runner,
-    Observer* observer) {
+    Observer* observer,
+    bool preserve_ftrace_buffer) {
   std::unique_ptr<FtraceProcfs> ftrace_procfs =
-      FtraceProcfs::CreateGuessingMountPoint();
+      FtraceProcfs::CreateGuessingMountPoint("", preserve_ftrace_buffer);
 
   if (!ftrace_procfs)
     return nullptr;
@@ -172,16 +173,17 @@
   std::unique_ptr<FtraceConfigMuxer> model =
       std::unique_ptr<FtraceConfigMuxer>(new FtraceConfigMuxer(
           ftrace_procfs.get(), table.get(), std::move(syscalls), vendor_evts));
-  return std::unique_ptr<FtraceController>(
-      new FtraceController(std::move(ftrace_procfs), std::move(table),
-                           std::move(model), runner, observer));
+  return std::unique_ptr<FtraceController>(new FtraceController(
+      std::move(ftrace_procfs), std::move(table), std::move(model), runner,
+      observer, preserve_ftrace_buffer));
 }
 
 FtraceController::FtraceController(std::unique_ptr<FtraceProcfs> ftrace_procfs,
                                    std::unique_ptr<ProtoTranslationTable> table,
                                    std::unique_ptr<FtraceConfigMuxer> model,
                                    base::TaskRunner* task_runner,
-                                   Observer* observer)
+                                   Observer* observer,
+                                   bool preserve_ftrace_buffer)
     : task_runner_(task_runner),
       observer_(observer),
       symbolizer_(new LazyKernelSymbolizer()),
@@ -189,6 +191,7 @@
       table_(std::move(table)),
       ftrace_config_muxer_(std::move(model)),
       ftrace_clock_snapshot_(new FtraceClockSnapshot()),
+      preserve_ftrace_buffer_(preserve_ftrace_buffer),
       weak_factory_(this) {}
 
 FtraceController::~FtraceController() {
diff --git a/src/traced/probes/ftrace/ftrace_controller.h b/src/traced/probes/ftrace/ftrace_controller.h
index e27f465..9a8ec2b 100644
--- a/src/traced/probes/ftrace/ftrace_controller.h
+++ b/src/traced/probes/ftrace/ftrace_controller.h
@@ -72,7 +72,9 @@
   };
 
   // The passed Observer must outlive the returned FtraceController instance.
-  static std::unique_ptr<FtraceController> Create(base::TaskRunner*, Observer*);
+  static std::unique_ptr<FtraceController> Create(base::TaskRunner*,
+                                                  Observer*,
+                                                  bool preserve_ftrace_buffer);
   virtual ~FtraceController();
 
   void DisableAllEvents();
@@ -98,7 +100,8 @@
                    std::unique_ptr<ProtoTranslationTable>,
                    std::unique_ptr<FtraceConfigMuxer>,
                    base::TaskRunner*,
-                   Observer*);
+                   Observer*,
+                   bool);
 
   // Protected and virtual for testing.
   virtual uint64_t NowMs() const;
@@ -138,6 +141,7 @@
   int generation_ = 0;
   bool atrace_running_ = false;
   bool retain_ksyms_on_stop_ = false;
+  bool preserve_ftrace_buffer_ = false;
   std::vector<PerCpuState> per_cpu_;  // empty if tracing isn't active
   std::set<FtraceDataSource*> data_sources_;
   std::set<FtraceDataSource*> started_data_sources_;
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index b521274..a9ad430 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -175,7 +175,8 @@
                          std::move(table),
                          std::move(model),
                          runner.get(),
-                         /*observer=*/this),
+                         /*observer=*/this,
+                         false),
         runner_(std::move(runner)),
         procfs_(raw_procfs) {}
 
diff --git a/src/traced/probes/ftrace/ftrace_data_source.cc b/src/traced/probes/ftrace/ftrace_data_source.cc
index 32f945e..7c051fb 100644
--- a/src/traced/probes/ftrace/ftrace_data_source.cc
+++ b/src/traced/probes/ftrace/ftrace_data_source.cc
@@ -112,6 +112,13 @@
     return;
   DumpFtraceStats(&stats_before_);
   setup_errors_ = FtraceSetupErrors();  // Dump only on START_OF_TRACE.
+
+  if (config_.preserve_ftrace_buffer()) {
+    auto stats_packet = writer_->NewTracePacket();
+    auto* stats = stats_packet->set_ftrace_stats();
+    stats->set_phase(protos::pbzero::FtraceStats::Phase::START_OF_TRACE);
+    stats->set_preserve_ftrace_buffer(true);
+  }
 }
 
 void FtraceDataSource::DumpFtraceStats(FtraceStats* stats) {
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 425a3cb..aa0d31d 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -83,7 +83,8 @@
 
 // static
 std::unique_ptr<FtraceProcfs> FtraceProcfs::CreateGuessingMountPoint(
-    const std::string& instance_path) {
+    const std::string& instance_path,
+    bool preserve_ftrace_buffer) {
   std::unique_ptr<FtraceProcfs> ftrace_procfs;
   size_t index = 0;
   while (!ftrace_procfs && kTracingPaths[index]) {
@@ -91,14 +92,16 @@
     if (!instance_path.empty())
       path += instance_path;
 
-    ftrace_procfs = Create(path);
+    ftrace_procfs = Create(path, preserve_ftrace_buffer);
   }
   return ftrace_procfs;
 }
 
 // static
-std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
-  if (!CheckRootPath(root)) {
+std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(
+    const std::string& root,
+    bool preserve_ftrace_buffer) {
+  if (!preserve_ftrace_buffer && !CheckRootPath(root)) {
     return nullptr;
   }
   return std::unique_ptr<FtraceProcfs>(new FtraceProcfs(root));
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index fb39fee..27411d1 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -34,9 +34,12 @@
   // Takes an optional |instance_path| such as "instances/wifi/", in which case
   // the returned object will be for that ftrace instance path.
   static std::unique_ptr<FtraceProcfs> CreateGuessingMountPoint(
-      const std::string& instance_path = "");
+      const std::string& instance_path = "",
+      bool preserve_ftrace_buffer = false);
 
-  static std::unique_ptr<FtraceProcfs> Create(const std::string& root);
+  static std::unique_ptr<FtraceProcfs> Create(
+      const std::string& root,
+      bool preserve_ftrace_buffer = false);
   static int g_kmesg_fd;
 
   explicit FtraceProcfs(const std::string& root);
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index f78f9bd..2febdbd 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -125,9 +125,12 @@
   if (ftrace_creation_failed_)
     return nullptr;
 
+  FtraceConfig ftrace_config;
+  ftrace_config.ParseFromString(config.ftrace_config_raw());
   // Lazily create on the first instance.
   if (!ftrace_) {
-    ftrace_ = FtraceController::Create(task_runner_, this);
+    ftrace_ = FtraceController::Create(task_runner_, this,
+                                       ftrace_config.preserve_ftrace_buffer());
 
     if (!ftrace_) {
       PERFETTO_ELOG("Failed to create FtraceController");
@@ -135,14 +138,14 @@
       return nullptr;
     }
 
-    ftrace_->DisableAllEvents();
-    ftrace_->ClearTrace();
+    if (!ftrace_config.preserve_ftrace_buffer()) {
+      ftrace_->DisableAllEvents();
+      ftrace_->ClearTrace();
+    }
   }
 
   PERFETTO_LOG("Ftrace setup (target_buf=%" PRIu32 ")", config.target_buffer());
   const BufferID buffer_id = static_cast<BufferID>(config.target_buffer());
-  FtraceConfig ftrace_config;
-  ftrace_config.ParseFromString(config.ftrace_config_raw());
   std::unique_ptr<FtraceDataSource> data_source(new FtraceDataSource(
       ftrace_->GetWeakPtr(), session_id, std::move(ftrace_config),
       endpoint_->CreateTraceWriter(buffer_id)));
diff --git a/src/tracing/core/metatrace_writer.cc b/src/tracing/core/metatrace_writer.cc
index f6acfa0..c270632 100644
--- a/src/tracing/core/metatrace_writer.cc
+++ b/src/tracing/core/metatrace_writer.cc
@@ -26,8 +26,10 @@
 
 namespace perfetto {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr char MetatraceWriter::kDataSourceName[];
+#endif
 
 MetatraceWriter::MetatraceWriter() : weak_ptr_factory_(this) {}
 
diff --git a/src/tracing/core/shared_memory_abi.cc b/src/tracing/core/shared_memory_abi.cc
index 9461848..0c4694b 100644
--- a/src/tracing/core/shared_memory_abi.cc
+++ b/src/tracing/core/shared_memory_abi.cc
@@ -69,6 +69,7 @@
 
 }  // namespace
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr uint32_t SharedMemoryABI::kNumChunksForLayout[];
 constexpr const char* SharedMemoryABI::kChunkStateStr[];
@@ -76,6 +77,7 @@
 constexpr const size_t SharedMemoryABI::kMinPageSize;
 constexpr const size_t SharedMemoryABI::kMaxPageSize;
 constexpr const size_t SharedMemoryABI::kPacketSizeDropPacket;
+#endif
 
 SharedMemoryABI::SharedMemoryABI() = default;
 
diff --git a/src/tracing/core/shared_memory_arbiter_impl.cc b/src/tracing/core/shared_memory_arbiter_impl.cc
index 7928676..f1b830a 100644
--- a/src/tracing/core/shared_memory_arbiter_impl.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl.cc
@@ -52,8 +52,10 @@
 SharedMemoryABI::PageLayout SharedMemoryArbiterImpl::default_page_layout =
     SharedMemoryABI::PageLayout::kPageDiv1;
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr BufferID SharedMemoryArbiterImpl::kInvalidBufferId;
+#endif
 
 // static
 std::unique_ptr<SharedMemoryArbiter> SharedMemoryArbiter::CreateInstance(
diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc
index 41633aa..c0527ad 100644
--- a/src/tracing/core/trace_buffer.cc
+++ b/src/tracing/core/trace_buffer.cc
@@ -42,8 +42,10 @@
     SharedMemoryABI::ChunkHeader::kChunkNeedsPatching;
 }  // namespace.
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr size_t TraceBuffer::ChunkRecord::kMaxSize;
-constexpr size_t TraceBuffer::InlineChunkHeaderSize = sizeof(ChunkRecord);
+#endif
+const size_t TraceBuffer::InlineChunkHeaderSize = sizeof(ChunkRecord);
 
 // static
 std::unique_ptr<TraceBuffer> TraceBuffer::Create(size_t size_in_bytes,
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index f910a90..da4e34c 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -312,10 +312,12 @@
 
 }  // namespace
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // These constants instead are defined in the header because are used by tests.
 constexpr size_t TracingServiceImpl::kMaxShmSize;
 constexpr uint32_t TracingServiceImpl::kDataSourceStopTimeoutMs;
 constexpr uint8_t TracingServiceImpl::kSyncMarker[];
+#endif
 
 std::string GetBugreportPath() {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
diff --git a/src/tracing/core/virtual_destructors.cc b/src/tracing/core/virtual_destructors.cc
index 3bcedce..ad9522e 100644
--- a/src/tracing/core/virtual_destructors.cc
+++ b/src/tracing/core/virtual_destructors.cc
@@ -36,7 +36,9 @@
 SharedMemory::Factory::~Factory() = default;
 SharedMemoryArbiter::~SharedMemoryArbiter() = default;
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr size_t TracingService::kDefaultShmSize;
 constexpr size_t TracingService::kDefaultShmPageSize;
+#endif
 
 }  // namespace perfetto
diff --git a/src/tracing/test/api_integrationtest_main.cc b/src/tracing/test/api_integrationtest_main.cc
index 49fb0d4..5073244 100644
--- a/src/tracing/test/api_integrationtest_main.cc
+++ b/src/tracing/test/api_integrationtest_main.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
+#include "test/gtest_and_gmock.h"
 
 #include "perfetto/tracing.h"
 
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
index 1bc4017..5863d55 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
@@ -1 +1 @@
-9da477a6b9736f3e06a2940543ef54fb86ed21a9ec975d61851d99cb16004e59
\ No newline at end of file
+f3c0069c0ef9643552081835fd06c972b87448b7df5ac538a4d3e213a437bcb1
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
index d5dcb60..e70add7 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
@@ -1 +1 @@
-f8bcea9fc715ac226cd7a50201eac183603c22b881070265d2fb9fb015357434
\ No newline at end of file
+04b6ed7959eef3625b2d25d9daa68e7caea3f8ad4846234a45a6c264aebc8e47
\ No newline at end of file
diff --git a/tools/gen_amalgamated_sql_metrics.py b/tools/gen_amalgamated_sql.py
similarity index 81%
rename from tools/gen_amalgamated_sql_metrics.py
rename to tools/gen_amalgamated_sql.py
index 16f6aac..099bc92 100755
--- a/tools/gen_amalgamated_sql_metrics.py
+++ b/tools/gen_amalgamated_sql.py
@@ -21,7 +21,7 @@
 # as a string constant to allow trace processor to exectue the metrics.
 
 REPLACEMENT_HEADER = '''/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -45,13 +45,32 @@
  #include <string.h>
 '''
 
-NAMESPACE_BEGIN = '''
+STDLIB_NAMESPACE_BEGIN = '''
+namespace perfetto {
+namespace trace_processor {
+namespace stdlib {
+'''
+
+STDLIB_NAMESPACE_END = '''
+}  // namespace stdlib
+}  // namespace trace_processor
+}  // namespace perfetto
+'''
+
+METRICS_NAMESPACE_BEGIN = '''
 namespace perfetto {
 namespace trace_processor {
 namespace metrics {
 namespace sql_metrics {
 '''
 
+METRICS_NAMESPACE_END = '''
+}  // namespace sql_metrics
+}  // namespace metrics
+}  // namespace trace_processor
+}  // namespace perfetto
+'''
+
 FILE_TO_SQL_STRUCT = '''
 struct FileToSql {
   const char* path;
@@ -59,12 +78,8 @@
 };
 '''
 
-NAMESPACE_END = '''
-}  // namespace sql_metrics
-}  // namespace metrics
-}  // namespace trace_processor
-}  // namsepace perfetto
-'''
+ROOT = '''
+const char kRootPath[] = '''
 
 
 def filename_to_variable(filename):
@@ -73,11 +88,14 @@
 
 def main():
   parser = argparse.ArgumentParser()
+  parser.add_argument('--type', required=True)
   parser.add_argument('--cpp_out', required=True)
   parser.add_argument('sql_files', nargs='*')
   args = parser.parse_args()
 
   root_path = os.path.commonprefix([os.path.abspath(x) for x in args.sql_files])
+  if '.sql' in root_path:
+    root_path = root_path.rsplit('/', 1)[0]
 
   # Extract the SQL output from each file.
   sql_outputs = {}
@@ -89,7 +107,13 @@
 
   with open(args.cpp_out, 'w+') as output:
     output.write(REPLACEMENT_HEADER)
-    output.write(NAMESPACE_BEGIN)
+    output.write(
+        METRICS_NAMESPACE_BEGIN) if args.type == 'METRICS' else output.write(
+            STDLIB_NAMESPACE_BEGIN)
+
+    if args.type == "LIB":
+      output.write(ROOT + f'''"{root_path.rsplit("stdlib/")[-1]}";
+      ''')
 
     # Create the C++ variable for each SQL file.
     for path, sql in sql_outputs.items():
@@ -122,7 +146,9 @@
       output.write('\n  {{"{}", {}}},\n'.format(path, variable))
     output.write("};\n")
 
-    output.write(NAMESPACE_END)
+    output.write(
+        METRICS_NAMESPACE_END) if args.type == 'METRICS' else output.write(
+            STDLIB_NAMESPACE_END)
 
   return 0
 
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 2469958..0fc587c 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -771,10 +771,11 @@
   bp_module_name = label_to_module_name(target.name)
   module = Module('genrule', bp_module_name, target.name)
   module.tool_files = [
-      'tools/gen_amalgamated_sql_metrics.py',
+      'tools/gen_amalgamated_sql.py',
   ]
   module.cmd = ' '.join([
-      '$(location tools/gen_amalgamated_sql_metrics.py)',
+      '$(location tools/gen_amalgamated_sql.py)',
+      '--type=METRICS',
       '--cpp_out=$(out)',
       '$(in)',
   ])
@@ -784,6 +785,23 @@
   blueprint.add_module(module)
   return module
 
+def create_amalgamated_stdlib_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_amalgamated_sql.py',
+  ]
+  module.cmd = ' '.join([
+      '$(location tools/gen_amalgamated_sql.py)',
+      '--type=LIB',
+      '--cpp_out=$(out)',
+      '$(in)',
+  ])
+  module.genrule_headers.add(module.name)
+  module.out.update(target.outputs)
+  module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
+  blueprint.add_module(module)
+  return module
 
 def create_cc_proto_descriptor_module(blueprint, target):
   bp_module_name = label_to_module_name(target.name)
@@ -891,6 +909,8 @@
   elif target.type == 'action':
     if 'gen_amalgamated_sql_metrics' in target.name:
       module = create_amalgamated_sql_metrics_module(blueprint, target)
+    elif 'gen_amalgamated_stdlib' in target.name:
+      module = create_amalgamated_stdlib_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 1c64257..e64307a 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -114,6 +114,9 @@
     '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics': [[
         ':cc_amalgamated_sql_metrics'
     ]],
+    '//src/trace_processor/stdlib:gen_amalgamated_stdlib': [[
+        ':cc_amalgamated_stdlib'
+    ]],
     gn_utils.GEN_VERSION_TARGET: ['PERFETTO_CONFIG.deps.version_header'],
 }
 
@@ -122,8 +125,18 @@
   label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule')
   label.srcs += [re.sub('^//', '', x) for x in sorted(target.inputs)]
   label.outs += target.outputs
-  label.cmd = r'$(location gen_amalgamated_sql_metrics_py) --cpp_out=$@ $(SRCS)'
-  label.exec_tools += [':gen_amalgamated_sql_metrics_py']
+  label.cmd = (r'$(location gen_amalgamated_sql_py) ' 
+  r'--type=METRICS --cpp_out=$@ $(SRCS)')
+  label.exec_tools += [':gen_amalgamated_sql_py']
+  return [label]
+
+def gen_amalgamated_stdlib(target):
+  label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule')
+  label.srcs += [re.sub('^//', '', x) for x in sorted(target.inputs)]
+  label.outs += target.outputs
+  label.cmd = (r'$(location gen_amalgamated_sql_py) '
+  r'--type=LIB --cpp_out=$@ $(SRCS)')
+  label.exec_tools += [':gen_amalgamated_sql_py']
   return [label]
 
 
@@ -152,6 +165,8 @@
         gen_version_header,
     '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics':
         gen_amalgamated_sql_metrics,
+    '//src/trace_processor/stdlib:gen_amalgamated_stdlib':
+        gen_amalgamated_stdlib,
 }
 
 # ------------------------------------------------------------------------------
diff --git a/tools/test_gen_amalgamated.py b/tools/test_gen_amalgamated.py
index aa99bc4..9eb17ad 100755
--- a/tools/test_gen_amalgamated.py
+++ b/tools/test_gen_amalgamated.py
@@ -52,7 +52,7 @@
 
 def check_amalgamated_build():
   args = [
-      '-std=c++11', '-Werror', '-Wall', '-Wextra',
+      '-std=c++17', '-Werror', '-Wall', '-Wextra',
       '-DPERFETTO_AMALGAMATED_SDK_TEST', '-I' + OUT_DIR,
       OUT_DIR + '/perfetto.cc', 'test/client_api_example.cc', '-o',
       OUT_DIR + '/test'
diff --git a/ui/src/common/query_result.ts b/ui/src/common/query_result.ts
index 347c4ed..bae847d 100644
--- a/ui/src/common/query_result.ts
+++ b/ui/src/common/query_result.ts
@@ -183,6 +183,12 @@
   // have been fetched. The promise return value is always the object iself.
   waitAllRows(): Promise<QueryResult>;
 
+  // Returns a promise that is resolved when either:
+  // - more rows are available
+  // - all rows are available
+  // The promise return value is always the object iself.
+  waitMoreRows(): Promise<QueryResult>;
+
   // Can return an empty array if called before the first batch is resolved.
   // This should be called only after having awaited for at least one batch.
   columns(): string[];
@@ -194,10 +200,6 @@
   // Returns the number of SQL statement that produced output rows. This number
   // is <= statementCount().
   statementWithOutputCount(): number;
-
-  // TODO(primiano): next CLs will introduce a waitMoreRows() to allow tracks
-  // to await until some more data (but not necessarily all) is available. For
-  // now everything uses waitAllRows().
 }
 
 // Interface exposed to engine.ts to pump in the data as new row batches arrive.
@@ -248,6 +250,10 @@
   // last result batch has been been retrieved.
   private allRowsPromise?: Deferred<QueryResult>;
 
+  // Promise awaiting on waitMoreRows(). This resolved when the next
+  // batch is appended via appendResultBatch.
+  private moreRowsPromise?: Deferred<QueryResult>;
+
   isComplete(): boolean {
     return this._isComplete;
   }
@@ -288,6 +294,20 @@
     return this.allRowsPromise;
   }
 
+  waitMoreRows(): Promise<QueryResult> {
+    if (this.moreRowsPromise !== undefined) {
+      return this.moreRowsPromise;
+    }
+
+    const moreRowsPromise = defer<QueryResult>();
+    if (this._isComplete) {
+      this.resolveOrReject(moreRowsPromise, this);
+    } else {
+      this.moreRowsPromise = moreRowsPromise;
+    }
+    return moreRowsPromise;
+  }
+
   // --- WritableQueryResult implementation.
 
   // Called by the engine when a new QueryResult is available. Note that a
@@ -369,6 +389,11 @@
       }  // switch (tag)
     }    // while (pos < end)
 
+    if (this.moreRowsPromise !== undefined) {
+      this.resolveOrReject(this.moreRowsPromise, this);
+      this.moreRowsPromise = undefined;
+    }
+
     if (this._isComplete && this.allRowsPromise !== undefined) {
       this.resolveOrReject(this.allRowsPromise, this);
     }
@@ -768,6 +793,9 @@
   waitAllRows() {
     return this.impl.waitAllRows();
   }
+  waitMoreRows() {
+    return this.impl.waitMoreRows();
+  }
   isComplete() {
     return this.impl.isComplete();
   }
diff --git a/ui/src/common/query_result_unittest.ts b/ui/src/common/query_result_unittest.ts
index 5fad6fe..8f508e7 100644
--- a/ui/src/common/query_result_unittest.ts
+++ b/ui/src/common/query_result_unittest.ts
@@ -300,3 +300,45 @@
   }
   expect(() => qr.iter({x_3: NUM})).toThrowError(/\bx_3\b.*not found/);
 });
+
+
+test('QueryResult.WaitMoreRows', async () => {
+  const batchA = QueryResultProto.CellsBatch.create({
+    cells: [T.CELL_VARINT],
+    varintCells: [42],
+    isLastBatch: false,
+  });
+  const resProtoA = QueryResultProto.create({
+    columnNames: ['a_int'],
+    batch: [batchA],
+  });
+
+  const qr = createQueryResult({query: 'Some query'});
+  qr.appendResultBatch(QueryResultProto.encode(resProtoA).finish());
+
+  const batchB = QueryResultProto.CellsBatch.create({
+    cells: [T.CELL_VARINT],
+    varintCells: [43],
+    isLastBatch: true,
+  });
+  const resProtoB = QueryResultProto.create({
+    columnNames: [],
+    batch: [batchB],
+  });
+
+  const waitPromise = qr.waitMoreRows();
+  const appendPromise = new Promise<void>((resolve, _) => {
+    setTimeout(() => {
+      qr.appendResultBatch(QueryResultProto.encode(resProtoB).finish());
+      resolve();
+    }, 0);
+  });
+
+  expect(qr.isComplete()).toBe(false);
+  expect(qr.numRows()).toBe(1);
+
+  await Promise.all([waitPromise, appendPromise]);
+
+  expect(qr.isComplete()).toBe(true);
+  expect(qr.numRows()).toBe(2);
+});
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts
index 7ed2880..3242b2c 100644
--- a/ui/src/frontend/help_modal.ts
+++ b/ui/src/frontend/help_modal.ts
@@ -48,6 +48,20 @@
                 m('td', 'Pan left/right'),
                 ),
             ),
+        m('h2', 'Navigation (Dvorak)'),
+        m(
+            'table',
+            m(
+                'tr',
+                m('td', keycap(','), '/', keycap('o')),
+                m('td', 'Zoom in/out'),
+                ),
+            m(
+                'tr',
+                m('td', keycap('a'), '/', keycap('e')),
+                m('td', 'Pan left/right'),
+                ),
+            ),
         m('h2', 'Mouse Controls'),
         m('table',
           m('tr', m('td', 'Click'), m('td', 'Select event')),