Merge "Add ability to pivot on depth"
diff --git a/BUILD b/BUILD
index 1c67ffa..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",
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/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/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/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();
 }