Merge "docs: Add quick start contributing to perfetto"
diff --git a/BUILD b/BUILD
index 8b80788..567b22c 100644
--- a/BUILD
+++ b/BUILD
@@ -591,6 +591,7 @@
     name = "include_perfetto_trace_processor_trace_processor",
     srcs = [
         "include/perfetto/trace_processor/iterator.h",
+        "include/perfetto/trace_processor/metatrace_config.h",
         "include/perfetto/trace_processor/read_trace.h",
         "include/perfetto/trace_processor/ref_counted.h",
         "include/perfetto/trace_processor/trace_processor.h",
@@ -1324,9 +1325,9 @@
     ],
 )
 
-# GN target: //src/trace_processor/metrics/sql/chrome:chrome
+# GN target: //src/trace_processor/metrics/sql/chrome:chrome_sql
 perfetto_filegroup(
-    name = "src_trace_processor_metrics_sql_chrome_chrome",
+    name = "src_trace_processor_metrics_sql_chrome_chrome_sql",
     srcs = [
         "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
@@ -1410,7 +1411,7 @@
     name = "src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
     deps = [
         ":src_trace_processor_metrics_sql_android_android",
-        ":src_trace_processor_metrics_sql_chrome_chrome",
+        ":src_trace_processor_metrics_sql_chrome_chrome_sql",
         ":src_trace_processor_metrics_sql_common_common",
         ":src_trace_processor_metrics_sql_experimental_experimental",
         ":src_trace_processor_metrics_sql_misc_sql",
diff --git a/OWNERS b/OWNERS
index 0da623c..dee7d70 100644
--- a/OWNERS
+++ b/OWNERS
@@ -21,6 +21,9 @@
 nuskos@google.com
 oysteine@google.com
 
+# UI, Chromium-related metrics and simpler trace processor changes.
+altimin@google.com
+
 # Most Android-related metrics.
 ilkos@google.com
 
diff --git a/docs/README.md b/docs/README.md
index 1ce43f8..0dac083 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -49,7 +49,7 @@
   memory to call-stacks, based on out-of-process unwinding, configurable
   sampling, attachable to already running processes.
 
-* [Java heap profiling](/docs/data-sources/java-heap-profiler.md): an
+* Capturing [Java heap dumps](/docs/data-sources/java-heap-profiler.md) with an
   out-of-process profiler tightly integrated with the Android RunTime that
   allows to get full snapshots of the managed heap retention graph (types,
   field names, retained size and references to other objects) without, however,
diff --git a/docs/case-studies/memory.md b/docs/case-studies/memory.md
index de4e401..b55bdce 100644
--- a/docs/case-studies/memory.md
+++ b/docs/case-studies/memory.md
@@ -307,7 +307,7 @@
 **Native Heap Profiles require Android 10.**
 
 NOTE: For detailed instructions about the native heap profiler and
-      troubleshooting see the [Data sources > Native heap profiler](
+      troubleshooting see the [Data sources > Heap profiler](
       /docs/data-sources/native-heap-profiler.md) page.
 
 Applications usually get memory through `malloc` or C++'s `new` rather than
@@ -353,42 +353,40 @@
 
 The tabs that are available are
 
-* **Unreleased size**: how many bytes were allocated but not freed at this
-  callstack the moment the dump was created.
-* **Total size**: how many bytes were allocated (including ones freed at the
-  moment of the dump) at this callstack
-* **Unreleased count**: how many allocations without matching frees were done at
-  this callstack.
-* **Total count**: how many allocations (including ones with matching frees)
-  were done at this callstack.
+* **Unreleased malloc size**: how many bytes were allocated but not freed at
+  this callstack the moment the dump was created.
+* **Total malloc size**: how many bytes were allocated (including ones freed at
+  the moment of the dump) at this callstack.
+* **Unreleased malloc count**: how many allocations without matching frees were
+  done at this callstack.
+* **Total malloc count**: how many allocations (including ones with matching
+  frees) were done at this callstack.
 
 The default view will show you all allocations that were done while the
 profile was running but that weren't freed (the **space** tab).
 
-![Native Flamegraph](/docs/images/syssrv-apk-assets-two.png)
+![Native Flamegraph](/docs/images/native-heap-prof.png)
 
 We can see that a lot of memory gets allocated in paths through
-`ResourceManager.loadApkAssets`. To get the total memory that was allocated
-this way, we can enter "loadApkAssets" into the Focus textbox. This will only
-show callstacks where some frame matches "loadApkAssets".
+`AssetManager.applyStyle`. To get the total memory that was allocated
+this way, we can enter "applyStyle" into the Focus textbox. This will only
+show callstacks where some frame matches "applyStyle".
 
-![Native Flamegraph with Focus](/docs/images/syssrv-apk-assets-focus.png)
+![Native Flamegraph with Focus](/docs/images/native-heap-prof-focus.png)
 
 From this we have a clear idea where in the code we have to look. From the
 code we can see how that memory is being used and if we actually need all of
-it. In this case the key is the `_CompressedAsset` that requires decompressing
-into RAM rather than being able to (_cleanly_) memory-map. By not compressing
-these data, we can save RAM.
+it.
 
 ## {#java-hprof} Analyzing the Java Heap
 
 **Java Heap Dumps require Android 11.**
 
-NOTE: For detailed instructions about the Java heap profiler and
-      troubleshooting see the [Data sources > Java heap profiler](
+NOTE: For detailed instructions about capturing Java heap dumps and
+      troubleshooting see the [Data sources > Java heap dumps](
       /docs/data-sources/java-heap-profiler.md) page.
 
-### {#capture-profile-java} Capturing the profile
+### {#capture-profile-java} Dumping the java heap
 We can get a snapshot of the graph of all the Java objects that constitute the
 Java heap. We use the `tools/java_heap_dump` script. If you are having trouble
 make sure you are using the [latest version](
@@ -415,12 +413,12 @@
 and is generally the highest-signal. The rightmost `[merged]` stacks is the
 sum of all objects that are too small to be displayed.
 
-![Java Flamegraph](/docs/images/java-flamegraph.png)
+![Java Flamegraph](/docs/images/java-heap-graph.png)
 
 The tabs that are available are
 
-* **space**: how many bytes are retained via this path to the GC root.
-* **objects**: how many objects are retained via this path to the GC root.
+* **Size**: how many bytes are retained via this path to the GC root.
+* **Objects**: how many objects are retained via this path to the GC root.
 
 If we want to only see callstacks that have a frame that contains some string,
 we can use the Focus feature. If we want to know all allocations that have to
@@ -430,7 +428,7 @@
 graph, we can filter by the names of the classes. If we wanted to see everything
 that could be caused by notifications, we can put "notification" in the Focus box.
 
-![Java Flamegraph with Focus](/docs/images/java-flamegraph-focus.png)
+![Java Flamegraph with Focus](/docs/images/java-heap-graph-focus.png)
 
 We aggregate the paths per class name, so if there are multiple objects of the
 same type retained by a `java.lang.Object[]`, we will show one element as its
diff --git a/docs/data-sources/java-heap-profiler.md b/docs/data-sources/java-heap-profiler.md
index f67cf03..e3d5921 100644
--- a/docs/data-sources/java-heap-profiler.md
+++ b/docs/data-sources/java-heap-profiler.md
@@ -1,15 +1,17 @@
-# Memory: Java heap profiler
+# Memory: Java heap dumps
 
-NOTE: The Java heap profiler requires Android 11 or higher
+NOTE: Capturing Java heap dumps requires Android 11 or higher
 
 See the [Memory Guide](/docs/case-studies/memory.md#java-hprof) for getting
-started with Java heap profiling.
+started with Java heap dumps.
 
-Conversely from the [Native heap profiler](native-heap-profiler.md), the Java
-heap profiler reports full retention graphs of managed objects but not
-call-stacks. The information recorded by the Java heap profiler is of the form:
-_Object X retains object Y, which is N bytes large, through its class member
-named Z_.
+Conversely from [Native heap profiles](native-heap-profiler.md), Java heap dumps
+report full retention graphs of managed objects but not call-stacks. The
+information recorded in a Java heap dump is of the form: _Object X retains
+object Y, which is N bytes large, through its class member named Z_.
+
+Java heap dumps are not to be confused with profiles taken by the
+[Java heap sampler](native-heap-profiler.md#java-heap-sampling)
 
 ## UI
 
@@ -17,13 +19,13 @@
 diamond in the _"Heap Profile"_ track of a process. Each diamond corresponds to
 a heap dump.
 
-![Java heap profiles in the process tracks](/docs/images/profile-diamond.png)
+![Java heap dumps in the process tracks](/docs/images/profile-diamond.png)
 
-![Flamegraph of a Java heap profiler](/docs/images/java-flamegraph.png)
+![Flamegraph of a Java heap dump](/docs/images/java-heap-graph.png)
 
 The native size of certain objects is represented as an extra child node in the
 flamegraph, prefixed with "[native]". The extra node counts as an extra object.
-This is available only on Android T+.
+This is available only on Android 13 or higher.
 
 ## SQL
 
@@ -93,7 +95,7 @@
 
 ## TraceConfig
 
-The Java heap profiler is configured through the
+The Java heap dump data source is configured through the
 [JavaHprofConfig](/docs/reference/trace-config-proto.autogen#JavaHprofConfig)
 section of the trace config.
 
diff --git a/docs/data-sources/native-heap-profiler.md b/docs/data-sources/native-heap-profiler.md
index f051124..1859295 100644
--- a/docs/data-sources/native-heap-profiler.md
+++ b/docs/data-sources/native-heap-profiler.md
@@ -1,13 +1,17 @@
-# Native heap profiler
+# Heap profiler
 
 NOTE: **heapprofd requires Android 10 or higher**
 
-Heapprofd is a tool that tracks native heap allocations & deallocations of an
-Android process within a given time period. The resulting profile can be used to
+Heapprofd is a tool that tracks heap allocations & deallocations of an Android
+process within a given time period. The resulting profile can be used to
 attribute memory usage to particular call-stacks, supporting a mix of both
 native and java code. The tool can be used by Android platform and app
 developers to investigate memory issues.
 
+By default, the tool records native allocations and deallocations done with
+malloc/free (or new/delete). It can be configured to record java heap memory
+allocations instead: see [Java heap sampling](#java-heap-sampling) below.
+
 On debug Android builds, you can profile all apps and most system services.
 On "user" builds, you can only use it on apps with the debuggable or
 profileable manifest flag.
@@ -25,7 +29,7 @@
 
 ![heapprofd snapshots in the UI tracks](/docs/images/profile-diamond.png)
 
-![heapprofd flamegraph](/docs/images/native-flamegraph.png)
+![heapprofd flamegraph](/docs/images/native-heap-prof.png)
 
 ## SQL
 
@@ -80,14 +84,14 @@
 
 The resulting profile proto contains four views on the data
 
-* **Unreleased size**: how many bytes were allocated but not freed at this
-  callstack the moment the dump was created.
-* **Total size**: how many bytes were allocated (including ones freed at the
-  moment of the dump) at this callstack
-* **Unreleased count**: how many allocations without matching frees were done at
-  this callstack.
-* **Total count**: how many allocations (including ones with matching frees)
-  were done at this callstack.
+* **Unreleased malloc size**: how many bytes were allocated but not freed at
+  this callstack the moment the dump was created.
+* **Total malloc size**: how many bytes were allocated (including ones freed at
+  the moment of the dump) at this callstack.
+* **Unreleased malloc count**: how many allocations without matching frees were
+  done at this callstack.
+* **Total malloc count**: how many allocations (including ones with matching
+  frees) were done at this callstack.
 
 _(Googlers: You can also open the gzipped protos using http://pprof/)_
 
@@ -217,6 +221,41 @@
 </manifest>
 ```
 
+## {#java-heap-sampling} Java heap sampling
+
+NOTE: **Java heap sampling is available on Android 12 or higher**
+
+NOTE: **Java heap sampling is not to be confused with [Java heap
+dumps](/docs/data-sources/java-heap-profiler.md)**
+
+Heapprofd can be configured to track Java allocations instead of native one.
+* By setting adding `heaps: "com.android.art"` in
+  [HeapprofdConfig](/docs/reference/trace-config-proto.autogen#HeapprofdConfig).
+* By adding `--heaps com.android.art` to the invocation of
+  [`tools/heap_profile`](/docs/reference/heap_profile-cli).
+
+Unlike java heap dumps (which show the retention graph of a snapshot of the live
+objects) but like native heap profiles, java heap samples show callstacks of
+allocations over time of the entire profile.
+
+Java heap samples only show callstacks of when objects are created, not when
+they're deleted or garbage collected.
+
+![javaheapsamples](/docs/images/java-heap-samples.png)
+
+The resulting profile proto contains two views on the data:
+
+* **Total allocation size**: how many bytes were allocated at this callstack
+  over time of the profile until this point. The bytes might have been freed or
+  not, the tool does not keep track of that.
+* **Total allocation count**: how many object were allocated at this callstack
+  over time of the profile until this point. The objects might have been freed
+  or not, the tool does not keep track of that.
+
+Java heap samples are useful to understand memory churn showing the call stack
+of which parts of the code large allocations are attributed to as well as the
+allocation type from the ART runtime.
+
 ## DEDUPED frames
 
 If the name of a Java method includes `[DEDUPED]`, this means that multiple
diff --git a/docs/design-docs/heapprofd-sampling.md b/docs/design-docs/heapprofd-sampling.md
index 3ebfb3d..9f238b5 100644
--- a/docs/design-docs/heapprofd-sampling.md
+++ b/docs/design-docs/heapprofd-sampling.md
@@ -1,6 +1,6 @@
 # heapprofd: Sampling for Memory Profiles
 
-_tomlewin, fmayer **·** 2021-04-14_  
+_tomlewin, fmayer **·** 2021-04-14_
 
 ## Background
 
@@ -173,7 +173,7 @@
 
 [algorithm]:
   https://cs.android.com/android/platform/superproject/+/master:external/perfetto/src/profiling/memory/sampler.h
-[example visualization]: /docs/images/syssrv-apk-assets-two.png
+[example visualization]: /docs/images/native-heap-prof.png
 [Android Heap Profiler]: /docs/design-docs/heapprofd-design
 [mean absolute percentage error]: https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
-[full results]: https://gist.github.com/fmayer/3aafcaf58f8db09714ba09aa4ac2b1ac
\ No newline at end of file
+[full results]: https://gist.github.com/fmayer/3aafcaf58f8db09714ba09aa4ac2b1ac
diff --git a/docs/images/java-flamegraph-focus.png b/docs/images/java-flamegraph-focus.png
deleted file mode 100644
index d75648f..0000000
--- a/docs/images/java-flamegraph-focus.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/java-flamegraph.png b/docs/images/java-flamegraph.png
deleted file mode 100644
index fa58843..0000000
--- a/docs/images/java-flamegraph.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/java-heap-graph-focus.png b/docs/images/java-heap-graph-focus.png
new file mode 100644
index 0000000..cd5d232
--- /dev/null
+++ b/docs/images/java-heap-graph-focus.png
Binary files differ
diff --git a/docs/images/java-heap-graph.png b/docs/images/java-heap-graph.png
new file mode 100644
index 0000000..829c7fe
--- /dev/null
+++ b/docs/images/java-heap-graph.png
Binary files differ
diff --git a/docs/images/java-heap-samples.png b/docs/images/java-heap-samples.png
new file mode 100644
index 0000000..7bd4307
--- /dev/null
+++ b/docs/images/java-heap-samples.png
Binary files differ
diff --git a/docs/images/native-flamegraph.png b/docs/images/native-flamegraph.png
deleted file mode 100644
index 88b92e3..0000000
--- a/docs/images/native-flamegraph.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/native-heap-prof-focus.png b/docs/images/native-heap-prof-focus.png
new file mode 100644
index 0000000..acb3da6
--- /dev/null
+++ b/docs/images/native-heap-prof-focus.png
Binary files differ
diff --git a/docs/images/native-heap-prof.png b/docs/images/native-heap-prof.png
new file mode 100644
index 0000000..5b7f9fa
--- /dev/null
+++ b/docs/images/native-heap-prof.png
Binary files differ
diff --git a/docs/images/profile-diamond.png b/docs/images/profile-diamond.png
index 5f14756..8c12fbc 100644
--- a/docs/images/profile-diamond.png
+++ b/docs/images/profile-diamond.png
Binary files differ
diff --git a/docs/images/syssrv-apk-assets-focus.png b/docs/images/syssrv-apk-assets-focus.png
deleted file mode 100644
index c5070dd..0000000
--- a/docs/images/syssrv-apk-assets-focus.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/syssrv-apk-assets-two.png b/docs/images/syssrv-apk-assets-two.png
deleted file mode 100644
index dea36ef..0000000
--- a/docs/images/syssrv-apk-assets-two.png
+++ /dev/null
Binary files differ
diff --git a/docs/quickstart/heap-profiling.md b/docs/quickstart/heap-profiling.md
index 6f9692f..3a58f20 100644
--- a/docs/quickstart/heap-profiling.md
+++ b/docs/quickstart/heap-profiling.md
@@ -60,7 +60,7 @@
 script. Then start the profile:
 
 ```bash
-python /path/to/heap_profile -n system_server 
+python /path/to/heap_profile -n system_server
 ```
 
 ## View profile
@@ -70,7 +70,7 @@
 _"Heap profile"_.
 
 ![Profile Diamond](/docs/images/profile-diamond.png)
-![Native Flamegraph](/docs/images/syssrv-apk-assets-two.png)
+![Native Flamegraph](/docs/images/native-heap-prof.png)
 
 ## Next steps
 
diff --git a/docs/toc.md b/docs/toc.md
index f9e010e..dc3c717 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -18,8 +18,8 @@
 * [Data sources](#)
   * [Memory](#)
     * [Counters and events](data-sources/memory-counters.md)
-    * [Native heap profiler](data-sources/native-heap-profiler.md)
-    * [Java heap profiler](data-sources/java-heap-profiler.md)
+    * [Heap profiler](data-sources/native-heap-profiler.md)
+    * [Java heap dumps](data-sources/java-heap-profiler.md)
   * [CPU](#)
     * [Scheduling events](data-sources/cpu-scheduling.md)
     * [System calls](data-sources/syscalls.md)
diff --git a/gn/perfetto_sql.gni b/gn/perfetto_sql.gni
index d6bae3c..dadbdd8 100644
--- a/gn/perfetto_sql.gni
+++ b/gn/perfetto_sql.gni
@@ -30,6 +30,7 @@
 
 template("perfetto_amalgamated_sql_header") {
   gen_txt_file = "$target_gen_dir/${target_name}.txt"
+  gen_depfile = "$target_gen_dir/${target_name}.d"
 
   generated_file("${target_name}_generated_file") {
     forward_variables_from(invoker, [ "deps" ])
@@ -49,6 +50,7 @@
     deps = [ ":${target_name}_generated_file" ]
     deps += invoker.deps
 
+    depfile = gen_depfile
     script = "$perfetto_root_path/tools/gen_amalgamated_sql.py"
     generated_header = "${target_gen_dir}/" + invoker.generated_header
     args = [
@@ -60,6 +62,8 @@
       rebase_path(generated_header, root_build_dir),
       "--input-list-file",
       rebase_path(gen_txt_file, root_build_dir),
+      "--depfile",
+      rebase_path(gen_depfile, root_build_dir),
     ]
     inputs = [ gen_txt_file ]
     outputs = [ generated_header ]
diff --git a/include/perfetto/trace_processor/BUILD.gn b/include/perfetto/trace_processor/BUILD.gn
index e6e0131..d0dae7f 100644
--- a/include/perfetto/trace_processor/BUILD.gn
+++ b/include/perfetto/trace_processor/BUILD.gn
@@ -15,6 +15,7 @@
 source_set("trace_processor") {
   sources = [
     "iterator.h",
+    "metatrace_config.h",
     "read_trace.h",
     "ref_counted.h",
     "trace_processor.h",
diff --git a/include/perfetto/trace_processor/metatrace_config.h b/include/perfetto/trace_processor/metatrace_config.h
new file mode 100644
index 0000000..6ffe4f0
--- /dev/null
+++ b/include/perfetto/trace_processor/metatrace_config.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDE_PERFETTO_TRACE_PROCESSOR_METATRACE_CONFIG_H_
+#define INCLUDE_PERFETTO_TRACE_PROCESSOR_METATRACE_CONFIG_H_
+
+#include <cstddef>
+
+namespace perfetto {
+namespace trace_processor {
+namespace metatrace {
+
+enum MetatraceCategories {
+  TOPLEVEL = 1 << 0,
+  QUERY = 1 << 1,
+  FUNCTION = 1 << 2,
+
+  NONE = 0,
+  ALL = TOPLEVEL | QUERY | FUNCTION,
+};
+
+struct MetatraceConfig {
+  MetatraceConfig();
+
+  MetatraceCategories categories = MetatraceCategories::ALL;
+  // Requested buffer size. The implemenation may choose to allocate a larger
+  // buffer size for efficiency.
+  size_t override_buffer_size = 0;
+};
+
+}  // namespace metatrace
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACE_PROCESSOR_METATRACE_CONFIG_H_
diff --git a/include/perfetto/trace_processor/trace_processor.h b/include/perfetto/trace_processor/trace_processor.h
index adcbca1..ff87e36 100644
--- a/include/perfetto/trace_processor/trace_processor.h
+++ b/include/perfetto/trace_processor/trace_processor.h
@@ -24,6 +24,7 @@
 #include "perfetto/base/export.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/iterator.h"
+#include "perfetto/trace_processor/metatrace_config.h"
 #include "perfetto/trace_processor/status.h"
 #include "perfetto/trace_processor/trace_processor_storage.h"
 
@@ -111,19 +112,8 @@
   // Metatracing involves tracing trace processor itself to root-cause
   // performace issues in trace processor. See |DisableAndReadMetatrace| for
   // more information on the format of the metatrace.
-  enum MetatraceCategories {
-    TOPLEVEL = 1 << 0,
-    QUERY = 1 << 1,
-    FUNCTION = 1 << 2,
-
-    NONE = 0,
-    ALL = TOPLEVEL | QUERY | FUNCTION,
-  };
-  struct MetatraceConfig {
-    MetatraceConfig();
-
-    MetatraceCategories categories = MetatraceCategories::ALL;
-  };
+  using MetatraceConfig = metatrace::MetatraceConfig;
+  using MetatraceCategories = metatrace::MetatraceCategories;
   virtual void EnableMetatrace(MetatraceConfig config = {}) = 0;
 
   // Disables "meta-tracing" of trace processor and writes the trace as a
diff --git a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
index df33b13..56caf9f 100644
--- a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+++ b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
@@ -27,11 +27,19 @@
 
     // For trace processor metatracing.
     string event_name = 8;
+    uint64 event_name_iid = 11;
+
     string counter_name = 9;
   }
   message Arg {
-    optional string key = 1;
-    optional string value = 2;
+    oneof key_or_interned_key {
+      string key = 1;
+      uint64 key_iid = 3;
+    }
+    oneof value_or_interned_value {
+      string value = 2;
+      uint64 value_iid = 4;
+    }
   }
 
   // Only when using |event_id|.
@@ -49,4 +57,12 @@
 
   // Args for the event.
   repeated Arg args = 7;
+
+  // Interned strings corresponding to the |event_name_iid|, |key_iid| and
+  // |value_iid| above.
+  message InternedString {
+    optional uint64 iid = 1;
+    optional string value = 2;
+  };
+  repeated InternedString interned_strings = 10;
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 251c12b..4b7c2a5 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -9812,11 +9812,19 @@
 
     // For trace processor metatracing.
     string event_name = 8;
+    uint64 event_name_iid = 11;
+
     string counter_name = 9;
   }
   message Arg {
-    optional string key = 1;
-    optional string value = 2;
+    oneof key_or_interned_key {
+      string key = 1;
+      uint64 key_iid = 3;
+    }
+    oneof value_or_interned_value {
+      string value = 2;
+      uint64 value_iid = 4;
+    }
   }
 
   // Only when using |event_id|.
@@ -9834,6 +9842,14 @@
 
   // Args for the event.
   repeated Arg args = 7;
+
+  // Interned strings corresponding to the |event_name_iid|, |key_iid| and
+  // |value_iid| above.
+  message InternedString {
+    optional uint64 iid = 1;
+    optional string value = 2;
+  };
+  repeated InternedString interned_strings = 10;
 }
 
 // End of protos/perfetto/trace/perfetto/perfetto_metatrace.proto
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index d815d04..ba96b2a 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -51,6 +51,7 @@
   deps = [
     "../../gn:default_deps",
     "../../include/perfetto/ext/base",
+    "../../include/perfetto/trace_processor",
     "../../protos/perfetto/trace_processor:zero",
   ]
 }
diff --git a/src/trace_processor/dynamic/descendant_generator.cc b/src/trace_processor/dynamic/descendant_generator.cc
index dee6afa..45061d5 100644
--- a/src/trace_processor/dynamic/descendant_generator.cc
+++ b/src/trace_processor/dynamic/descendant_generator.cc
@@ -71,21 +71,22 @@
                            static_cast<uint32_t>(starting_id.value));
   }
 
-  // All nested descendents must be on the same track, with a ts greater than
-  // |start_ref.ts| and whose depth is larger than |start_ref|'s.
-  std::vector<Constraint> cs({slices.ts().ge(start_ref->ts()),
-                              slices.track_id().eq(start_ref->track_id().value),
-                              slices.depth().gt(start_ref->depth())});
-
   // As an optimization, for any finished slices, we only need to consider
   // slices which started before the end of this slice (because slices on a
   // track are always perfectly stacked).
   // For unfinshed slices (i.e. -1 dur), we need to consider until the end of
   // the trace so we cannot add any similar constraint.
+  std::vector<Constraint> cs;
   if (start_ref->dur() >= 0) {
     cs.emplace_back(slices.ts().le(start_ref->ts() + start_ref->dur()));
   }
 
+  // All nested descendents must be on the same track, with a ts greater than
+  // |start_ref.ts| and whose depth is larger than |start_ref|'s.
+  cs.emplace_back(slices.ts().ge(start_ref->ts()));
+  cs.emplace_back(slices.track_id().eq(start_ref->track_id().value));
+  cs.emplace_back(slices.depth().gt(start_ref->depth()));
+
   // It's important we insert directly into |row_numbers_accumulator| and not
   // overwrite it because we expect the existing elements in
   // |row_numbers_accumulator| to be preserved.
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 4a8b49e..f348af8 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -63,7 +63,9 @@
       raw_chrome_legacy_system_trace_event_id_(
           context->storage->InternString("chrome_event.legacy_system_trace")),
       raw_chrome_legacy_user_trace_event_id_(
-          context->storage->InternString("chrome_event.legacy_user_trace")) {}
+          context->storage->InternString("chrome_event.legacy_user_trace")),
+      missing_metatrace_interned_string_id_(
+          context->storage->InternString("MISSING STRING")) {}
 
 ProtoTraceParser::~ProtoTraceParser() = default;
 
@@ -317,6 +319,14 @@
   StringId name_id = kNullStringId;
   char fallback[64];
 
+  for (auto it = event.interned_strings(); it; ++it) {
+    protos::pbzero::PerfettoMetatrace::InternedString::Decoder interned_string(
+        it->data(), it->size());
+    metatrace_interned_strings_.Insert(
+        interned_string.iid(),
+        context_->storage->InternString(interned_string.value()));
+  }
+
   // This function inserts the args from the proto into the args table.
   // Args inserted with the same key multiple times are treated as an array:
   // this function correctly creates the key and flat key for each arg array.
@@ -327,8 +337,18 @@
     std::vector<Arg> interned;
     for (auto it = event.args(); it; ++it) {
       protos::pbzero::PerfettoMetatrace::Arg::Decoder arg_proto(*it);
-      StringId key = context_->storage->InternString(arg_proto.key());
-      StringId value = context_->storage->InternString(arg_proto.value());
+      StringId key;
+      if (arg_proto.has_key_iid()) {
+        key = GetMetatraceInternedString(arg_proto.key_iid());
+      } else {
+        key = context_->storage->InternString(arg_proto.key());
+      }
+      StringId value;
+      if (arg_proto.has_value_iid()) {
+        value = GetMetatraceInternedString(arg_proto.value_iid());
+      } else {
+        value = context_->storage->InternString(arg_proto.value());
+      }
       interned.emplace_back(key, value);
     }
 
@@ -373,7 +393,8 @@
     }
   };
 
-  if (event.has_event_id() || event.has_event_name()) {
+  if (event.has_event_id() || event.has_event_name() ||
+      event.has_event_name_iid()) {
     if (event.has_event_id()) {
       auto eid = event.event_id();
       if (eid < metatrace::EVENTS_MAX) {
@@ -382,6 +403,8 @@
         sprintf(fallback, "Event %d", eid);
         name_id = context_->storage->InternString(fallback);
       }
+    } else if (event.has_event_name_iid()) {
+      name_id = GetMetatraceInternedString(event.event_name_iid());
     } else {
       name_id = context_->storage->InternString(event.event_name());
     }
@@ -454,5 +477,12 @@
                                           Variadic::String(id));
 }
 
+StringId ProtoTraceParser::GetMetatraceInternedString(uint64_t iid) {
+  StringId* maybe_id = metatrace_interned_strings_.Find(iid);
+  if (!maybe_id)
+    return missing_metatrace_interned_string_id_;
+  return *maybe_id;
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.h b/src/trace_processor/importers/proto/proto_trace_parser.h
index c3b570b..00b3f48 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.h
+++ b/src/trace_processor/importers/proto/proto_trace_parser.h
@@ -67,6 +67,8 @@
   void ParseTraceConfig(ConstBytes);
 
  private:
+  StringId GetMetatraceInternedString(uint64_t iid);
+
   TraceProcessorContext* context_;
 
   const StringId metatrace_id_;
@@ -74,6 +76,9 @@
   const StringId raw_chrome_metadata_event_id_;
   const StringId raw_chrome_legacy_system_trace_event_id_;
   const StringId raw_chrome_legacy_user_trace_event_id_;
+  const StringId missing_metatrace_interned_string_id_;
+
+  base::FlatHashMap<uint64_t, StringId> metatrace_interned_strings_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index c95518c..337b17c 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -28,7 +28,7 @@
   deps = [
     ":misc_sql",
     "android",
-    "chrome",
+    "chrome:chrome_sql",
     "common",
     "experimental",
     "webview",
diff --git a/src/trace_processor/metrics/sql/chrome/BUILD.gn b/src/trace_processor/metrics/sql/chrome/BUILD.gn
index 37cd21e..c382680 100644
--- a/src/trace_processor/metrics/sql/chrome/BUILD.gn
+++ b/src/trace_processor/metrics/sql/chrome/BUILD.gn
@@ -17,7 +17,9 @@
 
 assert(enable_perfetto_trace_processor_sqlite)
 
-perfetto_sql_source_set("chrome") {
+# This target cannot be named "chrome" because it breaks Chrome on iOS from
+# being built.
+perfetto_sql_source_set("chrome_sql") {
   sources = [
     "actual_power_by_category.sql",
     "actual_power_by_rail_mode.sql",
diff --git a/src/trace_processor/sqlite/sqlite_table.cc b/src/trace_processor/sqlite/sqlite_table.cc
index 2e63e59..7103442 100644
--- a/src/trace_processor/sqlite/sqlite_table.cc
+++ b/src/trace_processor/sqlite/sqlite_table.cc
@@ -258,6 +258,7 @@
                       r->AddArg("cache_hit", std::to_string(cache_hit));
                       r->AddArg("name", name_);
                       WriteQueryConstraintsToMetatrace(r, qc_cache_, schema_);
+                      r->AddArg("raw_constraints", idxStr);
                       r->AddArg("argc", std::to_string(argc));
                     });
 
diff --git a/src/trace_processor/tp_metatrace.cc b/src/trace_processor/tp_metatrace.cc
index 1169a14..79e6dbc 100644
--- a/src/trace_processor/tp_metatrace.cc
+++ b/src/trace_processor/tp_metatrace.cc
@@ -20,10 +20,34 @@
 namespace trace_processor {
 namespace metatrace {
 
+namespace {
+
+using ProtoEnum = protos::pbzero::MetatraceCategories;
+ProtoEnum MetatraceCategoriesToProtoEnum(MetatraceCategories categories) {
+  switch (categories) {
+    case MetatraceCategories::TOPLEVEL:
+      return ProtoEnum::TOPLEVEL;
+    case MetatraceCategories::FUNCTION:
+      return ProtoEnum::FUNCTION;
+    case MetatraceCategories::QUERY:
+      return ProtoEnum::QUERY;
+    case MetatraceCategories::ALL:
+      return ProtoEnum::ALL;
+    case MetatraceCategories::NONE:
+      return ProtoEnum::NONE;
+  }
+  return ProtoEnum::NONE;
+}
+
+}  // namespace
+
 Category g_enabled_categories = Category::NONE;
 
-void Enable(Category categories) {
-  g_enabled_categories = categories;
+void Enable(MetatraceConfig config) {
+  g_enabled_categories = MetatraceCategoriesToProtoEnum(config.categories);
+  if (config.override_buffer_size) {
+    RingBuffer::GetInstance()->Resize(config.override_buffer_size);
+  }
 }
 
 void DisableAndReadBuffer(std::function<void(Record*)> fn) {
@@ -33,19 +57,28 @@
   RingBuffer::GetInstance()->ReadAll(fn);
 }
 
-RingBuffer::RingBuffer() {
-  static_assert((kCapacity & (kCapacity - 1)) == 0,
+RingBuffer::RingBuffer() : data_(kDefaultCapacity) {
+  static_assert((kDefaultCapacity & (kDefaultCapacity - 1)) == 0,
                 "Capacity should be a power of 2");
 }
 
+void RingBuffer::Resize(size_t requested_capacity) {
+  size_t actual_capacity = 1;
+  while (actual_capacity < requested_capacity)
+    actual_capacity <<= 1;
+  data_.resize(actual_capacity);
+  start_idx_ = 0;
+  write_idx_ = 0;
+}
+
 void RingBuffer::ReadAll(std::function<void(Record*)> fn) {
   // Mark as reading so we don't get reentrancy in obtaining new
   // trace events.
   is_reading_ = true;
 
-  uint64_t start = (write_idx_ - start_idx_) < kCapacity
+  uint64_t start = (write_idx_ - start_idx_) < data_.size()
                        ? start_idx_
-                       : write_idx_ - kCapacity;
+                       : write_idx_ - data_.size();
   uint64_t end = write_idx_;
 
   // Increment the write index by kCapacity + 1. This ensures that if
@@ -54,7 +87,7 @@
   // This works because of the logic in ~ScopedEntry and
   // RingBuffer::HasOverwritten which ensures that we don't overwrite entries
   // more than kCapcity elements in the past.
-  write_idx_ += kCapacity + 1;
+  write_idx_ += data_.size() + 1;
 
   for (uint64_t i = start; i < end; ++i) {
     Record* record = At(i);
diff --git a/src/trace_processor/tp_metatrace.h b/src/trace_processor/tp_metatrace.h
index 9c2cd90..473fff6 100644
--- a/src/trace_processor/tp_metatrace.h
+++ b/src/trace_processor/tp_metatrace.h
@@ -25,6 +25,7 @@
 #include "perfetto/ext/base/metatrace_events.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/thread_checker.h"
+#include "perfetto/trace_processor/metatrace_config.h"
 #include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h"
 
 // Trace processor maintains its own base implementation to avoid the
@@ -97,7 +98,7 @@
 //     is enabled and read one-shot at the end of execution.
 class RingBuffer {
  public:
-  static constexpr uint32_t kCapacity = 256 * 1024;
+  static constexpr uint32_t kDefaultCapacity = 256 * 1024;
 
   RingBuffer();
   ~RingBuffer() = default;
@@ -115,7 +116,7 @@
     return std::make_pair(idx, record);
   }
 
-  Record* At(uint64_t idx) { return &data_[idx % kCapacity]; }
+  Record* At(uint64_t idx) { return &data_[idx % data_.size()]; }
 
   void ReadAll(std::function<void(Record*)>);
 
@@ -130,14 +131,19 @@
 
   // Returns whether the record at the |index| has been overwritten because
   // of wraps of the ring buffer.
-  bool HasOverwritten(uint64_t index) { return index + kCapacity < write_idx_; }
+  bool HasOverwritten(uint64_t index) {
+    return index + data_.size() < write_idx_;
+  }
+
+  // Request the ring buffer to be resized. Clears the existing buffer.
+  void Resize(size_t requested_capacity);
 
  private:
   bool is_reading_ = false;
 
   uint64_t start_idx_ = 0;
   uint64_t write_idx_ = 0;
-  std::array<Record, kCapacity> data_;
+  std::vector<Record> data_;
 
   PERFETTO_THREAD_CHECKER(thread_checker_)
 };
@@ -187,7 +193,7 @@
 };
 
 // Enables meta-tracing of trace-processor.
-void Enable(Category categories = Category::ALL);
+void Enable(MetatraceConfig config = {});
 
 // Disables meta-tracing of trace-processor and reads all records.
 void DisableAndReadBuffer(std::function<void(Record*)>);
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index dee5e80..3b4ecc4 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -1125,32 +1125,44 @@
   return pool_.SerializeAsDescriptorSet();
 }
 
+void TraceProcessorImpl::EnableMetatrace(MetatraceConfig config) {
+  metatrace::Enable(config);
+}
+
 namespace {
 
-using ProtoEnum = protos::pbzero::MetatraceCategories;
-ProtoEnum MetatraceCategoriesToProtoEnum(
-    TraceProcessor::MetatraceCategories categories) {
-  switch (categories) {
-    case TraceProcessor::TOPLEVEL:
-      return ProtoEnum::TOPLEVEL;
-    case TraceProcessor::FUNCTION:
-      return ProtoEnum::FUNCTION;
-    case TraceProcessor::QUERY:
-      return ProtoEnum::QUERY;
-    case TraceProcessor::ALL:
-      return ProtoEnum::ALL;
-    case TraceProcessor::NONE:
-      return ProtoEnum::NONE;
+class StringInterner {
+ public:
+  StringInterner(protos::pbzero::PerfettoMetatrace& event,
+                 base::FlatHashMap<std::string, uint64_t>& interned_strings)
+      : event_(event), interned_strings_(interned_strings) {}
+
+  ~StringInterner() {
+    for (const auto& interned_string : new_interned_strings_) {
+      auto* interned_string_proto = event_.add_interned_strings();
+      interned_string_proto->set_iid(interned_string.first);
+      interned_string_proto->set_value(interned_string.second);
+    }
   }
-  return ProtoEnum::NONE;
-}
+
+  uint64_t InternString(const std::string& str) {
+    uint64_t new_iid = interned_strings_.size();
+    auto insert_result = interned_strings_.Insert(str, new_iid);
+    if (insert_result.second) {
+      new_interned_strings_.emplace_back(new_iid, str);
+    }
+    return *insert_result.first;
+  }
+
+ private:
+  protos::pbzero::PerfettoMetatrace& event_;
+  base::FlatHashMap<std::string, uint64_t>& interned_strings_;
+
+  base::SmallVector<std::pair<uint64_t, std::string>, 16> new_interned_strings_;
+};
 
 }  // namespace
 
-void TraceProcessorImpl::EnableMetatrace(MetatraceConfig config) {
-  metatrace::Enable(MetatraceCategoriesToProtoEnum(config.categories));
-}
-
 base::Status TraceProcessorImpl::DisableAndReadMetatrace(
     std::vector<uint8_t>* trace_proto) {
   protozero::HeapBuffered<protos::pbzero::Trace> trace;
@@ -1175,11 +1187,16 @@
     }
   }
 
-  metatrace::DisableAndReadBuffer([&trace](metatrace::Record* record) {
+  base::FlatHashMap<std::string, uint64_t> interned_strings;
+  metatrace::DisableAndReadBuffer([&trace, &interned_strings](
+                                      metatrace::Record* record) {
     auto packet = trace->add_packet();
     packet->set_timestamp(record->timestamp_ns);
     auto* evt = packet->set_perfetto_metatrace();
-    evt->set_event_name(record->event_name);
+
+    StringInterner interner(*evt, interned_strings);
+
+    evt->set_event_name_iid(interner.InternString(record->event_name));
     evt->set_event_duration_ns(record->duration_ns);
     evt->set_thread_id(1);  // Not really important, just required for the ui.
 
@@ -1191,11 +1208,11 @@
         base::StringSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS);
     for (; s.Next();) {
       auto* arg_proto = evt->add_args();
-      arg_proto->set_key(s.cur_token());
+      arg_proto->set_key_iid(interner.InternString(s.cur_token()));
 
       bool has_next = s.Next();
       PERFETTO_CHECK(has_next);
-      arg_proto->set_value(s.cur_token());
+      arg_proto->set_value_iid(interner.InternString(s.cur_token()));
     }
   });
   *trace_proto = trace.SerializeAsArray();
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index caee3f7..3341279 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -652,6 +652,31 @@
   }
 };
 
+metatrace::MetatraceCategories ParseMetatraceCategories(std::string s) {
+  using Cat = metatrace::MetatraceCategories;
+  std::transform(s.begin(), s.end(), s.begin(),
+                 [](unsigned char c) { return std::tolower(c); });
+  base::StringSplitter splitter(s, ',');
+
+  Cat result = Cat::NONE;
+  for (; splitter.Next();) {
+    std::string cur = splitter.cur_token();
+    if (cur == "all" || cur == "*") {
+      result = Cat::ALL;
+    } else if (cur == "toplevel") {
+      result = static_cast<Cat>(result | Cat::TOPLEVEL);
+    } else if (cur == "function") {
+      result = static_cast<Cat>(result | Cat::FUNCTION);
+    } else if (cur == "query") {
+      result = static_cast<Cat>(result | Cat::QUERY);
+    } else {
+      PERFETTO_ELOG("Unknown metatrace category %s", cur.data());
+      exit(1);
+    }
+  }
+  return result;
+}
+
 struct CommandLineOptions {
   std::string perf_file_path;
   std::string query_file_path;
@@ -667,6 +692,9 @@
   bool wide = false;
   bool force_full_sort = false;
   std::string metatrace_path;
+  size_t metatrace_buffer_capacity = 0;
+  metatrace::MetatraceCategories metatrace_categories =
+      metatrace::MetatraceCategories::ALL;
   bool dev = false;
   bool no_ftrace_raw = false;
 };
@@ -713,6 +741,10 @@
                                       text).
  -m, --metatrace FILE                 Enables metatracing of trace processor
                                       writing the resulting trace into FILE.
+ --metatrace-buffer-capacity N        Sets metatrace event buffer to capture
+                                      last N events.
+ --metatrace-categories CATEGORIES    A comma-separated list of metatrace
+                                      categories to enable.
  --full-sort                          Forces the trace processor into performing
                                       a full sort ignoring any windowing
                                       logic.
@@ -745,6 +777,8 @@
     OPT_METRIC_EXTENSION,
     OPT_DEV,
     OPT_NO_FTRACE_RAW,
+    OPT_METATRACE_BUFFER_CAPACITY,
+    OPT_METATRACE_CATEGORIES,
   };
 
   static const option long_options[] = {
@@ -758,6 +792,10 @@
       {"query-file", required_argument, nullptr, 'q'},
       {"export", required_argument, nullptr, 'e'},
       {"metatrace", required_argument, nullptr, 'm'},
+      {"metatrace-buffer-capacity", required_argument, nullptr,
+       OPT_METATRACE_BUFFER_CAPACITY},
+      {"metatrace-categories", required_argument, nullptr,
+       OPT_METATRACE_CATEGORIES},
       {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
       {"pre-metrics", required_argument, nullptr, OPT_PRE_METRICS},
       {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
@@ -867,6 +905,18 @@
       continue;
     }
 
+    if (option == OPT_METATRACE_BUFFER_CAPACITY) {
+      command_line_options.metatrace_buffer_capacity =
+          static_cast<size_t>(atoi(optarg));
+      continue;
+    }
+
+    if (option == OPT_METATRACE_CATEGORIES) {
+      command_line_options.metatrace_categories =
+          ParseMetatraceCategories(optarg);
+      continue;
+    }
+
     PrintUsage(argv);
     exit(option == 'h' ? 0 : 1);
   }
@@ -1294,6 +1344,25 @@
   return base::OkStatus();
 }
 
+base::Status MaybeWriteMetatrace(const std::string& metatrace_path) {
+  if (metatrace_path.empty()) {
+    return base::OkStatus();
+  }
+  std::vector<uint8_t> serialized;
+  base::Status status = g_tp->DisableAndReadMetatrace(&serialized);
+  if (!status.ok())
+    return status;
+
+  auto file = base::OpenFile(metatrace_path, O_CREAT | O_RDWR | O_TRUNC);
+  if (!file)
+    return base::ErrStatus("Unable to open metatrace file");
+
+  ssize_t res = base::WriteAll(*file, serialized.data(), serialized.size());
+  if (res < 0)
+    return base::ErrStatus("Error while writing metatrace file");
+  return base::OkStatus();
+}
+
 base::Status TraceProcessorMain(int argc, char** argv) {
   CommandLineOptions options = ParseCommandLineOptions(argc, argv);
 
@@ -1320,7 +1389,10 @@
 
   // Enable metatracing as soon as possible.
   if (!options.metatrace_path.empty()) {
-    tp->EnableMetatrace();
+    metatrace::MetatraceConfig metatrace_config;
+    metatrace_config.override_buffer_size = options.metatrace_buffer_capacity;
+    metatrace_config.categories = options.metatrace_categories;
+    tp->EnableMetatrace(metatrace_config);
   }
 
   // We load all the metric extensions even when --run-metrics arg is not there,
@@ -1344,14 +1416,8 @@
     RETURN_IF_ERROR(PrintStats());
   }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
-  if (options.enable_httpd) {
-    RunHttpRPCServer(std::move(tp), options.port_number);
-    PERFETTO_FATAL("Should never return");
-  }
-#endif
-
 #if PERFETTO_HAS_SIGNAL_H()
+  // Set up interrupt signal to allow the user to abort query.
   signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
 #endif
 
@@ -1380,7 +1446,12 @@
   }
 
   if (!options.query_file_path.empty()) {
-    RETURN_IF_ERROR(RunQueries(options.query_file_path, true));
+    base::Status status = RunQueries(options.query_file_path, true);
+    if (!status.ok()) {
+      // Write metatrace if needed before exiting.
+      RETURN_IF_ERROR(MaybeWriteMetatrace(options.metatrace_path));
+      return status;
+    }
   }
   base::TimeNanos t_query = base::GetWallTimeNs() - t_query_start;
 
@@ -1388,6 +1459,19 @@
     RETURN_IF_ERROR(ExportTraceToDatabase(options.sqlite_file_path));
   }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
+  if (options.enable_httpd) {
+#if PERFETTO_HAS_SIGNAL_H()
+    // Restore the default signal handler to allow the user to terminate
+    // httpd server via Ctrl-C.
+    signal(SIGINT, SIG_DFL);
+#endif
+
+    RunHttpRPCServer(std::move(tp), options.port_number);
+    PERFETTO_FATAL("Should never return");
+  }
+#endif
+
   if (options.launch_shell) {
     RETURN_IF_ERROR(StartInteractiveShell(
         InteractiveOptions{options.wide ? 40u : 20u, metric_format,
@@ -1396,21 +1480,7 @@
     RETURN_IF_ERROR(PrintPerfFile(options.perf_file_path, t_load, t_query));
   }
 
-  if (!options.metatrace_path.empty()) {
-    std::vector<uint8_t> serialized;
-    base::Status status = g_tp->DisableAndReadMetatrace(&serialized);
-    if (!status.ok())
-      return status;
-
-    auto file =
-        base::OpenFile(options.metatrace_path, O_CREAT | O_RDWR | O_TRUNC);
-    if (!file)
-      return base::ErrStatus("Unable to open metatrace file");
-
-    ssize_t res = base::WriteAll(*file, serialized.data(), serialized.size());
-    if (res < 0)
-      return base::ErrStatus("Error while writing metatrace file");
-  }
+  RETURN_IF_ERROR(MaybeWriteMetatrace(options.metatrace_path));
 
   return base::OkStatus();
 }
diff --git a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256 b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
index 4f40c30..727703f 100644
--- a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
@@ -1 +1 @@
-6150b9e984e4e8ace644002edd9f643f696a3cf4c220e2ab426a27f606212d1d
\ No newline at end of file
+2c07ddb7c8b3cdede9cb9aec41ab8f206ee50e6fa789e8913044e209141a9365
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
index cfb384c..32cf9b1 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
@@ -1 +1 @@
-a07fb42e61182f2409a46266a77c592d869c977eb4fab642bbc8c81ab9ec6cc1
\ No newline at end of file
+6bb4ec59ccea148ed9ddd2878dad220dcdeb0264a3a61334c9ee66d57cd8094d
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
index 37bf83c..651681e 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
@@ -1 +1 @@
-3c7d8d5425911d796df29d75c60413e72bf72d14a17d949ebbc839f6b2ec844b
\ No newline at end of file
+d8c9876668f6f0673d99adf2ad7ee30dad0ffafe60bd0faa9fb709e49addba20
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
index 24c6d44..fd28c9c 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
@@ -1 +1 @@
-abb11679743ce148a24543a7956679816fa6ea4937896690b19996ce5d415642
\ No newline at end of file
+21d088add5542b38703452ac16600f878f865bc220baad0d912bc56d982ab207
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
index cfb384c..32cf9b1 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
@@ -1 +1 @@
-a07fb42e61182f2409a46266a77c592d869c977eb4fab642bbc8c81ab9ec6cc1
\ No newline at end of file
+6bb4ec59ccea148ed9ddd2878dad220dcdeb0264a3a61334c9ee66d57cd8094d
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
index cfb384c..32cf9b1 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
@@ -1 +1 @@
-a07fb42e61182f2409a46266a77c592d869c977eb4fab642bbc8c81ab9ec6cc1
\ No newline at end of file
+6bb4ec59ccea148ed9ddd2878dad220dcdeb0264a3a61334c9ee66d57cd8094d
\ No newline at end of file
diff --git a/tools/gen_amalgamated_sql.py b/tools/gen_amalgamated_sql.py
index 285093d..9e5ff36 100755
--- a/tools/gen_amalgamated_sql.py
+++ b/tools/gen_amalgamated_sql.py
@@ -74,6 +74,7 @@
   parser.add_argument('--cpp-out', required=True)
   parser.add_argument('--input-list-file')
   parser.add_argument('--root-dir', required=True)
+  parser.add_argument('--depfile')
   parser.add_argument('sql_files', nargs='*')
   args = parser.parse_args()
 
@@ -134,6 +135,12 @@
 
     output.write(NAMESPACE_END.format(args.namespace))
 
+  if args.depfile:
+    with open(args.depfile, 'w', encoding='utf-8') as f:
+      f.write(args.cpp_out + ":")
+      for line in sql_files:
+        f.write(' ' + line)
+
   return 0
 
 
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index c7e988b..94571ca 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -161,7 +161,7 @@
 export enum ProfileType {
   HEAP_PROFILE = 'heap_profile',
   NATIVE_HEAP_PROFILE = 'heap_profile:libc.malloc',
-  JAVA_HEAP_PROFILE = 'heap_profile:com.android.art',
+  JAVA_HEAP_SAMPLES = 'heap_profile:com.android.art',
   JAVA_HEAP_GRAPH = 'graph',
   PERF_SAMPLE = 'perf',
 }
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index bbbecb4..6d4f36d 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -60,7 +60,7 @@
   switch (type) {
     case ProfileType.HEAP_PROFILE:
     case ProfileType.NATIVE_HEAP_PROFILE:
-    case ProfileType.JAVA_HEAP_PROFILE:
+    case ProfileType.JAVA_HEAP_SAMPLES:
       return 'native';
     case ProfileType.JAVA_HEAP_GRAPH:
       return 'graph';
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index bd5d4d0..046db9e 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -147,6 +147,10 @@
   defaultValue: false,
 });
 
+// A local storage key where the indication that JSON warning has been shown is
+// stored.
+const SHOWN_JSON_WARNING_KEY = 'shownJsonWarning';
+
 function showJsonWarning() {
   showModal({
     title: 'Warning',
@@ -162,7 +166,7 @@
           m('br')),
     buttons: [],
   });
-};
+}
 
 // TraceController handles handshakes with the frontend for everything that
 // concerns a single trace. It owns the WASM trace processor engine, handles
@@ -405,7 +409,10 @@
       endSec,
     };
 
-    {
+    const shownJsonWarning =
+        window.localStorage.getItem(SHOWN_JSON_WARNING_KEY) !== null;
+
+    if (!shownJsonWarning) {
       // Show warning if the trace is in JSON format.
       const query = `select str_value from metadata where name = 'trace_type'`;
       const result = await assertExists(this.engine).query(query);
@@ -414,8 +421,11 @@
       // it passes to Perfetto, so we don't need to show this warning.
       if (traceType.str_value == 'json' && !frontendGlobals.embeddedMode) {
         showJsonWarning();
+        // Save that the warning has been shown. Value is irrelevant since only
+        // the presence of key is going to be checked.
+        window.localStorage.setItem(SHOWN_JSON_WARNING_KEY, 'true');
       }
-    };
+    }
 
     const emptyOmniboxState = {
       omnibox: '',
diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts
index c6b1745..c5d56cc 100644
--- a/ui/src/frontend/flamegraph_panel.ts
+++ b/ui/src/frontend/flamegraph_panel.ts
@@ -136,7 +136,7 @@
                     onkeydown: (e: Event) => e.stopPropagation(),
                   }),
                   this.profileType === ProfileType.NATIVE_HEAP_PROFILE ||
-                          this.profileType === ProfileType.JAVA_HEAP_PROFILE ?
+                          this.profileType === ProfileType.JAVA_HEAP_SAMPLES ?
                       m('button.download',
                         {
                           onclick: () => {
@@ -190,8 +190,8 @@
         return 'Heap profile:';
       case ProfileType.NATIVE_HEAP_PROFILE:
         return 'Native heap profile:';
-      case ProfileType.JAVA_HEAP_PROFILE:
-        return 'Java heap profile:';
+      case ProfileType.JAVA_HEAP_SAMPLES:
+        return 'Java heap samples:';
       case ProfileType.JAVA_HEAP_GRAPH:
         return 'Java heap graph:';
       case ProfileType.PERF_SAMPLE:
@@ -215,7 +215,7 @@
         }
       case ProfileType.HEAP_PROFILE:
       case ProfileType.NATIVE_HEAP_PROFILE:
-      case ProfileType.JAVA_HEAP_PROFILE:
+      case ProfileType.JAVA_HEAP_SAMPLES:
       case ProfileType.PERF_SAMPLE:
         return RENDER_SELF_AND_TOTAL;
       default:
@@ -315,7 +315,7 @@
           this.buildButtonComponent(
               OBJECTS_ALLOCATED_KEY, 'Total malloc count'),
         ];
-      case ProfileType.JAVA_HEAP_PROFILE:
+      case ProfileType.JAVA_HEAP_SAMPLES:
         return [
           this.buildButtonComponent(
               ALLOC_SPACE_MEMORY_ALLOCATED_KEY, 'Total allocation size'),
diff --git a/ui/src/frontend/pivot_table_redux_query_generator.ts b/ui/src/frontend/pivot_table_redux_query_generator.ts
index eae6b8c..ddc440b 100644
--- a/ui/src/frontend/pivot_table_redux_query_generator.ts
+++ b/ui/src/frontend/pivot_table_redux_query_generator.ts
@@ -40,7 +40,7 @@
 
 export const sliceTable = {
   name: 'slice',
-  columns: ['type', 'ts', 'dur', 'category', 'name'],
+  columns: ['type', 'ts', 'dur', 'category', 'name', 'depth'],
 };
 
 // Columns of `slice` table available for aggregation.
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index c61a4e7..d65fa35 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -961,6 +961,9 @@
             case 'ARRAY_BUFFER':
               traceTitle = engines[0].source.title;
               traceUrl = engines[0].source.url || '';
+              const arrayBufferSizeMB =
+                  Math.ceil(engines[0].source.buffer.byteLength / 1e6);
+              traceTitle += ` (${arrayBufferSizeMB} MB)`;
               break;
             case 'HTTP_RPC':
               traceTitle = 'External trace (RPC)';