Merge "perfetto-ui: Merging small callsites in flamegraph"
diff --git a/Android.bp b/Android.bp
index d93b0c1..514776e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,7 @@
     "src/trace_processor/metrics/android/android_startup_launches.sql",
     "src/trace_processor/metrics/android/android_task_state.sql",
     "src/trace_processor/metrics/android/heap_profile_callsites.sql",
+    "src/trace_processor/metrics/android/java_heap_stats.sql",
     "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
     "src/trace_processor/metrics/android/process_mem.sql",
     "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
@@ -2397,6 +2398,7 @@
     "protos/perfetto/metrics/android/cpu_metric.proto",
     "protos/perfetto/metrics/android/heap_profile_callsites.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
+    "protos/perfetto/metrics/android/java_heap_stats.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
     "protos/perfetto/metrics/android/mem_metric.proto",
     "protos/perfetto/metrics/android/mem_unagg_metric.proto",
@@ -2416,6 +2418,7 @@
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/heap_profile_callsites.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/ion_metric.pbzero.cc",
+    "external/perfetto/protos/perfetto/metrics/android/java_heap_stats.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/lmk_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/mem_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/mem_unagg_metric.pbzero.cc",
@@ -2435,6 +2438,7 @@
     "protos/perfetto/metrics/android/cpu_metric.proto",
     "protos/perfetto/metrics/android/heap_profile_callsites.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
+    "protos/perfetto/metrics/android/java_heap_stats.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
     "protos/perfetto/metrics/android/mem_metric.proto",
     "protos/perfetto/metrics/android/mem_unagg_metric.proto",
@@ -2454,6 +2458,7 @@
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/heap_profile_callsites.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/ion_metric.pbzero.h",
+    "external/perfetto/protos/perfetto/metrics/android/java_heap_stats.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/lmk_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/mem_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/mem_unagg_metric.pbzero.h",
@@ -4093,7 +4098,6 @@
   name: "perfetto_src_base_unittests",
   srcs: [
     "src/base/circular_queue_unittest.cc",
-    "src/base/copyable_ptr_unittest.cc",
     "src/base/metatrace_unittest.cc",
     "src/base/no_destructor_unittest.cc",
     "src/base/optional_unittest.cc",
@@ -4502,6 +4506,7 @@
 filegroup {
   name: "perfetto_src_protozero_unittests",
   srcs: [
+    "src/protozero/copyable_ptr_unittest.cc",
     "src/protozero/message_handle_unittest.cc",
     "src/protozero/message_unittest.cc",
     "src/protozero/proto_decoder_unittest.cc",
@@ -5050,6 +5055,14 @@
   ],
 }
 
+// GN: //src/tracing:sliced_protobuf_input_stream
+filegroup {
+  name: "perfetto_src_tracing_sliced_protobuf_input_stream",
+  srcs: [
+    "src/tracing/core/sliced_protobuf_input_stream.cc",
+  ],
+}
+
 // GN: //src/tracing/test:api_test_support
 filegroup {
   name: "perfetto_src_tracing_test_api_test_support",
@@ -5076,7 +5089,6 @@
     "src/tracing/core/packet_stream_validator.cc",
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
-    "src/tracing/core/sliced_protobuf_input_stream.cc",
     "src/tracing/core/startup_trace_writer.cc",
     "src/tracing/core/startup_trace_writer_registry.cc",
     "src/tracing/core/trace_buffer.cc",
@@ -5449,6 +5461,7 @@
     ":perfetto_src_traced_service_unittests",
     ":perfetto_src_tracing_common",
     ":perfetto_src_tracing_ipc",
+    ":perfetto_src_tracing_sliced_protobuf_input_stream",
     ":perfetto_src_tracing_test_support",
     ":perfetto_src_tracing_tracing",
     ":perfetto_src_tracing_unittests",
diff --git a/BUILD b/BUILD
index 562fb80..57831e6 100644
--- a/BUILD
+++ b/BUILD
@@ -208,7 +208,6 @@
     srcs = [
         "include/perfetto/base/build_config.h",
         "include/perfetto/base/compiler.h",
-        "include/perfetto/base/copyable_ptr.h",
         "include/perfetto/base/export.h",
         "include/perfetto/base/logging.h",
         "include/perfetto/base/task_runner.h",
@@ -311,7 +310,6 @@
         "include/perfetto/ext/tracing/core/shared_memory_abi.h",
         "include/perfetto/ext/tracing/core/shared_memory_arbiter.h",
         "include/perfetto/ext/tracing/core/slice.h",
-        "include/perfetto/ext/tracing/core/sliced_protobuf_input_stream.h",
         "include/perfetto/ext/tracing/core/startup_trace_writer.h",
         "include/perfetto/ext/tracing/core/startup_trace_writer_registry.h",
         "include/perfetto/ext/tracing/core/trace_packet.h",
@@ -346,6 +344,7 @@
     name = "include_perfetto_protozero_protozero",
     srcs = [
         "include/perfetto/protozero/contiguous_memory_range.h",
+        "include/perfetto/protozero/copyable_ptr.h",
         "include/perfetto/protozero/field.h",
         "include/perfetto/protozero/message.h",
         "include/perfetto/protozero/message_handle.h",
@@ -576,6 +575,7 @@
         "src/trace_processor/metrics/android/android_startup_launches.sql",
         "src/trace_processor/metrics/android/android_task_state.sql",
         "src/trace_processor/metrics/android/heap_profile_callsites.sql",
+        "src/trace_processor/metrics/android/java_heap_stats.sql",
         "src/trace_processor/metrics/android/mem_stats_priority_breakdown.sql",
         "src/trace_processor/metrics/android/process_mem.sql",
         "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
@@ -1073,7 +1073,6 @@
         "src/tracing/core/shared_memory_abi.cc",
         "src/tracing/core/shared_memory_arbiter_impl.cc",
         "src/tracing/core/shared_memory_arbiter_impl.h",
-        "src/tracing/core/sliced_protobuf_input_stream.cc",
         "src/tracing/core/startup_trace_writer.cc",
         "src/tracing/core/startup_trace_writer_registry.cc",
         "src/tracing/core/trace_buffer.cc",
@@ -1616,6 +1615,7 @@
         "protos/perfetto/metrics/android/cpu_metric.proto",
         "protos/perfetto/metrics/android/heap_profile_callsites.proto",
         "protos/perfetto/metrics/android/ion_metric.proto",
+        "protos/perfetto/metrics/android/java_heap_stats.proto",
         "protos/perfetto/metrics/android/lmk_metric.proto",
         "protos/perfetto/metrics/android/mem_metric.proto",
         "protos/perfetto/metrics/android/mem_unagg_metric.proto",
diff --git a/BUILD.gn b/BUILD.gn
index 965d68e..e912a12 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -211,12 +211,16 @@
   component("libperfetto") {
     public_configs = [ "gn:public_config" ]
     deps = [
+      "src/trace_processor:export_json",
+      "src/trace_processor:storage",
       "src/tracing",
     ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
     public_deps = [
+      "include/perfetto/ext/trace_processor:export_json",
       "include/perfetto/ext/tracing/core",
+      "include/perfetto/trace_processor:storage",
       "protos/perfetto/common:zero",
       "protos/perfetto/trace:zero",
       "protos/perfetto/trace/chrome:zero",
@@ -238,17 +242,4 @@
       ]
     }
   }
-  component("libproto_to_json") {
-    public_configs = [ "gn:public_config" ]
-    deps = [
-      "src/trace_processor:export_json",
-      "src/trace_processor:storage",
-    ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-    public_deps = [
-      "include/perfetto/ext/trace_processor:export_json",
-      "include/perfetto/trace_processor:storage",
-    ]
-  }
 }
diff --git a/docs/heapprofd.md b/docs/heapprofd.md
index 469ebac..fa2d2ff 100644
--- a/docs/heapprofd.md
+++ b/docs/heapprofd.md
@@ -33,6 +33,12 @@
 
 This will create a pprof-compatible heap dump when Ctrl+C is pressed.
 
+You can also use the [Perfetto UI](https://ui.perfetto.dev/#!/record?p=memory)
+to record heapprofd profiles. Tick "Heap profiling" in the trace configuration,
+enter the processes you want to target, click "Add Device" to pair your phone,
+and record profiles straight from your browser. This is also possible on
+Windows.
+
 ## Viewing the data
 
 The resulting profile proto contains four views on the data
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index 0e96e07..4dbcd42 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -194,7 +194,7 @@
 declare_args() {
   # Enables the SQL query layer of trace processor.
   enable_perfetto_trace_processor_sqlite =
-      enable_perfetto_trace_processor && !build_with_chromium
+      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
 
   # Enables the optional SQLite percentile module.
   enable_perfetto_trace_processor_percentile =
@@ -207,23 +207,23 @@
 
   # Enables ftrace support in the trace processor.
   enable_perfetto_trace_processor_ftrace =
-      enable_perfetto_trace_processor && !build_with_chromium
+      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
 
   # Enables parsing support for system probes in trace processor.
   enable_perfetto_trace_processor_system_probes =
-      enable_perfetto_trace_processor && !build_with_chromium
+      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
 
   # Enables parsing support for android system probes in trace processor.
   enable_perfetto_trace_processor_android_probes =
-      enable_perfetto_trace_processor && !build_with_chromium
+      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
 
   # Enables parsing support for heap graphs in trace processor.
   enable_perfetto_trace_processor_heap_graphs =
-      enable_perfetto_trace_processor && !build_with_chromium
+      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
 
   # Enables graphics event support in the trace processor.
   enable_perfetto_trace_processor_graphics =
-      enable_perfetto_trace_processor && !build_with_chromium
+      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
 
   # Enables JSON support in the trace processor. Required for JSON trace import
   # and export. Importer support can also be disabled using
@@ -233,7 +233,7 @@
 
   # Enables Fuchsia trace format support in trace processor.
   enable_perfetto_trace_processor_fuchsia =
-      enable_perfetto_trace_processor && !build_with_chromium
+      enable_perfetto_trace_processor && !(build_with_chromium && is_android)
 
   # Enables httpd RPC support in the trace processor.
   # Further per-OS conditionals are applied in gn/BUILD.gn.
@@ -244,7 +244,8 @@
 declare_args() {
   # Enables importer support for JSON traces in the trace processor.
   enable_perfetto_trace_processor_json_import =
-      enable_perfetto_trace_processor_json && !build_with_chromium
+      enable_perfetto_trace_processor_json &&
+      !(build_with_chromium && is_android)
 
   # Enables syscall support in trace processor. Required for ftrace, system
   # probes, and android probes support.
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index 9dc0893..e490551 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -18,7 +18,6 @@
   sources = [
     "build_config.h",
     "compiler.h",
-    "copyable_ptr.h",
     "export.h",
     "logging.h",
     "task_runner.h",
diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h
index fa44b6b..0b2efbf 100644
--- a/include/perfetto/base/compiler.h
+++ b/include/perfetto/base/compiler.h
@@ -57,6 +57,15 @@
 #define PERFETTO_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
 #endif
 
+#if defined(__GNUC__) || defined(__clang__)
+#define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() __PRETTY_FUNCTION__
+#elif defined(_MSC_VER)
+#define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() __FUNCSIG__
+#else
+#define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() \
+  static_assert(false, "Not implemented for this compiler")
+#endif
+
 namespace perfetto {
 namespace base {
 
diff --git a/include/perfetto/ext/tracing/core/BUILD.gn b/include/perfetto/ext/tracing/core/BUILD.gn
index e88fd76..69eff99 100644
--- a/include/perfetto/ext/tracing/core/BUILD.gn
+++ b/include/perfetto/ext/tracing/core/BUILD.gn
@@ -28,7 +28,6 @@
     "shared_memory_abi.h",
     "shared_memory_arbiter.h",
     "slice.h",
-    "sliced_protobuf_input_stream.h",
     "startup_trace_writer.h",
     "startup_trace_writer_registry.h",
     "trace_packet.h",
diff --git a/include/perfetto/ext/tracing/core/shared_memory_arbiter.h b/include/perfetto/ext/tracing/core/shared_memory_arbiter.h
index 2465d43..0937621 100644
--- a/include/perfetto/ext/tracing/core/shared_memory_arbiter.h
+++ b/include/perfetto/ext/tracing/core/shared_memory_arbiter.h
@@ -34,7 +34,6 @@
 class TaskRunner;
 }
 
-class CommitDataRequest;
 class StartupTraceWriter;
 class StartupTraceWriterRegistry;
 class SharedMemory;
diff --git a/include/perfetto/ext/tracing/core/trace_packet.h b/include/perfetto/ext/tracing/core/trace_packet.h
index 452c5b3..c1e7948 100644
--- a/include/perfetto/ext/tracing/core/trace_packet.h
+++ b/include/perfetto/ext/tracing/core/trace_packet.h
@@ -27,10 +27,6 @@
 
 namespace perfetto {
 
-namespace protos {
-class TracePacket;  // From protos/trace_packet.pb.h.
-}  // namespace protos
-
 // A wrapper around a byte buffer that contains a protobuf-encoded TracePacket
 // (see trace_packet.proto). The TracePacket is decoded only if the Consumer
 // requests that. This is to allow Consumer(s) to just stream the packet over
diff --git a/include/perfetto/protozero/BUILD.gn b/include/perfetto/protozero/BUILD.gn
index 7749684..994ff60 100644
--- a/include/perfetto/protozero/BUILD.gn
+++ b/include/perfetto/protozero/BUILD.gn
@@ -18,6 +18,7 @@
   ]
   sources = [
     "contiguous_memory_range.h",
+    "copyable_ptr.h",
     "field.h",
     "message.h",
     "message_handle.h",
diff --git a/include/perfetto/base/copyable_ptr.h b/include/perfetto/protozero/copyable_ptr.h
similarity index 92%
rename from include/perfetto/base/copyable_ptr.h
rename to include/perfetto/protozero/copyable_ptr.h
index 236382f..2fe5ff6 100644
--- a/include/perfetto/base/copyable_ptr.h
+++ b/include/perfetto/protozero/copyable_ptr.h
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef INCLUDE_PERFETTO_BASE_COPYABLE_PTR_H_
-#define INCLUDE_PERFETTO_BASE_COPYABLE_PTR_H_
+#ifndef INCLUDE_PERFETTO_PROTOZERO_COPYABLE_PTR_H_
+#define INCLUDE_PERFETTO_PROTOZERO_COPYABLE_PTR_H_
 
 #include <memory>
 
-namespace perfetto {
-namespace base {
+namespace protozero {
 
 // This class is essentially a std::vector<T> of fixed size = 1.
 // It's a pointer wrapper with deep copying and deep equality comparison.
@@ -83,7 +82,6 @@
   std::unique_ptr<T> ptr_;
 };
 
-}  // namespace base
-}  // namespace perfetto
+}  // namespace protozero
 
-#endif  // INCLUDE_PERFETTO_BASE_COPYABLE_PTR_H_
+#endif  // INCLUDE_PERFETTO_PROTOZERO_COPYABLE_PTR_H_
diff --git a/include/perfetto/protozero/field.h b/include/perfetto/protozero/field.h
index 5bd3021..f76fe84 100644
--- a/include/perfetto/protozero/field.h
+++ b/include/perfetto/protozero/field.h
@@ -84,6 +84,11 @@
     return static_cast<int32_t>(int_value_);
   }
 
+  int32_t as_sint32() const {
+    PERFETTO_DCHECK(!valid() || type() == proto_utils::ProtoWireType::kVarInt);
+    return proto_utils::ZigZagDecode(static_cast<uint32_t>(int_value_));
+  }
+
   uint64_t as_uint64() const {
     PERFETTO_DCHECK(!valid() || type() == proto_utils::ProtoWireType::kVarInt ||
                     type() == proto_utils::ProtoWireType::kFixed32 ||
@@ -98,6 +103,11 @@
     return static_cast<int64_t>(int_value_);
   }
 
+  int64_t as_sint64() const {
+    PERFETTO_DCHECK(!valid() || type() == proto_utils::ProtoWireType::kVarInt);
+    return proto_utils::ZigZagDecode(static_cast<uint64_t>(int_value_));
+  }
+
   float as_float() const {
     PERFETTO_DCHECK(!valid() || type() == proto_utils::ProtoWireType::kFixed32);
     float res;
@@ -159,8 +169,18 @@
   void get(int64_t* val) const { *val = as_int64(); }
   void get(float* val) const { *val = as_float(); }
   void get(double* val) const { *val = as_double(); }
+  void get(std::string* val) const { *val = as_std_string(); }
   void get(ConstChars* val) const { *val = as_string(); }
   void get(ConstBytes* val) const { *val = as_bytes(); }
+  void get_signed(int32_t* val) const { *val = as_sint32(); }
+  void get_signed(int64_t* val) const { *val = as_sint64(); }
+
+  // For enum types.
+  template <typename T,
+            typename = typename std::enable_if<std::is_enum<T>::value, T>::type>
+  void get(T* val) const {
+    *val = static_cast<T>(as_int32());
+  }
 
  private:
   // Fields are deliberately not initialized to keep the class trivially
diff --git a/include/perfetto/protozero/proto_decoder.h b/include/perfetto/protozero/proto_decoder.h
index 584f7be..5794cc9 100644
--- a/include/perfetto/protozero/proto_decoder.h
+++ b/include/perfetto/protozero/proto_decoder.h
@@ -43,8 +43,11 @@
 class ProtoDecoder {
  public:
   // Creates a ProtoDecoder using the given |buffer| with size |length| bytes.
-  ProtoDecoder(const uint8_t* buffer, size_t length)
-      : begin_(buffer), end_(buffer + length), read_ptr_(buffer) {}
+  ProtoDecoder(const void* buffer, size_t length)
+      : begin_(reinterpret_cast<const uint8_t*>(buffer)),
+        end_(begin_ + length),
+        read_ptr_(begin_) {}
+  ProtoDecoder(const std::string& str) : ProtoDecoder(str.data(), str.size()) {}
   ProtoDecoder(const ConstBytes& cb) : ProtoDecoder(cb.data, cb.size) {}
 
   // Reads the next field from the buffer and advances the read cursor. If a
@@ -113,6 +116,7 @@
     iter_->get(&val);
     return val;
   }
+  const Field* operator->() const { return iter_; }
 
   RepeatedFieldIterator& operator++() {
     PERFETTO_DCHECK(iter_ != end_);
diff --git a/include/perfetto/protozero/proto_utils.h b/include/perfetto/protozero/proto_utils.h
index 3bfe470..8a93951 100644
--- a/include/perfetto/protozero/proto_utils.h
+++ b/include/perfetto/protozero/proto_utils.h
@@ -158,6 +158,15 @@
          static_cast<UnsignedType>(value >> (sizeof(T) * 8 - 1));
 }
 
+// Proto types: sint64, sint32.
+template <typename T>
+inline typename std::make_signed<T>::type ZigZagDecode(T value) {
+  using UnsignedType = typename std::make_unsigned<T>::type;
+  auto u_value = static_cast<UnsignedType>(value);
+  return static_cast<typename std::make_signed<T>::type>(
+      ((u_value >> 1) ^ -(u_value & 1)));
+}
+
 template <typename T>
 inline uint8_t* WriteVarInt(T value, uint8_t* target) {
   // If value is <= 0 we must first sign extend to int64_t (see [1]).
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index 9cbdf34..c303938 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -24,15 +24,6 @@
 
 #include <unordered_map>
 
-// Only used to DCHECK that one interned data field isn't accidentally shared by
-// multiple TrackEventInternedData subclasses.
-// TODO(skyostil): Add __FUNCSIG__ for MSVC.
-#if PERFETTO_DCHECK_IS_ON() && defined(__GNUC__)
-#define PERFETTO_INTERNAL_TYPE_IDENTIFIER __PRETTY_FUNCTION__
-#else  // PERFETTO_DCHECK_IS_ON() && defined(__GNUC__)
-#define PERFETTO_INTERNAL_TYPE_IDENTIFIER ""
-#endif  // PERFETTO_DCHECK_IS_ON() && defined(__GNUC__)
-
 namespace perfetto {
 class DataSourceConfig;
 class DataSourceDescriptor;
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index 5ebf8a4..fca7deb 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -61,6 +61,8 @@
                                  &g_category_state_storage[0]);               \
   extern const ::perfetto::internal::TrackEventCategoryRegistry               \
       kCategoryRegistry;                                                      \
+  static_assert(kConstExprCategoryRegistry.ValidateCategories(),              \
+                "Invalid category names found");                              \
   }  // namespace internal
 
 // In a .cc file, declares storage for each category's runtime state.
@@ -95,4 +97,9 @@
         PERFETTO_GET_CATEGORY_INDEX(category)>(instances, ##__VA_ARGS__); \
   })
 
+// Generate a unique variable name with a given prefix.
+#define PERFETTO_INTERNAL_CONCAT2(a, b) a##b
+#define PERFETTO_INTERNAL_CONCAT(a, b) PERFETTO_INTERNAL_CONCAT2(a, b)
+#define PERFETTO_INTERNAL_UID(prefix) PERFETTO_INTERNAL_CONCAT(prefix, __LINE__)
+
 #endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_MACROS_H_
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index 83907ab..5b909be 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -54,8 +54,7 @@
 //
 //       int main() {
 //         perfetto::TrackEvent::Register();
-//         TRACK_EVENT_BEGIN("category", "MyEvent");
-//         TRACK_EVENT_END("category");
+//         TRACK_EVENT("category", "MyEvent");
 //         ...
 //       }
 //
@@ -143,11 +142,26 @@
       category, nullptr,               \
       ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END, ##__VA_ARGS__)
 
+// Begin a thread-scoped slice which gets automatically closed when going out of
+// scope.
+#define TRACE_EVENT(category, name, ...)               \
+  TRACE_EVENT_BEGIN(category, name, ##__VA_ARGS__);    \
+  struct {                                             \
+    struct EventFinalizer {                            \
+      ~EventFinalizer() { TRACE_EVENT_END(category); } \
+    } finalizer;                                       \
+  } PERFETTO_INTERNAL_UID(scoped_event)
+
+// Emit a thread-scoped slice which has zero duration.
+// TODO(skyostil): Add support for process-wide and global instant events.
+#define TRACE_EVENT_INSTANT(category, name, ...)                            \
+  PERFETTO_INTERNAL_TRACK_EVENT(                                            \
+      category, name, ::perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT, \
+      ##__VA_ARGS__)
+
 // TODO(skyostil): Add arguments.
-// TODO(skyostil): Add scoped events.
 // TODO(skyostil): Add async events.
 // TODO(skyostil): Add flow events.
-// TODO(skyostil): Add instant events.
 // TODO(skyostil): Add counters.
 
 #endif  // INCLUDE_PERFETTO_TRACING_TRACK_EVENT_H_
diff --git a/include/perfetto/tracing/track_event_category_registry.h b/include/perfetto/tracing/track_event_category_registry.h
index 3e15db6..f31696b 100644
--- a/include/perfetto/tracing/track_event_category_registry.h
+++ b/include/perfetto/tracing/track_event_category_registry.h
@@ -89,8 +89,22 @@
     return CategoryIndex;
   }
 
+  constexpr bool ValidateCategories(size_t index = 0) const {
+    return (index == category_count_)
+               ? true
+               : IsValidCategoryName(categories_[index].name)
+                     ? ValidateCategories(index + 1)
+                     : false;
+  }
+
  private:
   // TODO(skyostil): Make the compile-time routines nicer with C++14.
+  static constexpr bool IsValidCategoryName(const char* name) {
+    return (*name == '\"' || *name == '*')
+               ? false
+               : *name ? IsValidCategoryName(name + 1) : true;
+  }
+
   static constexpr bool StringEq(const char* a, const char* b) {
     return *a != *b ? false
                     : (!*a || !*b) ? (*a == *b) : StringEq(a + 1, b + 1);
diff --git a/include/perfetto/tracing/track_event_interned_data_index.h b/include/perfetto/tracing/track_event_interned_data_index.h
index 197081b..e1ef7ee 100644
--- a/include/perfetto/tracing/track_event_interned_data_index.h
+++ b/include/perfetto/tracing/track_event_interned_data_index.h
@@ -19,6 +19,8 @@
 
 #include "perfetto/tracing/internal/track_event_internal.h"
 
+#include "perfetto/base/compiler.h"
+
 #include <map>
 #include <type_traits>
 #include <unordered_map>
@@ -194,11 +196,12 @@
     for (const auto& entry : incremental_state->interned_data_indices) {
       if (entry.first == FieldNumber) {
 #if PERFETTO_DCHECK_IS_ON()
-        if (strcmp(PERFETTO_INTERNAL_TYPE_IDENTIFIER, entry.second->type_id_)) {
+        if (strcmp(PERFETTO_DEBUG_FUNCTION_IDENTIFIER(),
+                   entry.second->type_id_)) {
           PERFETTO_FATAL(
               "Interned data accessed under different types! Previous type: "
               "%s. New type: %s.",
-              entry.second->type_id_, PERFETTO_INTERNAL_TYPE_IDENTIFIER);
+              entry.second->type_id_, PERFETTO_DEBUG_FUNCTION_IDENTIFIER());
         }
 #endif  // PERFETTO_DCHECK_IS_ON()
         return reinterpret_cast<InternedDataType*>(entry.second.get());
@@ -210,7 +213,7 @@
         entry.first = FieldNumber;
         entry.second.reset(new InternedDataType());
 #if PERFETTO_DCHECK_IS_ON()
-        entry.second->type_id_ = PERFETTO_INTERNAL_TYPE_IDENTIFIER;
+        entry.second->type_id_ = PERFETTO_DEBUG_FUNCTION_IDENTIFIER();
 #endif  // PERFETTO_DCHECK_IS_ON()
         return reinterpret_cast<InternedDataType*>(entry.second.get());
       }
diff --git a/protos/perfetto/config/ftrace/ftrace_config.proto b/protos/perfetto/config/ftrace/ftrace_config.proto
index 56dc0d0..a612d89 100644
--- a/protos/perfetto/config/ftrace/ftrace_config.proto
+++ b/protos/perfetto/config/ftrace/ftrace_config.proto
@@ -27,16 +27,12 @@
   optional uint32 buffer_size_kb = 10;
   optional uint32 drain_period_ms = 11;
 
-  // Configuration for compact encoding of scheduler events. If enabled, this
-  // records a small subset of fields of selected scheduling events, and
-  // encodes them in a denser proto format than normal. This is useful due to
-  // scheduling events being abundant in a typical trace, and dominating its
-  // size via a combination of unnecessary data being retained, and proto
-  // format overheads.
-  // TODO(rsavitski): unstable, do not use.
+  // Configuration for compact encoding of scheduler events. When enabled (and
+  // recording the relevant ftrace events), specific high-volume events are
+  // encoded in a denser format than normal.
   message CompactSchedConfig {
-    // If true, and sched_switch ftrace event is enabled, record those events
-    // in the compact format.
+    // If true, and sched_switch or sched_waking ftrace events are enabled,
+    // record those events in the compact format.
     optional bool enabled = 1;
   }
   optional CompactSchedConfig compact_sched = 12;
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 49e5a66..23c33eb 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -631,16 +631,12 @@
   optional uint32 buffer_size_kb = 10;
   optional uint32 drain_period_ms = 11;
 
-  // Configuration for compact encoding of scheduler events. If enabled, this
-  // records a small subset of fields of selected scheduling events, and
-  // encodes them in a denser proto format than normal. This is useful due to
-  // scheduling events being abundant in a typical trace, and dominating its
-  // size via a combination of unnecessary data being retained, and proto
-  // format overheads.
-  // TODO(rsavitski): unstable, do not use.
+  // Configuration for compact encoding of scheduler events. When enabled (and
+  // recording the relevant ftrace events), specific high-volume events are
+  // encoded in a denser format than normal.
   message CompactSchedConfig {
-    // If true, and sched_switch ftrace event is enabled, record those events
-    // in the compact format.
+    // If true, and sched_switch or sched_waking ftrace events are enabled,
+    // record those events in the compact format.
     optional bool enabled = 1;
   }
   optional CompactSchedConfig compact_sched = 12;
@@ -1186,6 +1182,10 @@
   // process.
   optional bool block_client = 9;
 
+  // If set, stop the trace session after blocking the client for this
+  // timeout. Needs to be larger than 100 us, otherwise no retries are done.
+  optional uint32 block_client_timeout_us = 14;
+
   // Do not profile processes from startup, only match already running
   // processes.
   //
diff --git a/protos/perfetto/config/profiling/heapprofd_config.proto b/protos/perfetto/config/profiling/heapprofd_config.proto
index 2d208d3..0f3a30f 100644
--- a/protos/perfetto/config/profiling/heapprofd_config.proto
+++ b/protos/perfetto/config/profiling/heapprofd_config.proto
@@ -81,6 +81,10 @@
   // process.
   optional bool block_client = 9;
 
+  // If set, stop the trace session after blocking the client for this
+  // timeout. Needs to be larger than 100 us, otherwise no retries are done.
+  optional uint32 block_client_timeout_us = 14;
+
   // Do not profile processes from startup, only match already running
   // processes.
   //
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 9d1d001..41353b0 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -20,6 +20,7 @@
     "cpu_metric.proto",
     "heap_profile_callsites.proto",
     "ion_metric.proto",
+    "java_heap_stats.proto",
     "lmk_metric.proto",
     "mem_metric.proto",
     "mem_unagg_metric.proto",
diff --git a/protos/perfetto/metrics/android/heap_profile_callsites.proto b/protos/perfetto/metrics/android/heap_profile_callsites.proto
index ce0bdca..d19bba3 100644
--- a/protos/perfetto/metrics/android/heap_profile_callsites.proto
+++ b/protos/perfetto/metrics/android/heap_profile_callsites.proto
@@ -51,10 +51,16 @@
   }
 
   // Callsites per process instance.
+  // Next id: 6
   message InstanceStats {
     optional uint32 pid = 1;
     optional string process_name = 2;
     repeated Callsite callsites = 3;
+
+    // Bytes allocated via malloc but not freed.
+    optional int64 profile_delta_bytes = 4;
+    // Bytes allocated via malloc irrespective of whether they were freed.
+    optional int64 profile_total_bytes = 5;
   }
 
   repeated InstanceStats instance_stats = 1;
diff --git a/protos/perfetto/metrics/android/java_heap_stats.proto b/protos/perfetto/metrics/android/java_heap_stats.proto
new file mode 100644
index 0000000..a5e1513
--- /dev/null
+++ b/protos/perfetto/metrics/android/java_heap_stats.proto
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+message JavaHeapStats {
+  message Sample {
+    optional int64 ts = 1;
+    optional int64 heap_size = 2;
+    optional int64 reachable_heap_size = 3;
+  }
+
+  // Heap stats per process. One sample per dump (can be > 1 if continuous
+  // dump is enabled).
+  message InstanceStats {
+    optional uint32 upid = 1;
+    optional string process_name = 2;
+    repeated Sample samples = 3;
+  }
+
+  repeated InstanceStats instance_stats = 1;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index a197a38..508e830 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -31,6 +31,7 @@
 import "protos/perfetto/metrics/android/heap_profile_callsites.proto";
 import "protos/perfetto/metrics/android/package_list.proto";
 import "protos/perfetto/metrics/android/unsymbolized_frames.proto";
+import "protos/perfetto/metrics/android/java_heap_stats.proto";
 
 // Trace processor metadata (taken from the stats table schema and contents).
 // TODO: perhaps add the other columns once we have enum support
@@ -48,7 +49,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 17
+// Next id: 18
 message TraceMetrics {
   reserved 4, 13, 14;
 
@@ -94,6 +95,9 @@
   // Returns stack frames missing symbols.
   optional UnsymbolizedFrames unsymbolized_frames = 15;
 
+  // If the trace contains a heap graph, output allocation statistics.
+  optional JavaHeapStats java_heap_stats = 17;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto b/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
index e61377a..1d69b1f 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
@@ -36,8 +36,10 @@
   // a subset of events & their fields is recorded.
   // All fields (except comms) are stored in a structure-of-arrays form, one
   // entry in each repeated field per event.
-  // TODO(rsavitski): unstable, do not use.
   message CompactSched {
+    // Interned table of unique strings for this bundle.
+    repeated string intern_table = 5;
+
     // Delta-encoded timestamps across all sched_switch events within this
     // bundle. The first is absolute, each next one is relative to its
     // predecessor.
@@ -45,12 +47,20 @@
     repeated int64 switch_prev_state = 2 [packed = true];
     repeated int32 switch_next_pid = 3 [packed = true];
     repeated int32 switch_next_prio = 4 [packed = true];
-
-    // Interned table of unique comm strings for this bundle.
-    repeated string switch_next_comm_table = 5;
-    // One per event, index into |switch_next_comm_table| corresponding to the
+    // One per event, index into |intern_table| corresponding to the
     // next_comm field of the event.
     repeated uint32 switch_next_comm_index = 6 [packed = true];
+
+    // Delta-encoded timestamps across all sched_waking events within this
+    // bundle. The first is absolute, each next one is relative to its
+    // predecessor.
+    repeated uint64 waking_timestamp = 7 [packed = true];
+    repeated int32 waking_pid = 8 [packed = true];
+    repeated int32 waking_target_cpu = 9 [packed = true];
+    repeated int32 waking_prio = 10 [packed = true];
+    // One per event, index into |intern_table| corresponding to the
+    // comm field of the event.
+    repeated uint32 waking_comm_index = 11 [packed = true];
   }
   optional CompactSched compact_sched = 4;
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index f59d182..2585ba1 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -2316,8 +2316,10 @@
   // a subset of events & their fields is recorded.
   // All fields (except comms) are stored in a structure-of-arrays form, one
   // entry in each repeated field per event.
-  // TODO(rsavitski): unstable, do not use.
   message CompactSched {
+    // Interned table of unique strings for this bundle.
+    repeated string intern_table = 5;
+
     // Delta-encoded timestamps across all sched_switch events within this
     // bundle. The first is absolute, each next one is relative to its
     // predecessor.
@@ -2325,12 +2327,20 @@
     repeated int64 switch_prev_state = 2 [packed = true];
     repeated int32 switch_next_pid = 3 [packed = true];
     repeated int32 switch_next_prio = 4 [packed = true];
-
-    // Interned table of unique comm strings for this bundle.
-    repeated string switch_next_comm_table = 5;
-    // One per event, index into |switch_next_comm_table| corresponding to the
+    // One per event, index into |intern_table| corresponding to the
     // next_comm field of the event.
     repeated uint32 switch_next_comm_index = 6 [packed = true];
+
+    // Delta-encoded timestamps across all sched_waking events within this
+    // bundle. The first is absolute, each next one is relative to its
+    // predecessor.
+    repeated uint64 waking_timestamp = 7 [packed = true];
+    repeated int32 waking_pid = 8 [packed = true];
+    repeated int32 waking_target_cpu = 9 [packed = true];
+    repeated int32 waking_prio = 10 [packed = true];
+    // One per event, index into |intern_table| corresponding to the
+    // comm field of the event.
+    repeated uint32 waking_comm_index = 11 [packed = true];
   }
   optional CompactSched compact_sched = 4;
 }
@@ -4457,16 +4467,12 @@
   optional uint32 buffer_size_kb = 10;
   optional uint32 drain_period_ms = 11;
 
-  // Configuration for compact encoding of scheduler events. If enabled, this
-  // records a small subset of fields of selected scheduling events, and
-  // encodes them in a denser proto format than normal. This is useful due to
-  // scheduling events being abundant in a typical trace, and dominating its
-  // size via a combination of unnecessary data being retained, and proto
-  // format overheads.
-  // TODO(rsavitski): unstable, do not use.
+  // Configuration for compact encoding of scheduler events. When enabled (and
+  // recording the relevant ftrace events), specific high-volume events are
+  // encoded in a denser format than normal.
   message CompactSchedConfig {
-    // If true, and sched_switch ftrace event is enabled, record those events
-    // in the compact format.
+    // If true, and sched_switch or sched_waking ftrace events are enabled,
+    // record those events in the compact format.
     optional bool enabled = 1;
   }
   optional CompactSchedConfig compact_sched = 12;
@@ -5012,6 +5018,10 @@
   // process.
   optional bool block_client = 9;
 
+  // If set, stop the trace session after blocking the client for this
+  // timeout. Needs to be larger than 100 us, otherwise no retries are done.
+  optional uint32 block_client_timeout_us = 14;
+
   // Do not profile processes from startup, only match already running
   // processes.
   //
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 9f53d4b..0e7a0d0 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -122,7 +122,6 @@
 
   sources = [
     "circular_queue_unittest.cc",
-    "copyable_ptr_unittest.cc",
     "no_destructor_unittest.cc",
     "optional_unittest.cc",
     "paged_memory_unittest.cc",
diff --git a/src/ipc/buffered_frame_deserializer_fuzzer.cc b/src/ipc/buffered_frame_deserializer_fuzzer.cc
index 61a818d..086ccf7 100644
--- a/src/ipc/buffered_frame_deserializer_fuzzer.cc
+++ b/src/ipc/buffered_frame_deserializer_fuzzer.cc
@@ -20,8 +20,6 @@
 #include "perfetto/ext/base/utils.h"
 #include "src/ipc/buffered_frame_deserializer.h"
 
-#include "protos/perfetto/ipc/wire_protocol.pb.h"
-
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
diff --git a/src/ipc/client_impl.cc b/src/ipc/client_impl.cc
index b563d95..4d32e6e 100644
--- a/src/ipc/client_impl.cc
+++ b/src/ipc/client_impl.cc
@@ -73,7 +73,7 @@
     return service_proxy->OnConnect(false /* success */);
   }
   QueuedRequest qr;
-  qr.type = Frame::kMsgBindService;
+  qr.type = Frame::kMsgBindServiceFieldNumber;
   qr.request_id = request_id;
   qr.service_proxy = service_proxy;
   queued_requests_.emplace(request_id, std::move(qr));
@@ -107,7 +107,7 @@
   if (drop_reply)
     return 0;
   QueuedRequest qr;
-  qr.type = Frame::kMsgInvokeMethod;
+  qr.type = Frame::kMsgInvokeMethodFieldNumber;
   qr.request_id = request_id;
   qr.method_name = method_name;
   qr.service_proxy = std::move(service_proxy);
@@ -187,23 +187,23 @@
   QueuedRequest req = std::move(queued_requests_it->second);
   queued_requests_.erase(queued_requests_it);
 
-  if (req.type == Frame::kMsgBindService &&
-      frame.msg_case() == Frame::kMsgBindServiceReply) {
+  if (req.type == Frame::kMsgBindServiceFieldNumber &&
+      frame.has_msg_bind_service_reply()) {
     return OnBindServiceReply(std::move(req), frame.msg_bind_service_reply());
   }
-  if (req.type == Frame::kMsgInvokeMethod &&
-      frame.msg_case() == Frame::kMsgInvokeMethodReply) {
+  if (req.type == Frame::kMsgInvokeMethodFieldNumber &&
+      frame.has_msg_invoke_method_reply()) {
     return OnInvokeMethodReply(std::move(req), frame.msg_invoke_method_reply());
   }
-  if (frame.msg_case() == Frame::kMsgRequestError) {
+  if (frame.has_msg_request_error()) {
     PERFETTO_DLOG("Host error: %s", frame.msg_request_error().error().c_str());
     return;
   }
 
   PERFETTO_DLOG(
-      "OnFrameReceived() request msg_type=%d, received msg_type=%d in reply to "
+      "OnFrameReceived() request type=%d, received unknown frame in reply to "
       "request_id=%" PRIu64,
-      req.type, frame.msg_case(), static_cast<uint64_t>(frame.request_id()));
+      req.type, static_cast<uint64_t>(frame.request_id()));
 }
 
 void ClientImpl::OnBindServiceReply(QueuedRequest req,
diff --git a/src/ipc/client_impl_unittest.cc b/src/ipc/client_impl_unittest.cc
index e8e634e..56039d9 100644
--- a/src/ipc/client_impl_unittest.cc
+++ b/src/ipc/client_impl_unittest.cc
@@ -141,7 +141,7 @@
   }
 
   void OnFrameReceived(const Frame& req) {
-    if (req.msg_case() == Frame::kMsgBindService) {
+    if (req.has_msg_bind_service()) {
       auto svc_it = services.find(req.msg_bind_service().service_name());
       ASSERT_NE(services.end(), svc_it);
       const FakeService& svc = *svc_it->second;
@@ -155,7 +155,7 @@
         method->set_id(method_it.second->id);
       }
       Reply(reply);
-    } else if (req.msg_case() == Frame::kMsgInvokeMethod) {
+    } else if (req.has_msg_invoke_method()) {
       // Lookup the service and method.
       bool has_more = false;
       do {
diff --git a/src/ipc/host_impl.cc b/src/ipc/host_impl.cc
index 0a56b85..1158fb8 100644
--- a/src/ipc/host_impl.cc
+++ b/src/ipc/host_impl.cc
@@ -126,13 +126,12 @@
 
 void HostImpl::OnReceivedFrame(ClientConnection* client,
                                const Frame& req_frame) {
-  if (req_frame.msg_case() == Frame::kMsgBindService)
+  if (req_frame.has_msg_bind_service())
     return OnBindService(client, req_frame);
-  if (req_frame.msg_case() == Frame::kMsgInvokeMethod)
+  if (req_frame.has_msg_invoke_method())
     return OnInvokeMethod(client, req_frame);
 
-  PERFETTO_DLOG("Received invalid RPC frame %u from client %" PRIu64,
-                req_frame.msg_case(), client->id);
+  PERFETTO_DLOG("Received invalid RPC frame from client %" PRIu64, client->id);
   Frame reply_frame;
   reply_frame.set_request_id(req_frame.request_id());
   reply_frame.mutable_msg_request_error()->set_error("unknown request");
diff --git a/src/ipc/host_impl_unittest.cc b/src/ipc/host_impl_unittest.cc
index 0624cd7..4e9216b 100644
--- a/src/ipc/host_impl_unittest.cc
+++ b/src/ipc/host_impl_unittest.cc
@@ -139,16 +139,16 @@
     while (std::unique_ptr<Frame> frame = frame_deserializer_.PopNextFrame()) {
       ASSERT_EQ(1u, requests_.count(frame->request_id()));
       EXPECT_EQ(0, requests_[frame->request_id()]++);
-      if (frame->msg_case() == Frame::kMsgBindServiceReply) {
+      if (frame->has_msg_bind_service_reply()) {
         if (frame->msg_bind_service_reply().success())
           last_bound_service_id_ = frame->msg_bind_service_reply().service_id();
         return OnServiceBound(frame->msg_bind_service_reply());
       }
-      if (frame->msg_case() == Frame::kMsgInvokeMethodReply)
+      if (frame->has_msg_invoke_method_reply())
         return OnInvokeMethodReply(frame->msg_invoke_method_reply());
-      if (frame->msg_case() == Frame::kMsgRequestError)
+      if (frame->has_msg_request_error())
         return OnRequestError();
-      FAIL() << "Unexpected frame received from host " << frame->msg_case();
+      FAIL() << "Unexpected frame received from host";
     }
   }
 
diff --git a/src/perfetto_cmd/perfetto_config.descriptor.h b/src/perfetto_cmd/perfetto_config.descriptor.h
index 666bac9..0ce692b 100644
--- a/src/perfetto_cmd/perfetto_config.descriptor.h
+++ b/src/perfetto_cmd/perfetto_config.descriptor.h
@@ -12,15 +12,15 @@
 // SHA1(tools/gen_binary_descriptors)
 // 192b582ae52bb07b3d3ba66a94bcfd3127a5f42f
 // SHA1(protos/perfetto/config/perfetto_config.proto)
-// 267f9d49ca0c50158496fd5dc90ab15480500724
+// edb4dea1679b47b02d9296a9db0cc4136f294edf
 
 // This is the proto PerfettoConfig encoded as a ProtoFileDescriptor to allow
 // for reflection without libprotobuf full/non-lite protos.
 
 namespace perfetto {
 
-constexpr std::array<uint8_t, 16768> kPerfettoConfigDescriptor{
-    {0x0a, 0xfc, 0x82, 0x01, 0x0a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+constexpr std::array<uint8_t, 16823> kPerfettoConfigDescriptor{
+    {0x0a, 0xb3, 0x83, 0x01, 0x0a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
      0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x63, 0x6f,
      0x6e, 0x66, 0x69, 0x67, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f,
@@ -995,7 +995,7 @@
      0x1c, 0x0a, 0x18, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49,
      0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x4c,
      0x41, 0x54, 0x45, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, 0x22,
-     0xed, 0x04, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x66,
+     0xa4, 0x05, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x66,
      0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x73,
      0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x74, 0x65,
      0x72, 0x76, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01,
@@ -1027,397 +1027,401 @@
      0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
      0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52,
      0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
-     0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
-     0x75, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f,
-     0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x6e,
-     0x6f, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20,
-     0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x52, 0x75, 0x6e, 0x6e, 0x69,
-     0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x61,
-     0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c,
-     0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x64, 0x6c, 0x65, 0x41, 0x6c,
-     0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a,
-     0x0b, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x61, 0x74, 0x5f, 0x6d, 0x61, 0x78,
-     0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x75, 0x6d, 0x70,
-     0x41, 0x74, 0x4d, 0x61, 0x78, 0x1a, 0x64, 0x0a, 0x14, 0x43, 0x6f, 0x6e,
-     0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43,
-     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x75, 0x6d,
-     0x70, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x05,
-     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x75, 0x6d, 0x70, 0x50, 0x68,
-     0x61, 0x73, 0x65, 0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x75, 0x6d,
-     0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d,
-     0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x64, 0x75, 0x6d,
-     0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x22,
-     0x9f, 0x02, 0x0a, 0x0f, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x70, 0x72, 0x6f,
-     0x66, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6d, 0x64, 0x6c, 0x69,
-     0x6e, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65,
-     0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28,
-     0x04, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x6b, 0x0a, 0x16, 0x63, 0x6f,
-     0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x75, 0x6d,
-     0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x35, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76,
-     0x61, 0x48, 0x70, 0x72, 0x6f, 0x66, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-     0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44,
-     0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x63,
-     0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d,
-     0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x64, 0x0a, 0x14, 0x43,
-     0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d,
-     0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0d, 0x64,
-     0x75, 0x6d, 0x70, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73,
-     0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x75, 0x6d, 0x70,
-     0x50, 0x68, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x64,
-     0x75, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
-     0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x64,
-     0x75, 0x6d, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d,
-     0x73, 0x22, 0xb8, 0x01, 0x0a, 0x10, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2a,
-     0x0a, 0x11, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x65,
-     0x72, 0x69, 0x6f, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x0f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x50, 0x65,
-     0x72, 0x69, 0x6f, 0x64, 0x4e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f,
-     0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20,
-     0x03, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-     0x49, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x15, 0x69, 0x6e, 0x73, 0x74, 0x72,
-     0x75, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x61, 0x6d, 0x70,
-     0x6c, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14,
-     0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64,
-     0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x22, 0x0a, 0x0d,
-     0x66, 0x69, 0x78, 0x5f, 0x67, 0x70, 0x75, 0x5f, 0x63, 0x6c, 0x6f, 0x63,
-     0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x66, 0x69, 0x78,
-     0x47, 0x70, 0x75, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x44, 0x0a, 0x12,
-     0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74,
-     0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61,
-     0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66,
-     0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
-     0x11, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65,
-     0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2a, 0x8e, 0x01, 0x0a, 0x0c, 0x41,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12,
-     0x0f, 0x0a, 0x0b, 0x4c, 0x49, 0x44, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55,
-     0x4c, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f,
-     0x52, 0x41, 0x44, 0x49, 0x4f, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4c,
-     0x49, 0x44, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x53, 0x10, 0x02, 0x12,
-     0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45,
-     0x4d, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f, 0x43,
-     0x52, 0x41, 0x53, 0x48, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49,
-     0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x53, 0x10, 0x05, 0x12, 0x10, 0x0a,
-     0x0c, 0x4c, 0x49, 0x44, 0x5f, 0x53, 0x45, 0x43, 0x55, 0x52, 0x49, 0x54,
-     0x59, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f, 0x4b,
-     0x45, 0x52, 0x4e, 0x45, 0x4c, 0x10, 0x07, 0x2a, 0x9b, 0x01, 0x0a, 0x12,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6f, 0x67, 0x50, 0x72,
-     0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x52,
-     0x49, 0x4f, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
-     0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x49, 0x4f,
-     0x5f, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a,
-     0x0c, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x56, 0x45, 0x52, 0x42, 0x4f, 0x53,
-     0x45, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f,
-     0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x50,
-     0x52, 0x49, 0x4f, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x04, 0x12, 0x0d,
-     0x0a, 0x09, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x10,
-     0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x45, 0x52,
-     0x52, 0x4f, 0x52, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49,
-     0x4f, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x07, 0x2a, 0xbf, 0x06,
-     0x0a, 0x0f, 0x4d, 0x65, 0x6d, 0x69, 0x6e, 0x66, 0x6f, 0x43, 0x6f, 0x75,
-     0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d,
-     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
-     0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x54, 0x4f,
-     0x54, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d,
-     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x46, 0x52, 0x45,
-     0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
-     0x46, 0x4f, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x41, 0x56, 0x41, 0x49, 0x4c,
-     0x41, 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52,
-     0x53, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
-     0x46, 0x4f, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x44, 0x10, 0x05, 0x12,
-     0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53,
-     0x57, 0x41, 0x50, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x44, 0x10, 0x06,
-     0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
-     0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10,
-     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43,
-     0x54, 0x49, 0x56, 0x45, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45,
-     0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x09, 0x12, 0x19, 0x0a, 0x15, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54,
-     0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x17,
-     0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x43,
-     0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x0b, 0x12,
-     0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x49,
-     0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45,
-     0x10, 0x0c, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
-     0x4f, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c,
-     0x45, 0x10, 0x0d, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
-     0x46, 0x4f, 0x5f, 0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x0e,
+     0x12, 0x35, 0x0a, 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6c,
+     0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
+     0x5f, 0x75, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x62,
+     0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69,
+     0x6d, 0x65, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e,
+     0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x0a, 0x20,
+     0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74,
+     0x75, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x72, 0x75, 0x6e,
+     0x6e, 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
+     0x6e, 0x6f, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a,
+     0x10, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61,
+     0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52,
+     0x0f, 0x69, 0x64, 0x6c, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
+     0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x75, 0x6d, 0x70,
+     0x5f, 0x61, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x0d, 0x20, 0x01, 0x28,
+     0x08, 0x52, 0x09, 0x64, 0x75, 0x6d, 0x70, 0x41, 0x74, 0x4d, 0x61, 0x78,
+     0x1a, 0x64, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x6f,
+     0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+     0x12, 0x22, 0x0a, 0x0d, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x70, 0x68, 0x61,
+     0x73, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52,
+     0x0b, 0x64, 0x75, 0x6d, 0x70, 0x50, 0x68, 0x61, 0x73, 0x65, 0x4d, 0x73,
+     0x12, 0x28, 0x0a, 0x10, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x74,
+     0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01,
+     0x28, 0x0d, 0x52, 0x0e, 0x64, 0x75, 0x6d, 0x70, 0x49, 0x6e, 0x74, 0x65,
+     0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x22, 0x9f, 0x02, 0x0a, 0x0f, 0x4a,
+     0x61, 0x76, 0x61, 0x48, 0x70, 0x72, 0x6f, 0x66, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
+     0x73, 0x5f, 0x63, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20,
+     0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x43, 0x6d, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70,
+     0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x03, 0x70, 0x69,
+     0x64, 0x12, 0x6b, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75,
+     0x6f, 0x75, 0x73, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x6e,
+     0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x70, 0x72, 0x6f,
+     0x66, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6f, 0x6e, 0x74,
+     0x69, 0x6e, 0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f,
+     0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e,
+     0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x1a, 0x64, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e,
+     0x75, 0x6f, 0x75, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x43, 0x6f, 0x6e, 0x66,
+     0x69, 0x67, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x70,
+     0x68, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x0d, 0x52, 0x0b, 0x64, 0x75, 0x6d, 0x70, 0x50, 0x68, 0x61, 0x73, 0x65,
+     0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69,
+     0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x18, 0x02,
+     0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x64, 0x75, 0x6d, 0x70, 0x49, 0x6e,
+     0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x22, 0xb8, 0x01, 0x0a,
+     0x10, 0x47, 0x70, 0x75, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x43,
+     0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f,
+     0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x6f,
+     0x75, 0x6e, 0x74, 0x65, 0x72, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x4e,
+     0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
+     0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a,
+     0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x33,
+     0x0a, 0x15, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+     0x65, 0x64, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18,
+     0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, 0x6e, 0x73, 0x74, 0x72,
+     0x75, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x53, 0x61, 0x6d, 0x70, 0x6c,
+     0x69, 0x6e, 0x67, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x69, 0x78, 0x5f, 0x67,
+     0x70, 0x75, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01,
+     0x28, 0x08, 0x52, 0x0b, 0x66, 0x69, 0x78, 0x47, 0x70, 0x75, 0x43, 0x6c,
+     0x6f, 0x63, 0x6b, 0x22, 0x44, 0x0a, 0x12, 0x50, 0x61, 0x63, 0x6b, 0x61,
+     0x67, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+     0x67, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
+     0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
+     0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x70, 0x61, 0x63, 0x6b,
+     0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65,
+     0x72, 0x2a, 0x8e, 0x01, 0x0a, 0x0c, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x49,
+     0x44, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12,
+     0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x41, 0x44, 0x49, 0x4f,
+     0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x56,
+     0x45, 0x4e, 0x54, 0x53, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x49,
+     0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x10, 0x03, 0x12, 0x0d,
+     0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f, 0x43, 0x52, 0x41, 0x53, 0x48, 0x10,
+     0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x44, 0x5f, 0x53, 0x54, 0x41,
+     0x54, 0x53, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x44, 0x5f,
+     0x53, 0x45, 0x43, 0x55, 0x52, 0x49, 0x54, 0x59, 0x10, 0x06, 0x12, 0x0e,
+     0x0a, 0x0a, 0x4c, 0x49, 0x44, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c,
+     0x10, 0x07, 0x2a, 0x9b, 0x01, 0x0a, 0x12, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x4c, 0x6f, 0x67, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74,
+     0x79, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x55, 0x4e,
+     0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
+     0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x55, 0x4e, 0x55, 0x53,
+     0x45, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x49, 0x4f,
+     0x5f, 0x56, 0x45, 0x52, 0x42, 0x4f, 0x53, 0x45, 0x10, 0x02, 0x12, 0x0e,
+     0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47,
+     0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x49,
+     0x4e, 0x46, 0x4f, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x49,
+     0x4f, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x05, 0x12, 0x0e, 0x0a, 0x0a,
+     0x50, 0x52, 0x49, 0x4f, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x06,
+     0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x49, 0x4f, 0x5f, 0x46, 0x41, 0x54,
+     0x41, 0x4c, 0x10, 0x07, 0x2a, 0xbf, 0x06, 0x0a, 0x0f, 0x4d, 0x65, 0x6d,
+     0x69, 0x6e, 0x66, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73,
+     0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
+     0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+     0x00, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10, 0x01,
+     0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
+     0x4d, 0x45, 0x4d, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x10, 0x02, 0x12, 0x19,
+     0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x45,
+     0x4d, 0x5f, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10,
+     0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x53, 0x10, 0x04, 0x12, 0x12,
+     0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x41,
+     0x43, 0x48, 0x45, 0x44, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45,
+     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x43,
+     0x41, 0x43, 0x48, 0x45, 0x44, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x4d,
+     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
+     0x45, 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
+     0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10,
+     0x08, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e,
+     0x10, 0x09, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x41,
+     0x4e, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d,
+     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f,
+     0x46, 0x49, 0x4c, 0x45, 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45,
+     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49,
+     0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x0c, 0x12, 0x17, 0x0a,
+     0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x55, 0x4e, 0x45,
+     0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0d, 0x12, 0x13,
+     0x0a, 0x0f, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x4c,
+     0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x16, 0x0a, 0x12, 0x4d,
+     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f,
+     0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10, 0x0f, 0x12, 0x15, 0x0a, 0x11, 0x4d,
+     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f,
+     0x46, 0x52, 0x45, 0x45, 0x10, 0x10, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x45,
+     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x59, 0x10,
+     0x11, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x12,
      0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
-     0x53, 0x57, 0x41, 0x50, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10, 0x0f,
-     0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
-     0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x10, 0x10, 0x12,
-     0x11, 0x0a, 0x0d, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x44,
-     0x49, 0x52, 0x54, 0x59, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x42,
-     0x41, 0x43, 0x4b, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x4d,
-     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x50, 0x41,
-     0x47, 0x45, 0x53, 0x10, 0x13, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d,
-     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4d, 0x41, 0x50, 0x50, 0x45, 0x44, 0x10,
-     0x14, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
-     0x5f, 0x53, 0x48, 0x4d, 0x45, 0x4d, 0x10, 0x15, 0x12, 0x10, 0x0a, 0x0c,
-     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42,
-     0x10, 0x16, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
-     0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x52, 0x45, 0x43, 0x4c, 0x41,
-     0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x17, 0x12, 0x1e, 0x0a, 0x1a,
-     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42,
-     0x5f, 0x55, 0x4e, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42,
-     0x4c, 0x45, 0x10, 0x18, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d, 0x49,
-     0x4e, 0x46, 0x4f, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, 0x53,
-     0x54, 0x41, 0x43, 0x4b, 0x10, 0x19, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54,
-     0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x1a, 0x12, 0x18, 0x0a, 0x14, 0x4d,
-     0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49,
-     0x54, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x10, 0x1b, 0x12, 0x17, 0x0a,
-     0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4f, 0x4d,
-     0x4d, 0x49, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x53, 0x10, 0x1c, 0x12, 0x19,
-     0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56, 0x4d,
-     0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10,
-     0x1d, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
-     0x5f, 0x56, 0x4d, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x55, 0x53, 0x45,
-     0x44, 0x10, 0x1e, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
-     0x46, 0x4f, 0x5f, 0x56, 0x4d, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x43,
-     0x48, 0x55, 0x4e, 0x4b, 0x10, 0x1f, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45,
-     0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4d, 0x41, 0x5f, 0x54, 0x4f,
-     0x54, 0x41, 0x4c, 0x10, 0x20, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d,
-     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4d, 0x41, 0x5f, 0x46, 0x52, 0x45,
-     0x45, 0x10, 0x21, 0x2a, 0xc6, 0x15, 0x0a, 0x0e, 0x56, 0x6d, 0x73, 0x74,
-     0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x16,
-     0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x53,
-     0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18,
-     0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
-     0x46, 0x52, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x01,
-     0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
-     0x52, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x42, 0x41, 0x54, 0x43,
-     0x48, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56,
-     0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x43,
-     0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x04, 0x12,
-     0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
-     0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49,
-     0x4c, 0x45, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45,
-     0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x06, 0x12, 0x19, 0x0a, 0x15, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x55, 0x4e, 0x45,
-     0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x07, 0x12, 0x13,
-     0x0a, 0x0f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
-     0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4e, 0x4f,
-     0x4e, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x09, 0x12, 0x14, 0x0a,
-     0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4d,
-     0x41, 0x50, 0x50, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x49, 0x4c,
-     0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x0b, 0x12, 0x13, 0x0a,
-     0x0f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44,
-     0x49, 0x52, 0x54, 0x59, 0x10, 0x0c, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49, 0x54,
-     0x45, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x0d, 0x12, 0x1e, 0x0a, 0x1a, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x41,
+     0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x13,
+     0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
+     0x4d, 0x41, 0x50, 0x50, 0x45, 0x44, 0x10, 0x14, 0x12, 0x11, 0x0a, 0x0d,
+     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x48, 0x4d, 0x45,
+     0x4d, 0x10, 0x15, 0x12, 0x10, 0x0a, 0x0c, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
+     0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x10, 0x16, 0x12, 0x1c, 0x0a,
+     0x18, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41,
      0x42, 0x5f, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c,
-     0x45, 0x10, 0x0e, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x55, 0x4e,
-     0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10,
-     0x0f, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x4e, 0x52, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x41, 0x42, 0x4c,
-     0x45, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x10, 0x12, 0x1a, 0x0a,
-     0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4b,
+     0x45, 0x10, 0x17, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x45, 0x4d, 0x49, 0x4e,
+     0x46, 0x4f, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x55, 0x4e, 0x52, 0x45,
+     0x43, 0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x18, 0x12,
+     0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4b,
      0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x43, 0x4b, 0x10,
-     0x11, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x4e, 0x52, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x10,
-     0x12, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x4e, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10,
-     0x13, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x4e, 0x52, 0x5f, 0x42, 0x4f, 0x55, 0x4e, 0x43, 0x45, 0x10, 0x14, 0x12,
-     0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
-     0x5f, 0x56, 0x4d, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x57, 0x52, 0x49, 0x54,
-     0x45, 0x10, 0x15, 0x12, 0x26, 0x0a, 0x22, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x56, 0x4d, 0x53, 0x43, 0x41, 0x4e, 0x5f,
-     0x49, 0x4d, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x45,
-     0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x16, 0x12, 0x1c, 0x0a, 0x18, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49,
-     0x54, 0x45, 0x42, 0x41, 0x43, 0x4b, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x10,
-     0x17, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x19, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x50, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53,
+     0x10, 0x1a, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46,
+     0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x5f, 0x4c, 0x49, 0x4d,
+     0x49, 0x54, 0x10, 0x1b, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x45, 0x4d, 0x49,
+     0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x45, 0x44,
+     0x5f, 0x41, 0x53, 0x10, 0x1c, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x4d,
+     0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56, 0x4d, 0x41, 0x4c, 0x4c, 0x4f, 0x43,
+     0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10, 0x1d, 0x12, 0x18, 0x0a, 0x14,
+     0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56, 0x4d, 0x41, 0x4c,
+     0x4c, 0x4f, 0x43, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x10, 0x1e, 0x12, 0x19,
+     0x0a, 0x15, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x56, 0x4d,
+     0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x10,
+     0x1f, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f,
+     0x5f, 0x43, 0x4d, 0x41, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x10, 0x20,
+     0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x4d, 0x49, 0x4e, 0x46, 0x4f, 0x5f,
+     0x43, 0x4d, 0x41, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x10, 0x21, 0x2a, 0xc6,
+     0x15, 0x0a, 0x0e, 0x56, 0x6d, 0x73, 0x74, 0x61, 0x74, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
+     0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x5f,
+     0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4c, 0x4c,
+     0x4f, 0x43, 0x5f, 0x42, 0x41, 0x54, 0x43, 0x48, 0x10, 0x02, 0x12, 0x1b,
+     0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
+     0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x4e, 0x4f,
+     0x4e, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f,
+     0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x4e, 0x41, 0x43,
+     0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x05, 0x12,
+     0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
+     0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45,
+     0x10, 0x06, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41,
+     0x42, 0x4c, 0x45, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4d, 0x4c, 0x4f, 0x43, 0x4b,
+     0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x47,
+     0x45, 0x53, 0x10, 0x09, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4d, 0x41, 0x50, 0x50, 0x45, 0x44,
+     0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x50, 0x41, 0x47,
+     0x45, 0x53, 0x10, 0x0b, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x59, 0x10,
+     0x0c, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x42, 0x41, 0x43, 0x4b,
+     0x10, 0x0d, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x5f, 0x52, 0x45, 0x43,
+     0x4c, 0x41, 0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0e, 0x12, 0x20,
+     0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
+     0x53, 0x4c, 0x41, 0x42, 0x5f, 0x55, 0x4e, 0x52, 0x45, 0x43, 0x4c, 0x41,
+     0x49, 0x4d, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0f, 0x12, 0x1e, 0x0a, 0x1a,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x50, 0x41,
+     0x47, 0x45, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x41, 0x47,
+     0x45, 0x53, 0x10, 0x10, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c,
+     0x5f, 0x53, 0x54, 0x41, 0x43, 0x4b, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x4f, 0x56,
+     0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x55, 0x4e,
+     0x53, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x13, 0x12, 0x14, 0x0a, 0x10,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x42, 0x4f,
+     0x55, 0x4e, 0x43, 0x45, 0x10, 0x14, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x56, 0x4d, 0x53, 0x43,
+     0x41, 0x4e, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x15, 0x12, 0x26,
+     0x0a, 0x22, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
+     0x56, 0x4d, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x49, 0x4d, 0x4d, 0x45, 0x44,
+     0x49, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d,
+     0x10, 0x16, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x42, 0x41, 0x43,
+     0x4b, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x53,
+     0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x10,
+     0x18, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
      0x4e, 0x52, 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f,
-     0x41, 0x4e, 0x4f, 0x4e, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x53, 0x4f, 0x4c,
-     0x41, 0x54, 0x45, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x19, 0x12,
-     0x13, 0x0a, 0x0f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
-     0x5f, 0x53, 0x48, 0x4d, 0x45, 0x4d, 0x10, 0x1a, 0x12, 0x15, 0x0a, 0x11,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49,
-     0x52, 0x54, 0x49, 0x45, 0x44, 0x10, 0x1b, 0x12, 0x15, 0x0a, 0x11, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49,
-     0x54, 0x54, 0x45, 0x4e, 0x10, 0x1c, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x50, 0x41, 0x47, 0x45,
-     0x53, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x1d, 0x12,
-     0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x57, 0x4f,
-     0x52, 0x4b, 0x49, 0x4e, 0x47, 0x53, 0x45, 0x54, 0x5f, 0x52, 0x45, 0x46,
-     0x41, 0x55, 0x4c, 0x54, 0x10, 0x1e, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d,
+     0x46, 0x49, 0x4c, 0x45, 0x10, 0x19, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x48, 0x4d, 0x45,
+     0x4d, 0x10, 0x1a, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x49, 0x45, 0x44,
+     0x10, 0x1b, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x54, 0x45, 0x4e, 0x10,
+     0x1c, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x4e, 0x52, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x53, 0x5f, 0x53, 0x43, 0x41,
+     0x4e, 0x4e, 0x45, 0x44, 0x10, 0x1d, 0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d,
      0x53, 0x54, 0x41, 0x54, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47,
-     0x53, 0x45, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45,
-     0x10, 0x1f, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47, 0x53, 0x45, 0x54, 0x5f,
-     0x4e, 0x4f, 0x44, 0x45, 0x52, 0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10,
-     0x20, 0x12, 0x28, 0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x4e, 0x52, 0x5f, 0x41, 0x4e, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, 0x4e,
-     0x53, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x5f, 0x48, 0x55, 0x47, 0x45,
-     0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x21, 0x12, 0x16, 0x0a, 0x12, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x52, 0x45,
-     0x45, 0x5f, 0x43, 0x4d, 0x41, 0x10, 0x22, 0x12, 0x17, 0x0a, 0x13, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x57, 0x41,
-     0x50, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x23, 0x12, 0x1d, 0x0a, 0x19,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49,
-     0x52, 0x54, 0x59, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c,
-     0x44, 0x10, 0x24, 0x12, 0x28, 0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x59, 0x5f, 0x42,
-     0x41, 0x43, 0x4b, 0x47, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x54, 0x48,
-     0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x25, 0x12, 0x11, 0x0a,
-     0x0d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x50, 0x47,
-     0x49, 0x4e, 0x10, 0x26, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x50, 0x47, 0x50, 0x47, 0x4f, 0x55, 0x54, 0x10, 0x27,
+     0x53, 0x45, 0x54, 0x5f, 0x52, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10,
+     0x1e, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47, 0x53, 0x45, 0x54, 0x5f, 0x41,
+     0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x1f, 0x12, 0x21, 0x0a,
+     0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x57, 0x4f, 0x52, 0x4b,
+     0x49, 0x4e, 0x47, 0x53, 0x45, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x52,
+     0x45, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x20, 0x12, 0x28, 0x0a, 0x24,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x41, 0x4e,
+     0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x50, 0x41, 0x52, 0x45,
+     0x4e, 0x54, 0x5f, 0x48, 0x55, 0x47, 0x45, 0x50, 0x41, 0x47, 0x45, 0x53,
+     0x10, 0x21, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x5f, 0x43, 0x4d, 0x41,
+     0x10, 0x22, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x4e, 0x52, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x43, 0x41, 0x43, 0x48,
+     0x45, 0x10, 0x23, 0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x54, 0x59, 0x5f, 0x54,
+     0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x24, 0x12, 0x28,
+     0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f,
+     0x44, 0x49, 0x52, 0x54, 0x59, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x47, 0x52,
+     0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f,
+     0x4c, 0x44, 0x10, 0x25, 0x12, 0x11, 0x0a, 0x0d, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x50, 0x47, 0x50, 0x47, 0x49, 0x4e, 0x10, 0x26, 0x12,
+     0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
+     0x50, 0x47, 0x4f, 0x55, 0x54, 0x10, 0x27, 0x12, 0x17, 0x0a, 0x13, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x50, 0x47, 0x4f, 0x55,
+     0x54, 0x43, 0x4c, 0x45, 0x41, 0x4e, 0x10, 0x28, 0x12, 0x11, 0x0a, 0x0d,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x53, 0x57, 0x50, 0x49,
+     0x4e, 0x10, 0x29, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x50, 0x53, 0x57, 0x50, 0x4f, 0x55, 0x54, 0x10, 0x2a, 0x12,
+     0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
+     0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x2b, 0x12,
+     0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
+     0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c,
+     0x10, 0x2c, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x4d, 0x4f, 0x56,
+     0x41, 0x42, 0x4c, 0x45, 0x10, 0x2d, 0x12, 0x11, 0x0a, 0x0d, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x46, 0x52, 0x45, 0x45, 0x10,
+     0x2e, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x50, 0x47, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x2f,
      0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50,
-     0x47, 0x50, 0x47, 0x4f, 0x55, 0x54, 0x43, 0x4c, 0x45, 0x41, 0x4e, 0x10,
-     0x28, 0x12, 0x11, 0x0a, 0x0d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x50, 0x53, 0x57, 0x50, 0x49, 0x4e, 0x10, 0x29, 0x12, 0x12, 0x0a, 0x0e,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x53, 0x57, 0x50, 0x4f,
-     0x55, 0x54, 0x10, 0x2a, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x44,
-     0x4d, 0x41, 0x10, 0x2b, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x5f, 0x4e,
-     0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x2c, 0x12, 0x1a, 0x0a, 0x16, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x4c, 0x4c, 0x4f,
-     0x43, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x2d, 0x12,
-     0x11, 0x0a, 0x0d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
-     0x46, 0x52, 0x45, 0x45, 0x10, 0x2e, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x41, 0x43, 0x54, 0x49, 0x56,
-     0x41, 0x54, 0x45, 0x10, 0x2f, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x44, 0x45, 0x41, 0x43, 0x54, 0x49,
-     0x56, 0x41, 0x54, 0x45, 0x10, 0x30, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x46, 0x41, 0x55, 0x4c, 0x54,
-     0x10, 0x31, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x47, 0x4d, 0x41, 0x4a, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10,
-     0x32, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x50, 0x47, 0x52, 0x45, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x44, 0x4d, 0x41,
-     0x10, 0x33, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x47, 0x52, 0x45, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e, 0x4f,
-     0x52, 0x4d, 0x41, 0x4c, 0x10, 0x34, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d,
-     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52, 0x45, 0x46, 0x49, 0x4c,
-     0x4c, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x35, 0x12,
-     0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
-     0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44,
-     0x5f, 0x44, 0x4d, 0x41, 0x10, 0x36, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d,
+     0x47, 0x44, 0x45, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10,
+     0x30, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x50, 0x47, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x31, 0x12, 0x15, 0x0a,
+     0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x4d, 0x41,
+     0x4a, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x32, 0x12, 0x17, 0x0a, 0x13,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52, 0x45, 0x46,
+     0x49, 0x4c, 0x4c, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x33, 0x12, 0x1a, 0x0a,
+     0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52, 0x45,
+     0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10,
+     0x34, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x50, 0x47, 0x52, 0x45, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4d, 0x4f, 0x56,
+     0x41, 0x42, 0x4c, 0x45, 0x10, 0x35, 0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d,
      0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c,
-     0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4e, 0x4f, 0x52, 0x4d,
-     0x41, 0x4c, 0x10, 0x37, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x4b,
-     0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c,
-     0x45, 0x10, 0x38, 0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x44, 0x49,
-     0x52, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x39, 0x12, 0x20,
-     0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53,
-     0x54, 0x45, 0x41, 0x4c, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f,
-     0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x3a, 0x12, 0x21, 0x0a, 0x1d,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45,
-     0x41, 0x4c, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x4f,
-     0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x3b, 0x12, 0x1c, 0x0a, 0x18, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e,
      0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x44, 0x4d, 0x41, 0x10,
-     0x3c, 0x12, 0x1f, 0x0a, 0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50,
-     0x44, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x3d, 0x12, 0x20,
-     0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53,
-     0x43, 0x41, 0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4d,
-     0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x3e, 0x12, 0x1c, 0x0a, 0x18,
+     0x36, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
+     0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x4b, 0x53, 0x57, 0x41,
+     0x50, 0x44, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x37, 0x12,
+     0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
+     0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44,
+     0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x38, 0x12, 0x1d,
+     0x0a, 0x19, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53,
+     0x54, 0x45, 0x41, 0x4c, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f,
+     0x44, 0x4d, 0x41, 0x10, 0x39, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f,
+     0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41,
+     0x4c, 0x10, 0x3a, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x50, 0x47, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x5f, 0x44, 0x49,
+     0x52, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45,
+     0x10, 0x3b, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41,
+     0x50, 0x44, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x3c, 0x12, 0x1f, 0x0a, 0x1b,
      0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41,
-     0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4d, 0x41,
-     0x10, 0x3f, 0x12, 0x1f, 0x0a, 0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45,
-     0x43, 0x54, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x40, 0x12,
-     0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47,
-     0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f,
-     0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x41, 0x12, 0x21, 0x0a,
-     0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43,
-     0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x54, 0x48,
-     0x52, 0x4f, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x42, 0x12, 0x17, 0x0a, 0x13,
-     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x49, 0x4e, 0x4f,
-     0x44, 0x45, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x10, 0x43, 0x12, 0x18, 0x0a,
-     0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x53, 0x4c, 0x41, 0x42,
-     0x53, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x44, 0x12,
-     0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53,
-     0x57, 0x41, 0x50, 0x44, 0x5f, 0x49, 0x4e, 0x4f, 0x44, 0x45, 0x53, 0x54,
-     0x45, 0x41, 0x4c, 0x10, 0x45, 0x12, 0x27, 0x0a, 0x23, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4c,
-     0x4f, 0x57, 0x5f, 0x57, 0x4d, 0x41, 0x52, 0x4b, 0x5f, 0x48, 0x49, 0x54,
-     0x5f, 0x51, 0x55, 0x49, 0x43, 0x4b, 0x4c, 0x59, 0x10, 0x46, 0x12, 0x28,
-     0x0a, 0x24, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53, 0x57,
-     0x41, 0x50, 0x44, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x5f, 0x57, 0x4d, 0x41,
-     0x52, 0x4b, 0x5f, 0x48, 0x49, 0x54, 0x5f, 0x51, 0x55, 0x49, 0x43, 0x4b,
-     0x4c, 0x59, 0x10, 0x47, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x4f, 0x55, 0x54, 0x52, 0x55,
-     0x4e, 0x10, 0x48, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x53, 0x54, 0x41, 0x4c, 0x4c,
-     0x10, 0x49, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x50, 0x47, 0x52, 0x4f, 0x54, 0x41, 0x54, 0x45, 0x44, 0x10, 0x4a,
-     0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x44,
-     0x52, 0x4f, 0x50, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x43, 0x41, 0x43, 0x48,
-     0x45, 0x10, 0x4b, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x10,
-     0x4c, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f,
-     0x50, 0x47, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x55,
-     0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x4d, 0x12, 0x19, 0x0a, 0x15, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x4d, 0x49, 0x47, 0x52,
-     0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x4e, 0x12, 0x22,
-     0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d,
-     0x50, 0x41, 0x43, 0x54, 0x5f, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x45,
-     0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x4f, 0x12, 0x1f,
-     0x0a, 0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d,
-     0x50, 0x41, 0x43, 0x54, 0x5f, 0x46, 0x52, 0x45, 0x45, 0x5f, 0x53, 0x43,
-     0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x50, 0x12, 0x1b, 0x0a, 0x17, 0x56,
+     0x4e, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4e, 0x4f, 0x52,
+     0x4d, 0x41, 0x4c, 0x10, 0x3d, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x4b,
+     0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42, 0x4c,
+     0x45, 0x10, 0x3e, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52,
+     0x45, 0x43, 0x54, 0x5f, 0x44, 0x4d, 0x41, 0x10, 0x3f, 0x12, 0x1f, 0x0a,
+     0x1b, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43,
+     0x41, 0x4e, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4e, 0x4f,
+     0x52, 0x4d, 0x41, 0x4c, 0x10, 0x40, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f,
+     0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x4f, 0x56, 0x41, 0x42,
+     0x4c, 0x45, 0x10, 0x41, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x50, 0x47, 0x53, 0x43, 0x41, 0x4e, 0x5f, 0x44, 0x49,
+     0x52, 0x45, 0x43, 0x54, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x54, 0x54, 0x4c,
+     0x45, 0x10, 0x42, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41,
+     0x54, 0x5f, 0x50, 0x47, 0x49, 0x4e, 0x4f, 0x44, 0x45, 0x53, 0x54, 0x45,
+     0x41, 0x4c, 0x10, 0x43, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54,
+     0x41, 0x54, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x53, 0x5f, 0x53, 0x43, 0x41,
+     0x4e, 0x4e, 0x45, 0x44, 0x10, 0x44, 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4d,
+     0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f,
+     0x49, 0x4e, 0x4f, 0x44, 0x45, 0x53, 0x54, 0x45, 0x41, 0x4c, 0x10, 0x45,
+     0x12, 0x27, 0x0a, 0x23, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4b,
+     0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x4c, 0x4f, 0x57, 0x5f, 0x57, 0x4d,
+     0x41, 0x52, 0x4b, 0x5f, 0x48, 0x49, 0x54, 0x5f, 0x51, 0x55, 0x49, 0x43,
+     0x4b, 0x4c, 0x59, 0x10, 0x46, 0x12, 0x28, 0x0a, 0x24, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x4b, 0x53, 0x57, 0x41, 0x50, 0x44, 0x5f, 0x48,
+     0x49, 0x47, 0x48, 0x5f, 0x57, 0x4d, 0x41, 0x52, 0x4b, 0x5f, 0x48, 0x49,
+     0x54, 0x5f, 0x51, 0x55, 0x49, 0x43, 0x4b, 0x4c, 0x59, 0x10, 0x47, 0x12,
+     0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x41,
+     0x47, 0x45, 0x4f, 0x55, 0x54, 0x52, 0x55, 0x4e, 0x10, 0x48, 0x12, 0x15,
+     0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x41, 0x4c, 0x4c,
+     0x4f, 0x43, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x10, 0x49, 0x12, 0x14, 0x0a,
+     0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x52, 0x4f,
+     0x54, 0x41, 0x54, 0x45, 0x44, 0x10, 0x4a, 0x12, 0x19, 0x0a, 0x15, 0x56,
+     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x50,
+     0x41, 0x47, 0x45, 0x43, 0x41, 0x43, 0x48, 0x45, 0x10, 0x4b, 0x12, 0x14,
+     0x0a, 0x10, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x44, 0x52, 0x4f,
+     0x50, 0x5f, 0x53, 0x4c, 0x41, 0x42, 0x10, 0x4c, 0x12, 0x1c, 0x0a, 0x18,
+     0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x50, 0x47, 0x4d, 0x49, 0x47,
+     0x52, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53,
+     0x10, 0x4d, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x50, 0x47, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x46,
+     0x41, 0x49, 0x4c, 0x10, 0x4e, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f,
+     0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x43, 0x41, 0x4e,
+     0x4e, 0x45, 0x44, 0x10, 0x4f, 0x12, 0x1f, 0x0a, 0x1b, 0x56, 0x4d, 0x53,
+     0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f,
+     0x46, 0x52, 0x45, 0x45, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44,
+     0x10, 0x50, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x49, 0x53, 0x4f,
+     0x4c, 0x41, 0x54, 0x45, 0x44, 0x10, 0x51, 0x12, 0x18, 0x0a, 0x14, 0x56,
      0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43,
-     0x54, 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x10, 0x51,
-     0x12, 0x18, 0x0a, 0x14, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43,
-     0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x4c, 0x4c,
-     0x10, 0x52, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x46, 0x41, 0x49,
-     0x4c, 0x10, 0x53, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x53, 0x55,
-     0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x54, 0x12, 0x1e, 0x0a, 0x1a, 0x56,
-     0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43,
-     0x54, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x5f, 0x57, 0x41, 0x4b,
-     0x45, 0x10, 0x55, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41,
-     0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c,
-     0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x43, 0x55, 0x4c, 0x4c, 0x45, 0x44,
-     0x10, 0x56, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45,
-     0x5f, 0x50, 0x47, 0x53, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44,
-     0x10, 0x57, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45,
-     0x5f, 0x50, 0x47, 0x53, 0x5f, 0x52, 0x45, 0x53, 0x43, 0x55, 0x45, 0x44,
-     0x10, 0x58, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45,
-     0x5f, 0x50, 0x47, 0x53, 0x5f, 0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44,
-     0x10, 0x59, 0x12, 0x24, 0x0a, 0x20, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
-     0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45,
-     0x5f, 0x50, 0x47, 0x53, 0x5f, 0x4d, 0x55, 0x4e, 0x4c, 0x4f, 0x43, 0x4b,
-     0x45, 0x44, 0x10, 0x5a, 0x12, 0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42,
-     0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x43, 0x4c, 0x45, 0x41, 0x52,
-     0x45, 0x44, 0x10, 0x5b, 0x12, 0x23, 0x0a, 0x1f, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42,
-     0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x4e,
-     0x44, 0x45, 0x44, 0x10, 0x5c, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53,
-     0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x5a, 0x53, 0x50, 0x41, 0x47,
-     0x45, 0x53, 0x10, 0x5d, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x45,
-     0x41, 0x50, 0x10, 0x5e, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54,
-     0x41, 0x54, 0x5f, 0x4e, 0x52, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x48, 0x45,
-     0x41, 0x50, 0x10, 0x5f}};
+     0x54, 0x5f, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x10, 0x52, 0x12, 0x17, 0x0a,
+     0x13, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50,
+     0x41, 0x43, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x53, 0x12, 0x1a,
+     0x0a, 0x16, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x43, 0x4f, 0x4d,
+     0x50, 0x41, 0x43, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53,
+     0x10, 0x54, 0x12, 0x1e, 0x0a, 0x1a, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54,
+     0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x5f, 0x44, 0x41, 0x45,
+     0x4d, 0x4f, 0x4e, 0x5f, 0x57, 0x41, 0x4b, 0x45, 0x10, 0x55, 0x12, 0x21,
+     0x0a, 0x1d, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45,
+     0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53,
+     0x5f, 0x43, 0x55, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x56, 0x12, 0x22, 0x0a,
+     0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56,
+     0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f,
+     0x53, 0x43, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x10, 0x57, 0x12, 0x22, 0x0a,
+     0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56,
+     0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f,
+     0x52, 0x45, 0x53, 0x43, 0x55, 0x45, 0x44, 0x10, 0x58, 0x12, 0x22, 0x0a,
+     0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56,
+     0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f,
+     0x4d, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x59, 0x12, 0x24, 0x0a,
+     0x20, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x45, 0x56,
+     0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47, 0x53, 0x5f,
+     0x4d, 0x55, 0x4e, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x5a, 0x12,
+     0x22, 0x0a, 0x1e, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e,
+     0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47,
+     0x53, 0x5f, 0x43, 0x4c, 0x45, 0x41, 0x52, 0x45, 0x44, 0x10, 0x5b, 0x12,
+     0x23, 0x0a, 0x1f, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x55, 0x4e,
+     0x45, 0x56, 0x49, 0x43, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x50, 0x47,
+     0x53, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x5c,
+     0x12, 0x15, 0x0a, 0x11, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e,
+     0x52, 0x5f, 0x5a, 0x53, 0x50, 0x41, 0x47, 0x45, 0x53, 0x10, 0x5d, 0x12,
+     0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
+     0x5f, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x45, 0x41, 0x50, 0x10, 0x5e, 0x12,
+     0x16, 0x0a, 0x12, 0x56, 0x4d, 0x53, 0x54, 0x41, 0x54, 0x5f, 0x4e, 0x52,
+     0x5f, 0x47, 0x50, 0x55, 0x5f, 0x48, 0x45, 0x41, 0x50, 0x10, 0x5f}};
 
 }  // namespace perfetto
 
diff --git a/src/perfetto_cmd/trigger_producer.cc b/src/perfetto_cmd/trigger_producer.cc
index b121c6a..7aefabd 100644
--- a/src/perfetto_cmd/trigger_producer.cc
+++ b/src/perfetto_cmd/trigger_producer.cc
@@ -25,8 +25,6 @@
 
 namespace perfetto {
 
-class DataSourceConfig;
-
 TriggerProducer::TriggerProducer(base::TaskRunner* task_runner,
                                  std::function<void(bool)> callback,
                                  const std::vector<std::string>* const triggers)
diff --git a/src/profiling/memory/client.cc b/src/profiling/memory/client.cc
index 8065733..59fe313 100644
--- a/src/profiling/memory/client.cc
+++ b/src/profiling/memory/client.cc
@@ -30,6 +30,7 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsGetLocal.h>
 
+#include <algorithm>
 #include <atomic>
 #include <new>
 
@@ -81,6 +82,17 @@
   return 0;
 }
 
+constexpr uint64_t kInfiniteTries = 0;
+
+uint64_t GetMaxTries(const ClientConfiguration& client_config) {
+  if (!client_config.block_client)
+    return 1u;
+  if (client_config.block_client_timeout_us == 0)
+    return kInfiniteTries;
+  return std::min<uint64_t>(
+      1ul, client_config.block_client_timeout_us / kResendBackoffUs);
+}
+
 }  // namespace
 
 const char* GetThreadStackBase() {
@@ -213,10 +225,7 @@
   }
 
   PERFETTO_DCHECK(client_config.interval >= 1);
-  // TODO(fmayer): Always make this nonblocking.
-  // This is so that without block_client, we get the old behaviour that rate
-  // limits using the blocking socket. We do not want to change that for Q.
-  sock.SetBlocking(!client_config.block_client);
+  sock.SetBlocking(false);
   Sampler sampler{client_config.interval};
   // note: the shared_ptr will retain a copy of the unhooked_allocator
   return std::allocate_shared<Client>(unhooked_allocator, std::move(sock),
@@ -232,6 +241,7 @@
                pid_t pid_at_creation,
                const char* main_thread_stack_base)
     : client_config_(client_config),
+      max_shmem_tries_(GetMaxTries(client_config_)),
       sampler_(std::move(sampler)),
       sock_(std::move(sock)),
       main_thread_stack_base_(main_thread_stack_base),
@@ -310,17 +320,19 @@
 }
 
 bool Client::SendWireMessageWithRetriesIfBlocking(const WireMessage& msg) {
-  for (;;) {
+  for (uint64_t i = 0;
+       max_shmem_tries_ == kInfiniteTries || i < max_shmem_tries_; ++i) {
     if (PERFETTO_LIKELY(SendWireMessage(&shmem_, msg)))
       return true;
     // retry if in blocking mode and still connected
     if (client_config_.block_client && base::IsAgain(errno) && IsConnected()) {
       usleep(kResendBackoffUs);
-      continue;
+    } else {
+      break;
     }
-    PERFETTO_PLOG("Failed to write to shared ring buffer. Disconnecting.");
-    return false;
   }
+  PERFETTO_PLOG("Failed to write to shared ring buffer. Disconnecting.");
+  return false;
 }
 
 bool Client::RecordFree(const uint64_t alloc_address) {
@@ -379,10 +391,12 @@
 }
 
 bool Client::SendControlSocketByte() {
-  // TODO(fmayer): Fix the special casing that only block_client uses a
-  // nonblocking socket.
+  // If base::IsAgain(errno), the socket buffer is full, so the service will
+  // pick up the notification even without adding another byte.
+  // In other error cases (usually EPIPE) we want to disconnect, because that
+  // is how the service signals the tracing session was torn down.
   if (sock_.Send(kSingleByte, sizeof(kSingleByte)) == -1 &&
-      (!client_config_.block_client || !base::IsAgain(errno))) {
+      !base::IsAgain(errno)) {
     PERFETTO_PLOG("Failed to send control socket byte.");
     return false;
   }
diff --git a/src/profiling/memory/client.h b/src/profiling/memory/client.h
index d32f2e8..02e5a50 100644
--- a/src/profiling/memory/client.h
+++ b/src/profiling/memory/client.h
@@ -105,6 +105,7 @@
   bool IsConnected();
 
   ClientConfiguration client_config_;
+  uint64_t max_shmem_tries_;
   // sampler_ operations are not thread-safe.
   Sampler sampler_;
   base::UnixSocketRaw sock_;
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index c10adc3..cdfca23 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -351,6 +351,8 @@
   auto& cli_config = data_source.client_configuration;
   cli_config.interval = heapprofd_config.sampling_interval_bytes();
   cli_config.block_client = heapprofd_config.block_client();
+  cli_config.block_client_timeout_us =
+      heapprofd_config.block_client_timeout_us();
   data_source.config = heapprofd_config;
   data_source.normalized_cmdlines = std::move(normalized_cmdlines);
   data_source.stop_timeout_ms = ds_config.stop_timeout_ms();
diff --git a/src/profiling/memory/malloc_hooks.cc b/src/profiling/memory/malloc_hooks.cc
index d70ac3c..7f6d1c3 100644
--- a/src/profiling/memory/malloc_hooks.cc
+++ b/src/profiling/memory/malloc_hooks.cc
@@ -84,12 +84,12 @@
 int HEAPPROFD_ADD_PREFIX(_posix_memalign)(void** memptr,
                                           size_t alignment,
                                           size_t size);
-int HEAPPROFD_ADD_PREFIX(_iterate)(uintptr_t base,
-                                   size_t size,
-                                   void (*callback)(uintptr_t base,
-                                                    size_t size,
-                                                    void* arg),
-                                   void* arg);
+int HEAPPROFD_ADD_PREFIX(_malloc_iterate)(uintptr_t base,
+                                          size_t size,
+                                          void (*callback)(uintptr_t base,
+                                                           size_t size,
+                                                           void* arg),
+                                          void* arg);
 void HEAPPROFD_ADD_PREFIX(_malloc_disable)();
 void HEAPPROFD_ADD_PREFIX(_malloc_enable)();
 
@@ -620,12 +620,12 @@
   return dispatch->malloc_info(options, fp);
 }
 
-int HEAPPROFD_ADD_PREFIX(_iterate)(uintptr_t,
-                                   size_t,
-                                   void (*)(uintptr_t base,
-                                            size_t size,
-                                            void* arg),
-                                   void*) {
+int HEAPPROFD_ADD_PREFIX(_malloc_iterate)(uintptr_t,
+                                          size_t,
+                                          void (*)(uintptr_t base,
+                                                   size_t size,
+                                                   void* arg),
+                                          void*) {
   return 0;
 }
 
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index ced87f9..6b030c0 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -303,9 +303,7 @@
     shmem.EndRead(std::move(buf));
     // Reparsing takes time, so process the rest in a new batch to avoid timing
     // out.
-    // TODO(fmayer): Do not special case blocking mode.
-    if (client_data.client_config.block_client &&
-        reparses_before < client_data.metadata.reparses) {
+    if (reparses_before < client_data.metadata.reparses) {
       repost_task = true;
       break;
     }
diff --git a/src/profiling/memory/wire_protocol.h b/src/profiling/memory/wire_protocol.h
index 3ca21d9..1a40b8f 100644
--- a/src/profiling/memory/wire_protocol.h
+++ b/src/profiling/memory/wire_protocol.h
@@ -45,6 +45,7 @@
   // Must be >= 1.
   uint64_t interval;
   bool block_client;
+  uint64_t block_client_timeout_us;
 };
 
 // Types needed for the wire format used for communication between the client
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index bcc34e0..b2cc13a 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -57,6 +57,7 @@
     "../base:test_support",
   ]
   sources = [
+    "copyable_ptr_unittest.cc",
     "message_handle_unittest.cc",
     "message_unittest.cc",
     "proto_decoder_unittest.cc",
diff --git a/src/base/copyable_ptr_unittest.cc b/src/protozero/copyable_ptr_unittest.cc
similarity index 94%
rename from src/base/copyable_ptr_unittest.cc
rename to src/protozero/copyable_ptr_unittest.cc
index ecbd1ef..d9be950 100644
--- a/src/base/copyable_ptr_unittest.cc
+++ b/src/protozero/copyable_ptr_unittest.cc
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include "perfetto/base/copyable_ptr.h"
+#include "perfetto/protozero/copyable_ptr.h"
 
 #include "test/gtest_and_gmock.h"
 
-namespace perfetto {
-namespace base {
+namespace protozero {
 namespace {
 
 struct X {
@@ -116,5 +115,4 @@
 }
 
 }  // namespace
-}  // namespace base
-}  // namespace perfetto
+}  // namespace protozero
diff --git a/src/protozero/proto_decoder_unittest.cc b/src/protozero/proto_decoder_unittest.cc
index 1a63cef..eb9f6f7 100644
--- a/src/protozero/proto_decoder_unittest.cc
+++ b/src/protozero/proto_decoder_unittest.cc
@@ -25,8 +25,11 @@
 #include "src/protozero/test/example_proto/test_messages.pb.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
 
-namespace pbtest = foo::bar::pbzero;  // Generated by the protozero plugin.
-namespace pbgold = foo::bar;  // Generated by the official protobuf compiler.
+// Generated by the protozero plugin.
+namespace pbtest = protozero::test::protos::pbzero;
+
+// Generated by the official protobuf compiler.
+namespace pbgold = protozero::test::protos;
 
 namespace protozero {
 namespace {
@@ -90,6 +93,28 @@
   EXPECT_FALSE(++it);
 }
 
+TEST(ProtoDecoderTest, RepeatedVariableLengthField) {
+  HeapBuffered<Message> message;
+
+  static constexpr char kTestString[] = "test";
+  static constexpr char kTestString2[] = "honk honk";
+  message->AppendString(1, kTestString);
+  message->AppendString(1, kTestString2);
+  std::vector<uint8_t> proto = message.SerializeAsArray();
+  TypedProtoDecoder<32, false> decoder(proto.data(), proto.size());
+
+  auto it = decoder.GetRepeated<ConstChars>(1);
+  ASSERT_EQ(it->type(), ProtoWireType::kLengthDelimited);
+  ASSERT_EQ(it->size(), sizeof(kTestString) - 1);
+  ASSERT_EQ(it->as_std_string(), std::string(kTestString));
+  ASSERT_EQ((*it).ToStdString(), std::string(kTestString));
+  ++it;
+  ASSERT_EQ(it->type(), ProtoWireType::kLengthDelimited);
+  ASSERT_EQ(it->size(), sizeof(kTestString2) - 1);
+  ASSERT_EQ(it->as_std_string(), std::string(kTestString2));
+  ASSERT_EQ((*it).ToStdString(), std::string(kTestString2));
+}
+
 TEST(ProtoDecoderTest, SingleRepeatedFieldWithExpansion) {
   Message message;
   ScatteredHeapBuffer delegate(512, 512);
diff --git a/src/protozero/proto_utils_unittest.cc b/src/protozero/proto_utils_unittest.cc
index 9d56d07..a724189 100644
--- a/src/protozero/proto_utils_unittest.cc
+++ b/src/protozero/proto_utils_unittest.cc
@@ -103,6 +103,14 @@
             ZigZagEncode(std::numeric_limits<int32_t>::min()));
   EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
             ZigZagEncode(std::numeric_limits<int64_t>::min()));
+
+  EXPECT_EQ(0, ZigZagDecode(ZigZagEncode(0)));
+  EXPECT_EQ(-1, ZigZagDecode(ZigZagEncode(-1)));
+  EXPECT_EQ(1, ZigZagDecode(ZigZagEncode(1)));
+  EXPECT_EQ(-127, ZigZagDecode(ZigZagEncode(-127)));
+  EXPECT_EQ(0x7fffffff, ZigZagDecode(ZigZagEncode(0x7fffffff)));
+  EXPECT_EQ(9000000000, ZigZagDecode(ZigZagEncode(9000000000)));
+  EXPECT_EQ(-9000000000, ZigZagDecode(ZigZagEncode(-9000000000)));
 }
 
 TEST(ProtoUtilsTest, VarIntEncoding) {
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index b396a67..bbd2b23 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -121,7 +121,7 @@
   h_printer.Print("#include <vector>\n");
   h_printer.Print("#include <string>\n");
   h_printer.Print("#include <type_traits>\n\n");
-  h_printer.Print("#include \"perfetto/base/copyable_ptr.h\"\n");
+  h_printer.Print("#include \"perfetto/protozero/copyable_ptr.h\"\n");
   h_printer.Print("#include \"perfetto/base/export.h\"\n\n");
 
   cc_printer.Print(kHeader);
@@ -314,10 +314,25 @@
 void CppObjGenerator::GenEnum(const EnumDescriptor* enum_desc,
                               Printer* p) const {
   std::string full_name = GetFullName(enum_desc);
+
+  // When generating enums, there are two cases:
+  // 1. Enums nested in a message (most frequent case), e.g.:
+  //    message MyMsg { enum MyEnum { FOO=1; BAR=2; } }
+  // 2. Enum defined at the package level, outside of any message.
+  //
+  // In the case 1, the C++ code generated by the official protobuf library is:
+  // enum MyEnum {  MyMsg_MyEnum_FOO=1, MyMsg_MyEnum_BAR=2 }
+  // class MyMsg { static const auto FOO = MyMsg_MyEnum_FOO; ... same for BAR }
+  //
+  // In the case 2, the C++ code is simply:
+  // enum MyEnum { FOO=1, BAR=2 }
+  // Hence this |prefix| logic.
+  std::string prefix = enum_desc->containing_type() ? full_name + "_" : "";
+
   p->Print("enum $f$ : int {\n", "f", full_name);
   for (int e = 0; e < enum_desc->value_count(); e++) {
     const EnumValueDescriptor* value = enum_desc->value(e);
-    p->Print("  $f$_$n$ = $v$,\n", "f", full_name, "n", value->name(), "v",
+    p->Print("  $p$$n$ = $v$,\n", "p", prefix, "n", value->name(), "v",
              std::to_string(value->number()));
   }
   p->Print("};\n");
@@ -426,7 +441,7 @@
     } else if (!field->is_repeated()) {
       std::string type = GetCppType(field, false);
       if (field->type() == TYPE_MESSAGE) {
-        type = "::perfetto::base::CopyablePtr<" + type + ">";
+        type = "::protozero::CopyablePtr<" + type + ">";
         p->Print("$t$ $n$_;\n", "t", type, "n", field->lowercase_name());
       } else {
         p->Print("$t$ $n$_{};\n", "t", type, "n", field->lowercase_name());
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index 72d380c..dd49d45 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -23,6 +23,7 @@
 #include <google/protobuf/compiler/code_generator.h>
 #include <google/protobuf/compiler/plugin.h>
 #include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/io/printer.h>
 #include <google/protobuf/io/zero_copy_stream.h>
 
@@ -429,7 +430,7 @@
   void GeneratePackedRepeatedFieldDescriptor(const FieldDescriptor* field) {
     std::map<std::string, std::string> setter;
     setter["id"] = std::to_string(field->number());
-    setter["name"] = field->name();
+    setter["name"] = field->lowercase_name();
     setter["action"] = "set";
     setter["buffer_type"] = FieldTypeToPackedBufferType(field->type());
     stub_h_->Print(
@@ -442,11 +443,15 @@
   void GenerateSimpleFieldDescriptor(const FieldDescriptor* field) {
     std::map<std::string, std::string> setter;
     setter["id"] = std::to_string(field->number());
-    setter["name"] = field->name();
+    setter["name"] = field->lowercase_name();
     setter["action"] = field->is_repeated() ? "add" : "set";
 
     std::string appender;
     std::string cpp_type;
+    const char* code_stub =
+        "void $action$_$name$($cpp_type$ value) {\n"
+        "  $appender$($id$, value);\n"
+        "}\n";
 
     switch (field->type()) {
       case FieldDescriptor::TYPE_BOOL: {
@@ -519,18 +524,21 @@
         cpp_type = GetCppClassName(field->enum_type(), true);
         break;
       }
-      case FieldDescriptor::TYPE_STRING: {
-        appender = "AppendString";
-        cpp_type = "const char*";
-        break;
-      }
+      case FieldDescriptor::TYPE_STRING:
       case FieldDescriptor::TYPE_BYTES: {
-        stub_h_->Print(
-            setter,
-            "void $action$_$name$(const uint8_t* data, size_t size) {\n"
+        if (field->type() == FieldDescriptor::TYPE_STRING) {
+          cpp_type = "const char*";
+        } else {
+          cpp_type = "const uint8_t*";
+        }
+        code_stub =
+            "void $action$_$name$(const std::string& value) {\n"
+            "  AppendBytes($id$, value.data(), value.size());\n"
+            "}\n"
+            "void $action$_$name$($cpp_type$ data, size_t size) {\n"
             "  AppendBytes($id$, data, size);\n"
-            "}\n");
-        return;
+            "}\n";
+        break;
       }
       case FieldDescriptor::TYPE_GROUP:
       case FieldDescriptor::TYPE_MESSAGE: {
@@ -540,20 +548,7 @@
     }
     setter["appender"] = appender;
     setter["cpp_type"] = cpp_type;
-    stub_h_->Print(setter,
-                   "void $action$_$name$($cpp_type$ value) {\n"
-                   "  $appender$($id$, value);\n"
-                   "}\n");
-
-    // For strings also generate a variant for non-null terminated strings.
-    if (field->type() == FieldDescriptor::TYPE_STRING) {
-      stub_h_->Print(setter,
-                     "// Doesn't check for null terminator.\n"
-                     "// Expects |value| to be at least |size| long.\n"
-                     "void $action$_$name$($cpp_type$ value, size_t size) {\n"
-                     "  AppendBytes($id$, value, size);\n"
-                     "}\n");
-    }
+    stub_h_->Print(setter, code_stub);
   }
 
   void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
@@ -563,8 +558,17 @@
         "template <typename T = $inner_class$> T* $action$_$name$() {\n"
         "  return BeginNestedMessage<T>($id$);\n"
         "}\n\n",
-        "id", std::to_string(field->number()), "name", field->name(), "action",
-        action, "inner_class", inner_class);
+        "id", std::to_string(field->number()), "name", field->lowercase_name(),
+        "action", action, "inner_class", inner_class);
+    if (field->options().lazy()) {
+      stub_h_->Print(
+          "void $action$_$name$_raw(const std::string& raw) {\n"
+          "  return AppendBytes($id$, raw.data(), raw.size());\n"
+          "}\n\n",
+          "id", std::to_string(field->number()), "name",
+          field->lowercase_name(), "action", action, "inner_class",
+          inner_class);
+    }
   }
 
   void GenerateDecoder(const Descriptor* message) {
@@ -664,7 +668,7 @@
       }
 
       stub_h_->Print("bool has_$name$() const { return at<$id$>().valid(); }\n",
-                     "name", field->name(), "id",
+                     "name", field->lowercase_name(), "id",
                      std::to_string(field->number()));
 
       if (field->is_packed()) {
@@ -676,19 +680,20 @@
             "GetPackedRepeated<$wire_type$, $cpp_type$>($id$, "
             "parse_error_ptr); }\n",
             "wire_type", protozero_wire_type, "cpp_type", cpp_type, "name",
-            field->name(), "id", std::to_string(field->number()));
+            field->lowercase_name(), "id", std::to_string(field->number()));
       } else if (field->is_repeated()) {
         stub_h_->Print(
             "::protozero::RepeatedFieldIterator<$cpp_type$> $name$() const { "
             "return "
             "GetRepeated<$cpp_type$>($id$); }\n",
-            "name", field->name(), "cpp_type", cpp_type, "id",
+            "name", field->lowercase_name(), "cpp_type", cpp_type, "id",
             std::to_string(field->number()));
       } else {
         stub_h_->Print(
             "$cpp_type$ $name$() const { return at<$id$>().$getter$(); }\n",
-            "name", field->name(), "id", std::to_string(field->number()),
-            "cpp_type", cpp_type, "getter", getter);
+            "name", field->lowercase_name(), "id",
+            std::to_string(field->number()), "cpp_type", cpp_type, "getter",
+            getter);
       }
     }
     stub_h_->Outdent();
diff --git a/src/protozero/test/example_proto/library.proto b/src/protozero/test/example_proto/library.proto
index 4ff8fee..059080f 100644
--- a/src/protozero/test/example_proto/library.proto
+++ b/src/protozero/test/example_proto/library.proto
@@ -17,7 +17,7 @@
 syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
-package foo.bar;
+package protozero.test.protos;
 
 import public "src/protozero/test/example_proto/library_internals/galaxies.proto";
 
diff --git a/src/protozero/test/example_proto/library_internals/galaxies.proto b/src/protozero/test/example_proto/library_internals/galaxies.proto
index 0b1c1c5..d2a1e42 100644
--- a/src/protozero/test/example_proto/library_internals/galaxies.proto
+++ b/src/protozero/test/example_proto/library_internals/galaxies.proto
@@ -17,7 +17,7 @@
 syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
-package foo.bar;
+package protozero.test.protos;
 
 import public "src/protozero/test/example_proto/upper_import.proto";
 
diff --git a/src/protozero/test/example_proto/test_messages.proto b/src/protozero/test/example_proto/test_messages.proto
index d28b1e8..db8212c 100644
--- a/src/protozero/test/example_proto/test_messages.proto
+++ b/src/protozero/test/example_proto/test_messages.proto
@@ -17,7 +17,7 @@
 syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
-package foo.bar;
+package protozero.test.protos;
 
 import "src/protozero/test/example_proto/library.proto";
 
diff --git a/src/protozero/test/example_proto/upper_import.proto b/src/protozero/test/example_proto/upper_import.proto
index 4aacd41..486eff9 100644
--- a/src/protozero/test/example_proto/upper_import.proto
+++ b/src/protozero/test/example_proto/upper_import.proto
@@ -17,6 +17,6 @@
 syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
-package foo.bar;
+package protozero.test.protos;
 
 message TrickyPublicImport {}
diff --git a/src/protozero/test/protozero_conformance_unittest.cc b/src/protozero/test/protozero_conformance_unittest.cc
index a374c51..85eaf43 100644
--- a/src/protozero/test/protozero_conformance_unittest.cc
+++ b/src/protozero/test/protozero_conformance_unittest.cc
@@ -28,8 +28,11 @@
 #include "src/protozero/test/example_proto/test_messages.pb.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
 
-namespace pbtest = foo::bar::pbzero;  // Generated by the protozero plugin.
-namespace pbgold = foo::bar;  // Generated by the official protobuf compiler.
+// Generated by the protozero plugin.
+namespace pbtest = protozero::test::protos::pbzero;
+
+// Generated by the official protobuf compiler.
+namespace pbgold = protozero::test::protos;
 
 namespace protozero {
 namespace {
diff --git a/src/trace_processor/android_logs_table.cc b/src/trace_processor/android_logs_table.cc
index b104280..017a89a 100644
--- a/src/trace_processor/android_logs_table.cc
+++ b/src/trace_processor/android_logs_table.cc
@@ -47,16 +47,15 @@
 int AndroidLogsTable::BestIndex(const QueryConstraints& qc,
                                 BestIndexInfo* info) {
   info->estimated_cost = static_cast<uint32_t>(storage_->android_logs().size());
-
-  info->order_by_consumed = true;
+  info->sqlite_omit_order_by = true;
 
   // Only the string columns are handled by SQLite.
   size_t tag_index = schema().ColumnIndexFromName("tag");
   size_t msg_index = schema().ColumnIndexFromName("msg");
   for (size_t i = 0; i < qc.constraints().size(); i++) {
-    info->omit[i] =
-        qc.constraints()[i].iColumn != static_cast<int>(tag_index) &&
-        qc.constraints()[i].iColumn != static_cast<int>(msg_index);
+    info->sqlite_omit_constraint[i] =
+        qc.constraints()[i].column != static_cast<int>(tag_index) &&
+        qc.constraints()[i].column != static_cast<int>(msg_index);
   }
 
   return SQLITE_OK;
diff --git a/src/trace_processor/counter_values_table.cc b/src/trace_processor/counter_values_table.cc
index fc536b4..f77d91a 100644
--- a/src/trace_processor/counter_values_table.cc
+++ b/src/trace_processor/counter_values_table.cc
@@ -24,14 +24,14 @@
 
 void CounterValuesTable::RegisterTable(sqlite3* db,
                                        const TraceStorage* storage) {
-  SqliteTable::Register<CounterValuesTable>(db, storage, "counter_values");
+  SqliteTable::Register<CounterValuesTable>(db, storage, "counter");
 }
 
 StorageSchema CounterValuesTable::CreateStorageSchema() {
   const auto& cs = storage_->counter_values();
   return StorageSchema::Builder()
       .AddGenericNumericColumn("id", RowIdAccessor(TableId::kCounterValues))
-      .AddNumericColumn("counter_id", &cs.track_ids(), &cs.rows_for_track_id())
+      .AddNumericColumn("track_id", &cs.track_ids(), &cs.rows_for_track_id())
       .AddOrderedNumericColumn("ts", &cs.timestamps())
       .AddNumericColumn("value", &cs.values())
       .AddNumericColumn("arg_set_id", &cs.arg_set_ids())
@@ -45,17 +45,15 @@
 int CounterValuesTable::BestIndex(const QueryConstraints& qc,
                                   BestIndexInfo* info) {
   info->estimated_cost = EstimateCost(qc);
-
-  info->order_by_consumed = true;
-  for (size_t i = 0; i < qc.constraints().size(); i++) {
-    info->omit[i] = true;
-  }
+  info->sqlite_omit_order_by = true;
+  auto& omit_cs = info->sqlite_omit_constraint;
+  std::fill(omit_cs.begin(), omit_cs.end(), true);
 
   return SQLITE_OK;
 }
 
 uint32_t CounterValuesTable::EstimateCost(const QueryConstraints& qc) {
-  if (HasEqConstraint(qc, "counter_id"))
+  if (HasEqConstraint(qc, "track_id"))
     return RowCount() / 100;
   return RowCount();
 }
diff --git a/src/trace_processor/cpu_profile_stack_sample_table.cc b/src/trace_processor/cpu_profile_stack_sample_table.cc
index bb51404..1226020 100644
--- a/src/trace_processor/cpu_profile_stack_sample_table.cc
+++ b/src/trace_processor/cpu_profile_stack_sample_table.cc
@@ -46,7 +46,7 @@
 
 int CpuProfileStackSampleTable::BestIndex(const QueryConstraints& qc,
                                           BestIndexInfo* info) {
-  info->order_by_consumed = true;
+  info->sqlite_omit_order_by = true;
   info->estimated_cost = HasEqConstraint(qc, "id") ? 1 : RowCount();
   return SQLITE_OK;
 }
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index 0fbe66e..648e490 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -50,9 +50,8 @@
       string_pool_(table->string_pool_) {}
 
 Column Column::IdColumn(Table* table, uint32_t col_idx, uint32_t row_map_idx) {
-  return Column("id", ColumnType::kId,
-                Flag::kId | Flag::kSorted | Flag::kNonNull, table, col_idx,
-                row_map_idx, nullptr);
+  return Column("id", ColumnType::kId, Flag::kSorted | Flag::kNonNull, table,
+                col_idx, row_map_idx, nullptr);
 }
 
 const RowMap& Column::row_map() const {
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index fdcce59..d1361f2 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -71,17 +71,9 @@
     // Indicates that this column has no special properties.
     kNoFlag = 0,
 
-    // Indiciates that the column is an "id" column. Specifically, this means
-    // the backing data for this column has the property that data[i] = i;
-    //
-    // Note: generally, this flag should not be passed by users of this class.
-    // Instead they should use the Column::IdColumn method to create an id
-    // column.
-    kId = 1 << 0,
-
     // Indicates the data in the column is sorted. This can be used to speed
     // up filtering and skip sorting.
-    kSorted = 1 << 1,
+    kSorted = 1 << 0,
 
     // Indicates the data in the column is non-null. That is, the SparseVector
     // passed in will never have any null entries. This is only used for
@@ -90,7 +82,7 @@
     //
     // This is used to speed up filters as we can safely index SparseVector
     // directly if this flag is set.
-    kNonNull = 1 << 2
+    kNonNull = 1 << 1,
   };
 
   template <typename T>
@@ -154,7 +146,7 @@
   // given filter constraint.
   void FilterInto(FilterOp op, SqlValue value, RowMap* rm) const {
     // TODO(lalitm): add special logic here to deal with kId and kSorted flags.
-    if (type_ == ColumnType::kId && op == FilterOp::kEq) {
+    if (IsId() && op == FilterOp::kEq) {
       auto opt_idx = IndexOf(value);
       if (opt_idx) {
         rm->Intersect(RowMap::SingleRow(*opt_idx));
@@ -200,7 +192,7 @@
   }
 
   // Returns true if this column is considered an id column.
-  bool IsId() const { return (flags_ & Flag::kId) != 0; }
+  bool IsId() const { return type_ == ColumnType::kId; }
 
   // Returns true if this column is a nullable column.
   bool IsNullable() const { return (flags_ & Flag::kNonNull) == 0; }
diff --git a/src/trace_processor/db/table.cc b/src/trace_processor/db/table.cc
index 289b357..289d540 100644
--- a/src/trace_processor/db/table.cc
+++ b/src/trace_processor/db/table.cc
@@ -130,7 +130,7 @@
 
   for (const Column& col : columns_) {
     // We skip id columns as they are misleading on join tables.
-    if ((col.flags_ & Column::kId) != 0)
+    if (col.IsId())
       continue;
     table.columns_.emplace_back(col, &table, table.columns_.size(),
                                 col.row_map_idx_);
@@ -160,7 +160,7 @@
   uint32_t left_row_maps_size = static_cast<uint32_t>(row_maps_.size());
   for (const Column& col : other.columns_) {
     // We skip id columns as they are misleading on join tables.
-    if ((col.flags_ & Column::kId) != 0)
+    if (col.IsId())
       continue;
 
     // Ensure that we offset the RowMap index by the number of RowMaps in the
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 15ad611..5d2d0a0 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -799,12 +799,22 @@
           static_cast<int32_t>(storage->GetProcess(*thread.upid).pid);
     }
 
-    // Use "I" instead of "i" phase for backwards-compat with old consumers.
-    event["ph"] = "I";
+    event["ph"] = "n";
     event["cat"] = "disabled_by_default-cpu_profiler";
     event["name"] = "StackCpuSampling";
     event["s"] = "t";
 
+    // Add a dummy thread timestamp to this event to match the format of instant
+    // events. Useful in the UI to view args of a selected group of samples.
+    event["tts"] = Json::Int64(1);
+
+    // "n"-phase events are nestable async events which get tied together with
+    // their id, so we need to give each one a unique ID as we only
+    // want the samples to show up on their own track in the trace-viewer but
+    // not nested together.
+    static size_t g_id_counter = 0;
+    event["id"] = PrintUint64(++g_id_counter);
+
     std::vector<std::string> callstack;
     const auto& callsites = storage->stack_profile_callsite_table();
     int64_t maybe_callsite_id = samples.callsite_ids()[i];
@@ -853,6 +863,12 @@
     }
 
     event["args"]["frames"] = merged_callstack;
+
+    // TODO(oysteine): Used for backwards compatibility with the memlog
+    // pipeline, should remove once we've switched to looking directly at the
+    // tid.
+    event["args"]["thread_id"] = thread.tid;
+
     writer->WriteCommonEvent(event);
   }
 
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index c5c949a..6b7ad59 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -1189,7 +1189,8 @@
 
   EXPECT_EQ(result["traceEvents"].size(), 1u);
   Json::Value event = result["traceEvents"][0];
-  EXPECT_EQ(event["ph"].asString(), "I");
+  EXPECT_EQ(event["ph"].asString(), "n");
+  EXPECT_EQ(event["id"].asString(), "0x1");
   EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
   EXPECT_EQ(event["tid"].asUInt(), kThreadID);
   EXPECT_EQ(event["cat"].asString(), "disabled_by_default-cpu_profiler");
diff --git a/src/trace_processor/heap_profile_allocation_table.cc b/src/trace_processor/heap_profile_allocation_table.cc
index 8bd050e..dcdb825 100644
--- a/src/trace_processor/heap_profile_allocation_table.cc
+++ b/src/trace_processor/heap_profile_allocation_table.cc
@@ -48,7 +48,7 @@
 
 int HeapProfileAllocationTable::BestIndex(const QueryConstraints& qc,
                                           BestIndexInfo* info) {
-  info->order_by_consumed = true;
+  info->sqlite_omit_order_by = true;
   info->estimated_cost = HasEqConstraint(qc, "id") ? 1 : RowCount();
   return SQLITE_OK;
 }
diff --git a/src/trace_processor/importers/ftrace/ftrace_module.h b/src/trace_processor/importers/ftrace/ftrace_module.h
index 9d8d489..fd1ace0 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module.h
+++ b/src/trace_processor/importers/ftrace/ftrace_module.h
@@ -65,14 +65,7 @@
 
   ModuleResult ParseFtracePacket(uint32_t cpu,
                                  const TimestampedTracePiece& ttp) {
-    // Handle the (optional) alternative encoding format for sched_switch.
-    if (ttp.inline_event.type == InlineEvent::Type::kSchedSwitch) {
-      parser_.ParseInlineSchedSwitch(cpu, ttp.timestamp,
-                                     ttp.inline_event.sched_switch);
-      return ModuleResult::Handled();
-    }
-
-    return parser_.ParseFtraceEvent(cpu, ttp.timestamp, ttp.blob_view);
+    return parser_.ParseFtraceEvent(cpu, ttp);
   }
 
  private:
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index f325192..3ee0cf2 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -184,12 +184,32 @@
 
 PERFETTO_ALWAYS_INLINE
 util::Status FtraceParser::ParseFtraceEvent(uint32_t cpu,
-                                            int64_t ts,
-                                            const TraceBlobView& event) {
+                                            const TimestampedTracePiece& ttp) {
+  using protos::pbzero::FtraceEvent;
+  int64_t ts = ttp.timestamp;
+
+  // Handle the (optional) alternative encoding format for sched_switch.
+  if (ttp.inline_event.type == InlineEvent::Type::kSchedSwitch) {
+    const auto& event = ttp.inline_event.sched_switch;
+    context_->sched_tracker->PushSchedSwitchCompact(
+        cpu, ts, event.prev_state, static_cast<uint32_t>(event.next_pid),
+        event.next_prio, event.next_comm);
+    return util::OkStatus();
+  }
+
+  // Handle the (optional) alternative encoding format for sched_waking.
+  if (ttp.inline_event.type == InlineEvent::Type::kSchedWaking) {
+    const auto& event = ttp.inline_event.sched_waking;
+    context_->sched_tracker->PushSchedWakingCompact(
+        cpu, ts, static_cast<uint32_t>(event.pid), event.target_cpu, event.prio,
+        event.comm);
+    return util::OkStatus();
+  }
+
+  const TraceBlobView& event = ttp.blob_view;
   ProtoDecoder decoder(event.data(), event.length());
   uint64_t raw_pid = 0;
-  if (auto pid_field =
-          decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber)) {
+  if (auto pid_field = decoder.FindField(FtraceEvent::kPidFieldNumber)) {
     raw_pid = pid_field.as_uint64();
   } else {
     return util::ErrStatus("Pid field not found in ftrace packet");
@@ -197,126 +217,125 @@
   uint32_t pid = static_cast<uint32_t>(raw_pid);
 
   for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) {
-    bool is_metadata_field =
-        fld.id() == protos::pbzero::FtraceEvent::kPidFieldNumber ||
-        fld.id() == protos::pbzero::FtraceEvent::kTimestampFieldNumber;
+    bool is_metadata_field = fld.id() == FtraceEvent::kPidFieldNumber ||
+                             fld.id() == FtraceEvent::kTimestampFieldNumber;
     if (is_metadata_field)
       continue;
 
     ConstBytes data = fld.as_bytes();
-    if (fld.id() == protos::pbzero::FtraceEvent::kGenericFieldNumber) {
+    if (fld.id() == FtraceEvent::kGenericFieldNumber) {
       ParseGenericFtrace(ts, cpu, pid, data);
-    } else if (fld.id() !=
-               protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber) {
+    } else if (fld.id() != FtraceEvent::kSchedSwitchFieldNumber) {
+      // sched_switch parsing populates the raw table by itself
       ParseTypedFtraceToRaw(fld.id(), ts, cpu, pid, data);
     }
 
     switch (fld.id()) {
-      case protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber: {
+      case FtraceEvent::kSchedSwitchFieldNumber: {
         ParseSchedSwitch(cpu, ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSchedWakeupFieldNumber: {
+      case FtraceEvent::kSchedWakeupFieldNumber: {
         ParseSchedWakeup(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSchedWakingFieldNumber: {
+      case FtraceEvent::kSchedWakingFieldNumber: {
         ParseSchedWaking(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber: {
+      case FtraceEvent::kSchedProcessFreeFieldNumber: {
         ParseSchedProcessFree(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kCpuFrequencyFieldNumber: {
+      case FtraceEvent::kCpuFrequencyFieldNumber: {
         ParseCpuFreq(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kGpuFrequencyFieldNumber: {
+      case FtraceEvent::kGpuFrequencyFieldNumber: {
         ParseGpuFreq(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kCpuIdleFieldNumber: {
+      case FtraceEvent::kCpuIdleFieldNumber: {
         ParseCpuIdle(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kPrintFieldNumber: {
+      case FtraceEvent::kPrintFieldNumber: {
         ParsePrint(cpu, ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kZeroFieldNumber: {
+      case FtraceEvent::kZeroFieldNumber: {
         ParseZero(cpu, ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kRssStatFieldNumber: {
+      case FtraceEvent::kRssStatFieldNumber: {
         ParseRssStat(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kIonHeapGrowFieldNumber: {
+      case FtraceEvent::kIonHeapGrowFieldNumber: {
         ParseIonHeapGrowOrShrink(ts, pid, data, true);
         break;
       }
-      case protos::pbzero::FtraceEvent::kIonHeapShrinkFieldNumber: {
+      case FtraceEvent::kIonHeapShrinkFieldNumber: {
         ParseIonHeapGrowOrShrink(ts, pid, data, false);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSignalGenerateFieldNumber: {
+      case FtraceEvent::kSignalGenerateFieldNumber: {
         ParseSignalGenerate(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSignalDeliverFieldNumber: {
+      case FtraceEvent::kSignalDeliverFieldNumber: {
         ParseSignalDeliver(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kLowmemoryKillFieldNumber: {
+      case FtraceEvent::kLowmemoryKillFieldNumber: {
         ParseLowmemoryKill(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kOomScoreAdjUpdateFieldNumber: {
+      case FtraceEvent::kOomScoreAdjUpdateFieldNumber: {
         ParseOOMScoreAdjUpdate(ts, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kMmEventRecordFieldNumber: {
+      case FtraceEvent::kMmEventRecordFieldNumber: {
         ParseMmEventRecord(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSysEnterFieldNumber: {
+      case FtraceEvent::kSysEnterFieldNumber: {
         ParseSysEvent(ts, pid, true, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kSysExitFieldNumber: {
+      case FtraceEvent::kSysExitFieldNumber: {
         ParseSysEvent(ts, pid, false, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber: {
+      case FtraceEvent::kTaskNewtaskFieldNumber: {
         ParseTaskNewTask(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kTaskRenameFieldNumber: {
+      case FtraceEvent::kTaskRenameFieldNumber: {
         ParseTaskRename(data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderTransactionFieldNumber: {
+      case FtraceEvent::kBinderTransactionFieldNumber: {
         ParseBinderTransaction(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderTransactionReceivedFieldNumber: {
+      case FtraceEvent::kBinderTransactionReceivedFieldNumber: {
         ParseBinderTransactionReceived(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderTransactionAllocBufFieldNumber: {
+      case FtraceEvent::kBinderTransactionAllocBufFieldNumber: {
         ParseBinderTransactionAllocBuf(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderLockFieldNumber: {
+      case FtraceEvent::kBinderLockFieldNumber: {
         ParseBinderLock(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderUnlockFieldNumber: {
+      case FtraceEvent::kBinderUnlockFieldNumber: {
         ParseBinderUnlock(ts, pid, data);
         break;
       }
-      case protos::pbzero::FtraceEvent::kBinderLockedFieldNumber: {
+      case FtraceEvent::kBinderLockedFieldNumber: {
         ParseBinderLocked(ts, pid, data);
         break;
       }
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 0117d7c..3d7138b 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -34,17 +34,7 @@
 
   void ParseFtraceStats(protozero::ConstBytes);
 
-  void ParseInlineSchedSwitch(uint32_t cpu,
-                              int64_t ts,
-                              const InlineSchedSwitch& event) {
-    context_->sched_tracker->PushSchedSwitchCompact(
-        cpu, ts, event.prev_state, static_cast<uint32_t>(event.next_pid),
-        event.next_prio, event.next_comm);
-  }
-
-  util::Status ParseFtraceEvent(uint32_t cpu,
-                                int64_t ts,
-                                const TraceBlobView& packet);
+  util::Status ParseFtraceEvent(uint32_t cpu, const TimestampedTracePiece& ttp);
 
  private:
   void ParseGenericFtrace(int64_t timestamp,
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 8a183a7..4319e2d 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -65,19 +65,66 @@
 }
 
 PERFETTO_ALWAYS_INLINE
+void FtraceTokenizer::TokenizeFtraceEvent(uint32_t cpu, TraceBlobView event) {
+  constexpr auto kTimestampFieldNumber =
+      protos::pbzero::FtraceEvent::kTimestampFieldNumber;
+  const uint8_t* data = event.data();
+  const size_t length = event.length();
+  ProtoDecoder decoder(data, length);
+  uint64_t raw_timestamp = 0;
+  bool timestamp_found = false;
+
+  // Speculate on the fact that the timestamp is often the 1st field of the
+  // event.
+  constexpr auto timestampFieldTag = MakeTagVarInt(kTimestampFieldNumber);
+  if (PERFETTO_LIKELY(length > 10 && data[0] == timestampFieldTag)) {
+    // Fastpath.
+    const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
+    timestamp_found = next != data + 1;
+    decoder.Reset(next);
+  } else {
+    // Slowpath.
+    if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
+      timestamp_found = true;
+      raw_timestamp = ts_field.as_uint64();
+    }
+  }
+
+  if (PERFETTO_UNLIKELY(!timestamp_found)) {
+    PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
+    context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+    return;
+  }
+
+  int64_t timestamp = static_cast<int64_t>(raw_timestamp);
+
+  // We don't need to parse this packet, just push it to be sorted with
+  // the timestamp.
+  context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event));
+}
+
+PERFETTO_ALWAYS_INLINE
 void FtraceTokenizer::TokenizeFtraceCompactSched(uint32_t cpu,
                                                  const uint8_t* data,
                                                  size_t size) {
-  protos::pbzero::FtraceEventBundle_CompactSched::Decoder compact(data, size);
-
-  // Build the interning table for next_comm fields.
+  protos::pbzero::FtraceEventBundle::CompactSched::Decoder compact_sched(data,
+                                                                         size);
+  // Build the interning table for comm fields.
   std::vector<StringId> string_table;
   string_table.reserve(512);
-  for (auto it = compact.switch_next_comm_table(); it; it++) {
+  for (auto it = compact_sched.intern_table(); it; it++) {
     StringId value = context_->storage->InternString(*it);
     string_table.push_back(value);
   }
 
+  TokenizeFtraceCompactSchedSwitch(cpu, compact_sched, string_table);
+  TokenizeFtraceCompactSchedWaking(cpu, compact_sched, string_table);
+}
+
+void FtraceTokenizer::TokenizeFtraceCompactSchedSwitch(
+    uint32_t cpu,
+    const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+    const std::vector<StringId>& string_table) {
   // Accumulator for timestamp deltas.
   int64_t timestamp_acc = 0;
 
@@ -117,43 +164,48 @@
     context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
 }
 
-PERFETTO_ALWAYS_INLINE
-void FtraceTokenizer::TokenizeFtraceEvent(uint32_t cpu, TraceBlobView event) {
-  constexpr auto kTimestampFieldNumber =
-      protos::pbzero::FtraceEvent::kTimestampFieldNumber;
-  const uint8_t* data = event.data();
-  const size_t length = event.length();
-  ProtoDecoder decoder(data, length);
-  uint64_t raw_timestamp = 0;
-  bool timestamp_found = false;
+void FtraceTokenizer::TokenizeFtraceCompactSchedWaking(
+    uint32_t cpu,
+    const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+    const std::vector<StringId>& string_table) {
+  // Accumulator for timestamp deltas.
+  int64_t timestamp_acc = 0;
 
-  // Speculate on the fact that the timestamp is often the 1st field of the
-  // event.
-  constexpr auto timestampFieldTag = MakeTagVarInt(kTimestampFieldNumber);
-  if (PERFETTO_LIKELY(length > 10 && data[0] == timestampFieldTag)) {
-    // Fastpath.
-    const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
-    timestamp_found = next != data + 1;
-    decoder.Reset(next);
-  } else {
-    // Slowpath.
-    if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
-      timestamp_found = true;
-      raw_timestamp = ts_field.as_uint64();
-    }
+  // The events' fields are stored in a structure-of-arrays style, using packed
+  // repeated fields. Walk each repeated field in step to recover individual
+  // events.
+  bool parse_error = false;
+  auto timestamp_it = compact.waking_timestamp(&parse_error);
+  auto pid_it = compact.waking_pid(&parse_error);
+  auto tcpu_it = compact.waking_target_cpu(&parse_error);
+  auto prio_it = compact.waking_prio(&parse_error);
+  auto comm_it = compact.waking_comm_index(&parse_error);
+
+  for (; timestamp_it && pid_it && tcpu_it && prio_it && comm_it;
+       ++timestamp_it, ++pid_it, ++tcpu_it, ++prio_it, ++comm_it) {
+    InlineSchedWaking event{};
+
+    // delta-encoded timestamp
+    timestamp_acc += static_cast<int64_t>(*timestamp_it);
+    int64_t event_timestamp = timestamp_acc;
+
+    // index into the interned string table
+    PERFETTO_DCHECK(*comm_it < string_table.size());
+    event.comm = string_table[*comm_it];
+
+    event.pid = *pid_it;
+    event.target_cpu = *tcpu_it;
+    event.prio = *prio_it;
+
+    context_->sorter->PushInlineFtraceEvent(cpu, event_timestamp,
+                                            InlineEvent::SchedWaking(event));
   }
 
-  if (PERFETTO_UNLIKELY(!timestamp_found)) {
-    PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
-    context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
-    return;
-  }
-
-  int64_t timestamp = static_cast<int64_t>(raw_timestamp);
-
-  // We don't need to parse this packet, just push it to be sorted with
-  // the timestamp.
-  context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event));
+  // Check that all packed buffers were decoded correctly, and fully.
+  bool sizes_match =
+      !timestamp_it && !pid_it && !tcpu_it && !prio_it && !comm_it;
+  if (parse_error || !sizes_match)
+    context_->storage->IncrementStats(stats::compact_sched_has_parse_errors);
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
index 30b5a5f..8d1007f 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
@@ -17,8 +17,10 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
 
+#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 #include "src/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -28,13 +30,21 @@
   explicit FtraceTokenizer(TraceProcessorContext* context)
       : context_(context) {}
 
-  void TokenizeFtraceBundle(TraceBlobView);
+  void TokenizeFtraceBundle(TraceBlobView bundle);
 
  private:
-  void TokenizeFtraceEvent(uint32_t cpu, TraceBlobView);
+  void TokenizeFtraceEvent(uint32_t cpu, TraceBlobView event);
   void TokenizeFtraceCompactSched(uint32_t cpu,
                                   const uint8_t* data,
                                   size_t size);
+  void TokenizeFtraceCompactSchedSwitch(
+      uint32_t cpu,
+      const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+      const std::vector<StringId>& string_table);
+  void TokenizeFtraceCompactSchedWaking(
+      uint32_t cpu,
+      const protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact,
+      const std::vector<StringId>& string_table);
 
   TraceProcessorContext* context_;
 };
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index 4c38e99..a4027f6 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -36,15 +36,27 @@
 
 SchedEventTracker::SchedEventTracker(TraceProcessorContext* context)
     : context_(context) {
-  auto* descriptor = GetMessageDescriptorForId(
+  // pre-parse sched_switch
+  auto* switch_descriptor = GetMessageDescriptorForId(
       protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber);
-  PERFETTO_CHECK(descriptor->max_field_id == kSchedSwitchMaxFieldId);
+  PERFETTO_CHECK(switch_descriptor->max_field_id == kSchedSwitchMaxFieldId);
 
   for (size_t i = 1; i <= kSchedSwitchMaxFieldId; i++) {
     sched_switch_field_ids_[i] =
-        context->storage->InternString(descriptor->fields[i].name);
+        context->storage->InternString(switch_descriptor->fields[i].name);
   }
-  sched_switch_id_ = context->storage->InternString(descriptor->name);
+  sched_switch_id_ = context->storage->InternString(switch_descriptor->name);
+
+  // pre-parse sched_waking
+  auto* waking_descriptor = GetMessageDescriptorForId(
+      protos::pbzero::FtraceEvent::kSchedWakingFieldNumber);
+  PERFETTO_CHECK(waking_descriptor->max_field_id == kSchedWakingMaxFieldId);
+
+  for (size_t i = 1; i <= kSchedWakingMaxFieldId; i++) {
+    sched_waking_field_ids_[i] =
+        context->storage->InternString(waking_descriptor->fields[i].name);
+  }
+  sched_waking_id_ = context->storage->InternString(waking_descriptor->name);
 }
 
 SchedEventTracker::~SchedEventTracker() = default;
@@ -130,6 +142,8 @@
   // create slices as normal, but the first per-cpu switch is effectively
   // discarded.
   if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
+    context_->storage->IncrementStats(stats::compact_sched_switch_skipped);
+
     pending_sched->last_pid = next_pid;
     pending_sched->last_utid = next_utid;
     pending_sched->last_prio = next_prio;
@@ -226,6 +240,62 @@
   slices->set_end_state(pending_slice_idx, task_state);
 }
 
+// Processes a sched_waking that was decoded from a compact representation,
+// adding to the raw and instants tables.
+void SchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
+                                               int64_t ts,
+                                               uint32_t wakee_pid,
+                                               int32_t target_cpu,
+                                               int32_t prio,
+                                               StringId comm_id) {
+  // At this stage all events should be globally timestamp ordered.
+  if (ts < context_->event_tracker->max_timestamp()) {
+    PERFETTO_ELOG("sched_waking event out of order by %.4f ms, skipping",
+                  (context_->event_tracker->max_timestamp() - ts) / 1e6);
+    context_->storage->IncrementStats(stats::sched_waking_out_of_order);
+    return;
+  }
+  context_->event_tracker->UpdateMaxTimestamp(ts);
+  PERFETTO_DCHECK(cpu < base::kMaxCpus);
+
+  // We infer the task that emitted the event (i.e. common_pid) from the
+  // scheduling slices. Drop the event if we haven't seen any sched_switch
+  // events for this cpu yet.
+  // Note that if sched_switch wasn't enabled, we will have to skip all
+  // compact waking events.
+  auto* pending_sched = &pending_sched_per_cpu_[cpu];
+  if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
+    context_->storage->IncrementStats(stats::compact_sched_waking_skipped);
+    return;
+  }
+  auto curr_utid = pending_sched->last_utid;
+
+  // Add an entry to the raw table.
+  auto rid = context_->storage->mutable_raw_events()->AddRawEvent(
+      ts, sched_waking_id_, cpu, curr_utid);
+
+  // "success" is hardcoded as always 1 by the kernel, with a TODO to remove it.
+  static constexpr int32_t kHardcodedSuccess = 1;
+
+  using SW = protos::pbzero::SchedWakingFtraceEvent;
+  auto add_raw_arg = [this](RowId row_id, int field_num, Variadic var) {
+    StringId key = sched_waking_field_ids_[static_cast<size_t>(field_num)];
+    context_->args_tracker->AddArg(row_id, key, key, var);
+  };
+  add_raw_arg(rid, SW::kCommFieldNumber, Variadic::String(comm_id));
+  add_raw_arg(rid, SW::kPidFieldNumber, Variadic::Integer(wakee_pid));
+  add_raw_arg(rid, SW::kPrioFieldNumber, Variadic::Integer(prio));
+  add_raw_arg(rid, SW::kSuccessFieldNumber,
+              Variadic::Integer(kHardcodedSuccess));
+  add_raw_arg(rid, SW::kTargetCpuFieldNumber, Variadic::Integer(target_cpu));
+
+  // Add a waking entry to the instants.
+  auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
+  auto* instants = context_->storage->mutable_instants();
+  instants->AddInstantEvent(ts, sched_waking_id_, /*value=*/0, wakee_utid,
+                            RefType::kRefUtid);
+}
+
 void SchedEventTracker::FlushPendingEvents() {
   // TODO(lalitm): the day this method is called before end of trace, don't
   // flush the sched events as they will probably be pushed in the next round
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.h b/src/trace_processor/importers/ftrace/sched_event_tracker.h
index 46acb8f..3c100b6 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.h
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.h
@@ -59,6 +59,16 @@
                               int32_t next_prio,
                               StringId next_comm_id);
 
+  // This method is called when parsing a sched_waking encoded in the compact
+  // format. Note that the default encoding is handled by
+  // |EventTracker::PushInstant|.
+  void PushSchedWakingCompact(uint32_t cpu,
+                              int64_t ts,
+                              uint32_t wakee_pid,
+                              int32_t target_cpu,
+                              int32_t prio,
+                              StringId comm_id);
+
   // Called at the end of trace to flush any events which are pending to the
   // storage.
   void FlushPendingEvents();
@@ -99,6 +109,10 @@
   std::array<StringId, kSchedSwitchMaxFieldId + 1> sched_switch_field_ids_;
   StringId sched_switch_id_;
 
+  static constexpr uint8_t kSchedWakingMaxFieldId = 5;
+  std::array<StringId, kSchedWakingMaxFieldId + 1> sched_waking_field_ids_;
+  StringId sched_waking_id_;
+
   TraceProcessorContext* const context_;
 };
 
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.cc b/src/trace_processor/importers/proto/heap_graph_walker.cc
index 5805b77..8655d85 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker.cc
@@ -72,6 +72,7 @@
 
 void HeapGraphWalker::MarkRoot(int64_t row) {
   Node& n = GetNode(row);
+  n.root = true;
   ReachableNode(&n);
 }
 
@@ -96,7 +97,9 @@
 }
 
 int64_t HeapGraphWalker::RetainedSize(const Component& component) {
-  int64_t retained_size = static_cast<int64_t>(component.unique_retained_size);
+  int64_t retained_size =
+      static_cast<int64_t>(component.unique_retained_size) +
+      static_cast<int64_t>(component.unique_retained_root_size);
   for (const int64_t child_component_id : component.children_components) {
     const Component& child_component =
         components_[static_cast<size_t>(child_component_id)];
@@ -150,6 +153,8 @@
     // A node can never be part of two components.
     PERFETTO_CHECK(stack_elem->component == -1);
     stack_elem->component = component_id;
+    if (stack_elem->root)
+      component.root = true;
   } while (stack_elem != node);
 
   for (Node* elem : component_nodes) {
@@ -184,11 +189,20 @@
           components_[static_cast<size_t>(grand_component_id)];
       grand_component.pending_nodes -= count;
       if (grand_component.pending_nodes == 0) {
-        component.unique_retained_size += grand_component.unique_retained_size;
-        if (IsUniqueOwner(component_to_node, count, grand_component_id,
-                          dc.last_node_row)) {
-          unique_retained_by_node[dc.last_node_row] +=
+        component.unique_retained_root_size +=
+            grand_component.unique_retained_root_size;
+        if (grand_component.root) {
+          component.unique_retained_root_size +=
               grand_component.unique_retained_size;
+        } else {
+          component.unique_retained_size +=
+              grand_component.unique_retained_size;
+
+          if (IsUniqueOwner(component_to_node, count, grand_component_id,
+                            dc.last_node_row)) {
+            unique_retained_by_node[dc.last_node_row] +=
+                grand_component.unique_retained_size;
+          }
         }
         grand_component.children_components.clear();
         component.children_components.erase(grand_component_id);
@@ -200,22 +214,22 @@
     child_component.incoming_edges -= count;
     child_component.pending_nodes -= count;
 
-    if (child_component.orig_incoming_edges == count) {
+    if (child_component.pending_nodes == 0) {
       PERFETTO_CHECK(child_component.incoming_edges == 0);
-      PERFETTO_CHECK(child_component.pending_nodes == 0);
 
-      component.unique_retained_size += child_component.unique_retained_size;
-      if (count == 1) {
-        unique_retained_by_node[dc.last_node_row] +=
+      component.unique_retained_root_size +=
+          child_component.unique_retained_root_size;
+      if (child_component.root) {
+        component.unique_retained_root_size +=
             child_component.unique_retained_size;
-      }
-    } else if (child_component.pending_nodes == 0) {
-      PERFETTO_CHECK(child_component.incoming_edges == 0);
-      component.unique_retained_size += child_component.unique_retained_size;
-      if (IsUniqueOwner(component_to_node, count, child_component_id,
-                        dc.last_node_row)) {
-        unique_retained_by_node[dc.last_node_row] +=
-            child_component.unique_retained_size;
+      } else {
+        component.unique_retained_size += child_component.unique_retained_size;
+
+        if (IsUniqueOwner(component_to_node, count, child_component_id,
+                          dc.last_node_row)) {
+          unique_retained_by_node[dc.last_node_row] +=
+              child_component.unique_retained_size;
+        }
       }
       component.children_components.erase(child_component_id);
     } else {
@@ -228,9 +242,11 @@
 
   size_t parents = component.orig_incoming_edges;
   // If this has no parents, but does not retain a node, we know that no other
-  // node can retain this node. Add 1 to poison that node.
-  if (parents == 0)
-    parents = 1;
+  // node can uniquely retain this node. Add 1 to poison that node.
+  // If this is a root, but it does not retain a node, we also know that no
+  // node can uniquely retain that node.
+  if (parents == 0 || component.root)
+    parents += 1;
   for (const int64_t child_component_id : component.children_components) {
     Component& child_component =
         components_[static_cast<size_t>(child_component_id)];
@@ -256,6 +272,7 @@
   node_stack_.push_back(node);
   node->on_stack = true;
   for (Node* child : node->children) {
+    PERFETTO_CHECK(child->reachable);
     if (child->node_index == 0) {
       FindSCC(child);
       if (child->lowlink < node->lowlink)
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.h b/src/trace_processor/importers/proto/heap_graph_walker.h
index 57d4550..b23f2a3 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.h
+++ b/src/trace_processor/importers/proto/heap_graph_walker.h
@@ -135,15 +135,20 @@
 
     bool reachable = false;
     bool on_stack = false;
+    bool root = false;
   };
 
   struct Component {
+    uint64_t self_size = 0;
     uint64_t unique_retained_size = 0;
+    uint64_t unique_retained_root_size = 0;
     size_t incoming_edges = 0;
     size_t orig_incoming_edges = 0;
     size_t pending_nodes = 0;
     std::set<int64_t> children_components;
     uint64_t lowlink = 0;
+
+    bool root = false;
   };
 
   Node& GetNode(int64_t id) { return nodes_[static_cast<size_t>(id)]; }
diff --git a/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
index 74b98e7..7ec9c09 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
@@ -23,6 +23,9 @@
 namespace trace_processor {
 namespace {
 
+using ::testing::UnorderedElementsAre;
+using ::testing::UnorderedElementsAreArray;
+
 class HeapGraphWalkerTestDelegate : public HeapGraphWalker::Delegate {
  public:
   ~HeapGraphWalkerTestDelegate() override = default;
@@ -68,7 +71,7 @@
 //   2   3   |
 //   ^   ^   |
 //    \ /    |
-//     4     |
+//     4R    |
 TEST(HeapGraphWalkerTest, Diamond) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -96,10 +99,10 @@
   EXPECT_EQ(delegate.UniqueRetained(4), 10);
 }
 
-// 1     2  |
-// ^     ^  |
-// \    /   |
-// 3<->4    |
+// 1       2  |
+// ^       ^  |
+//  \     /   |
+//  3R<->4    |
 TEST(HeapGraphWalkerTest, Loop) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -127,7 +130,7 @@
   EXPECT_EQ(delegate.UniqueRetained(4), 6);
 }
 
-//    1     |
+//    1R    |
 //    ^\    |
 //   /  v   |
 //   3<-2   |
@@ -157,10 +160,10 @@
 // 1      |
 // ^      |
 // |      |
-// 2  4   |
-// ^  ^   |
-// |  |   |
-// 3  5   |
+// 2   4  |
+// ^   ^  |
+// |   |  |
+// 3R  5  |
 TEST(HeapGraphWalkerTest, Disconnected) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -201,7 +204,7 @@
 //    4   5    |
 //    ^   ^    |
 //    \  /     |
-//      6      |
+//      6R     |
 TEST(HeapGraphWalkerTest, Complex) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -244,7 +247,7 @@
 //  2<-> 3   |
 //  ^        |
 //  |        |
-//  4        |
+//  4R       |
 TEST(HeapGraphWalkerTest, SharedInComponent) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -268,6 +271,7 @@
   EXPECT_EQ(delegate.Retained(4), 10);
 
   EXPECT_EQ(delegate.UniqueRetained(1), 1);
+  // TODO(fmayer): this should be 6, as it breaks away the component.
   EXPECT_EQ(delegate.UniqueRetained(2), 2);
   EXPECT_EQ(delegate.UniqueRetained(3), 3);
   EXPECT_EQ(delegate.UniqueRetained(4), 10);
@@ -276,7 +280,7 @@
 // 1 <- 2   |
 // ^    ^   |
 // |    |   |
-// 3<-> 4   |
+// 3<-> 4R  |
 TEST(HeapGraphWalkerTest, TwoPaths) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -308,7 +312,7 @@
 //    1     |
 //   ^^     |
 //  /  \    |
-// 2    3   |
+// 2R   3R  |
 TEST(HeapGraphWalkerTest, Diverge) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -335,7 +339,7 @@
 //    1            |
 //   ^^            |
 //  /  \           |
-// 2    3 (dead)   |
+// 2R   3 (dead)   |
 TEST(HeapGraphWalkerTest, Dead) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
@@ -356,6 +360,229 @@
   EXPECT_EQ(delegate.UniqueRetained(2), 3);
 }
 
+//    2<->3  |
+//    ^      |
+//    |      |
+//    1R     |
+TEST(HeapGraphWalkerTest, Component) {
+  HeapGraphWalkerTestDelegate delegate;
+  HeapGraphWalker walker(&delegate);
+  walker.AddNode(1, 1);
+  walker.AddNode(2, 2);
+  walker.AddNode(3, 3);
+
+  walker.AddEdge(1, 2);
+  walker.AddEdge(2, 3);
+  walker.AddEdge(3, 2);
+
+  walker.MarkRoot(1);
+  walker.CalculateRetained();
+
+  EXPECT_EQ(delegate.Retained(1), 6);
+  EXPECT_EQ(delegate.Retained(2), 5);
+  EXPECT_EQ(delegate.Retained(3), 5);
+
+  EXPECT_EQ(delegate.UniqueRetained(1), 6);
+  // TODO(fmayer): this should be 5, as this breaks away the component.
+  EXPECT_EQ(delegate.UniqueRetained(2), 2);
+  EXPECT_EQ(delegate.UniqueRetained(3), 3);
+}
+
+//    2<->3R |
+//    ^      |
+//    |      |
+//    1R     |
+TEST(HeapGraphWalkerTest, ComponentWithRoot) {
+  HeapGraphWalkerTestDelegate delegate;
+  HeapGraphWalker walker(&delegate);
+  walker.AddNode(1, 1);
+  walker.AddNode(2, 2);
+  walker.AddNode(3, 3);
+
+  walker.AddEdge(1, 2);
+  walker.AddEdge(2, 3);
+  walker.AddEdge(3, 2);
+
+  walker.MarkRoot(1);
+  walker.MarkRoot(3);
+  walker.CalculateRetained();
+
+  EXPECT_EQ(delegate.Retained(1), 6);
+  EXPECT_EQ(delegate.Retained(2), 5);
+  EXPECT_EQ(delegate.Retained(3), 5);
+
+  EXPECT_EQ(delegate.UniqueRetained(1), 1);
+  EXPECT_EQ(delegate.UniqueRetained(2), 2);
+  EXPECT_EQ(delegate.UniqueRetained(3), 3);
+}
+
+// R
+// 2 <-  3   |
+//  ^   ^   |
+//   \ /    |
+//    1R    |
+TEST(HeapGraphWalkerTest, TwoRoots) {
+  HeapGraphWalkerTestDelegate delegate;
+  HeapGraphWalker walker(&delegate);
+  walker.AddNode(1, 1);
+  walker.AddNode(2, 2);
+  walker.AddNode(3, 3);
+
+  walker.AddEdge(1, 2);
+  walker.AddEdge(1, 3);
+  walker.AddEdge(3, 2);
+
+  walker.MarkRoot(1);
+  walker.MarkRoot(2);
+  walker.CalculateRetained();
+
+  EXPECT_EQ(delegate.Retained(1), 6);
+  EXPECT_EQ(delegate.Retained(2), 2);
+  EXPECT_EQ(delegate.Retained(3), 5);
+
+  EXPECT_EQ(delegate.UniqueRetained(1), 4);
+  EXPECT_EQ(delegate.UniqueRetained(2), 2);
+  EXPECT_EQ(delegate.UniqueRetained(3), 3);
+}
+
+// Call a function for every set in the powerset or the cartesian product
+// of v with itself.
+// TODO(fmayer): Find a smarter way to generate all graphs.
+template <typename F>
+void SquarePowerSet(const std::vector<int64_t>& v, F fn) {
+  for (uint64_t subset = 0; subset < pow(2, pow(v.size(), 2)); ++subset) {
+    std::vector<std::pair<int64_t, int64_t>> ps;
+    uint64_t node = 0;
+    for (int64_t n1 : v) {
+      for (int64_t n2 : v) {
+        if ((1 << node++) & subset)
+          ps.emplace_back(n1, n2);
+      }
+    }
+    fn(ps);
+  }
+}
+
+// Call a function for every set in the powerset.
+template <typename F>
+void PowerSet(const std::vector<int64_t>& v, F fn) {
+  for (uint64_t subset = 0; subset < pow(2, v.size()); ++subset) {
+    std::vector<int64_t> ps;
+    uint64_t node = 0;
+    for (int64_t n : v) {
+      if ((1 << node++) & subset)
+        ps.emplace_back(n);
+    }
+    fn(ps);
+  }
+}
+
+TEST(PowerSetTest, Simple) {
+  std::vector<int64_t> s = {0, 1, 2};
+  std::vector<std::vector<int64_t>> ps;
+  PowerSet(s, [&ps](const std::vector<int64_t>& x) { ps.emplace_back(x); });
+  EXPECT_THAT(ps, UnorderedElementsAre(std::vector<int64_t>{},      //
+                                       std::vector<int64_t>{0},     //
+                                       std::vector<int64_t>{1},     //
+                                       std::vector<int64_t>{2},     //
+                                       std::vector<int64_t>{0, 1},  //
+                                       std::vector<int64_t>{0, 2},  //
+                                       std::vector<int64_t>{1, 2},  //
+                                       std::vector<int64_t>{0, 1, 2}));
+}
+
+TEST(SquarePowerSetTest, Simple) {
+  std::vector<int64_t> s = {0, 1};
+  std::vector<std::vector<std::pair<int64_t, int64_t>>> ps;
+  SquarePowerSet(s, [&ps](const std::vector<std::pair<int64_t, int64_t>>& x) {
+    ps.emplace_back(x);
+  });
+
+  std::vector<std::pair<int64_t, int64_t>> expected[] = {
+      {},                        //
+      {{0, 0}},                  //
+      {{0, 1}},                  //
+      {{1, 0}},                  //
+      {{1, 1}},                  //
+      {{0, 0}, {0, 1}},          //
+      {{0, 0}, {1, 0}},          //
+      {{0, 0}, {1, 1}},          //
+      {{0, 1}, {1, 0}},          //
+      {{0, 1}, {1, 1}},          //
+      {{1, 0}, {1, 1}},          //
+      {{0, 0}, {0, 1}, {1, 0}},  //
+      {{0, 0}, {0, 1}, {1, 1}},  //
+      {{0, 0}, {1, 0}, {1, 1}},  //
+      {{0, 1}, {1, 0}, {1, 1}},  //
+      {{0, 0}, {0, 1}, {1, 0}, {1, 1}}};
+  EXPECT_THAT(ps, UnorderedElementsAreArray(expected));
+}
+
+// Generate all graphs with 4 nodes, and assert that deleting one node frees
+// up more memory than that node's unique retained.
+TEST(HeapGraphWalkerTest, DISABLED_AllGraphs) {
+  std::vector<int64_t> nodes{1, 2, 3, 4};
+  std::vector<uint64_t> sizes{0, 1, 2, 3, 4};
+  PowerSet(nodes, [&nodes, &sizes](const std::vector<int64_t>& roots) {
+    SquarePowerSet(
+        nodes, [&nodes, &sizes,
+                &roots](const std::vector<std::pair<int64_t, int64_t>>& edges) {
+          HeapGraphWalkerTestDelegate delegate;
+          HeapGraphWalker walker(&delegate);
+
+          HeapGraphWalkerTestDelegate delegate2;
+          HeapGraphWalker walker2(&delegate2);
+
+          for (int64_t node : nodes) {
+            walker.AddNode(node, sizes[static_cast<size_t>(node)]);
+            // walker2 leaves out node 1.
+            if (node != 1)
+              walker2.AddNode(node, sizes[static_cast<size_t>(node)]);
+          }
+
+          for (const auto& p : edges) {
+            walker.AddEdge(p.first, p.second);
+            // walker2 leaves out node 1.
+            if (p.first != 1 && p.second != 1)
+              walker2.AddEdge(p.first, p.second);
+          }
+
+          for (int64_t r : roots) {
+            walker.MarkRoot(r);
+            // walker2 leaves out node 1.
+            if (r != 1)
+              walker2.MarkRoot(r);
+          }
+
+          walker.CalculateRetained();
+          // We do not need to CalculateRetained on walker2, because we only
+          // get the reachable nodes.
+
+          int64_t reachable = 0;
+          int64_t reachable2 = 0;
+
+          ASSERT_FALSE(delegate2.Reachable(1));
+          for (int64_t n : nodes) {
+            if (delegate.Reachable(n))
+              reachable += sizes[static_cast<size_t>(n)];
+            if (delegate2.Reachable(n))
+              reachable2 += sizes[static_cast<size_t>(n)];
+          }
+          EXPECT_LE(reachable2, reachable);
+          if (delegate.Reachable(1)) {
+            // TODO(fmayer): This should be EXPECT_EQ, but we do currently
+            // undercount the unique retained, because we do not include the
+            // memory that could get freed by the component being broken apart.
+            EXPECT_LE(delegate.UniqueRetained(1), reachable - reachable2)
+                << "roots: " << testing::PrintToString(roots)
+                << ", edges: " << testing::PrintToString(edges);
+          } else {
+            EXPECT_EQ(reachable2, reachable);
+          }
+        });
+  });
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index 8ee597b..b6bc5a6 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -22,6 +22,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "perfetto/base/compiler.h"
 #include "perfetto/protozero/proto_decoder.h"
 #include "src/trace_processor/stack_profile_tracker.h"
 #include "src/trace_processor/trace_blob_view.h"
@@ -31,13 +32,13 @@
 namespace perfetto {
 namespace trace_processor {
 
-#if PERFETTO_DCHECK_IS_ON() && defined(__GNUC__)
-// When called from GetOrCreateDecoder(), __PRETTY_FUNCTION__ (supported by GCC
-// + clang) should include the stringified name of the MessageType.
-#define PERFETTO_TYPE_IDENTIFIER __PRETTY_FUNCTION__
-#else  // PERFETTO_DCHECK_IS_ON() && defined(__GNUC__)
+#if PERFETTO_DCHECK_IS_ON()
+// When called from GetOrCreateDecoder(), should include the stringified name of
+// the MessageType.
+#define PERFETTO_TYPE_IDENTIFIER PERFETTO_DEBUG_FUNCTION_IDENTIFIER()
+#else  // PERFETTO_DCHECK_IS_ON()
 #define PERFETTO_TYPE_IDENTIFIER nullptr
-#endif  // PERFETTO_DCHECK_IS_ON() && defined(__GNUC__)
+#endif  // PERFETTO_DCHECK_IS_ON()
 
 class PacketSequenceState {
  public:
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 0811da4..bd19c0e 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -630,20 +630,26 @@
   for (auto addr_it = module_symbols.address_symbols(); addr_it; ++addr_it) {
     protos::pbzero::AddressSymbols::Decoder address_symbols(*addr_it);
 
-    ssize_t frame_row = -1;
+    uint32_t symbol_set_id = context_->storage->symbol_table().size();
+    bool frame_found = false;
     for (int64_t mapping_row : mapping_rows) {
-      frame_row = context_->storage->stack_profile_frames().FindFrameRow(
-          static_cast<size_t>(mapping_row), address_symbols.address());
-      if (frame_row != -1)
-        break;
+      std::vector<int64_t> frame_rows =
+          context_->storage->stack_profile_frames().FindFrameRow(
+              static_cast<size_t>(mapping_row), address_symbols.address());
+
+      for (const int64_t frame_row : frame_rows) {
+        PERFETTO_DCHECK(frame_row >= 0);
+        context_->storage->mutable_stack_profile_frames()->SetSymbolSetId(
+            static_cast<size_t>(frame_row), symbol_set_id);
+        frame_found = true;
+      }
     }
-    if (frame_row == -1) {
+
+    if (!frame_found) {
       context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
       continue;
     }
-    uint32_t symbol_set_id = context_->storage->symbol_table().size();
-    context_->storage->mutable_stack_profile_frames()->SetSymbolSetId(
-        static_cast<size_t>(frame_row), symbol_set_id);
+
     for (auto line_it = address_symbols.lines(); line_it; ++line_it) {
       protos::pbzero::Line::Decoder line(*line_it);
       context_->storage->mutable_symbol_table()->Insert(
diff --git a/src/trace_processor/instants_table.cc b/src/trace_processor/instants_table.cc
index 279e36c..53ddd80 100644
--- a/src/trace_processor/instants_table.cc
+++ b/src/trace_processor/instants_table.cc
@@ -50,15 +50,15 @@
 int InstantsTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
   info->estimated_cost =
       static_cast<uint32_t>(storage_->instants().instant_count());
+  info->sqlite_omit_order_by = true;
 
   // Only the string columns are handled by SQLite
-  info->order_by_consumed = true;
   size_t name_index = schema().ColumnIndexFromName("name");
   size_t ref_type_index = schema().ColumnIndexFromName("ref_type");
   for (size_t i = 0; i < qc.constraints().size(); i++) {
-    info->omit[i] =
-        qc.constraints()[i].iColumn != static_cast<int>(name_index) &&
-        qc.constraints()[i].iColumn != static_cast<int>(ref_type_index);
+    info->sqlite_omit_constraint[i] =
+        qc.constraints()[i].column != static_cast<int>(name_index) &&
+        qc.constraints()[i].column != static_cast<int>(ref_type_index);
   }
 
   return SQLITE_OK;
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index bfa95de..28d6f01 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -32,6 +32,7 @@
   "android/android_startup_cpu.sql",
   "android/android_package_list.sql",
   "android/heap_profile_callsites.sql",
+  "android/java_heap_stats.sql",
   "android/process_unagg_mem_view.sql",
   "android/process_mem.sql",
   "android/mem_stats_priority_breakdown.sql",
diff --git a/src/trace_processor/metrics/android/heap_profile_callsites.sql b/src/trace_processor/metrics/android/heap_profile_callsites.sql
index a0cd0c4..ce2aeb4 100644
--- a/src/trace_processor/metrics/android/heap_profile_callsites.sql
+++ b/src/trace_processor/metrics/android/heap_profile_callsites.sql
@@ -14,6 +14,17 @@
 -- limitations under the License.
 --
 
+CREATE VIEW memory_delta AS
+SELECT upid, SUM(size) AS delta
+FROM heap_profile_allocation
+GROUP BY 1;
+
+CREATE VIEW memory_total AS
+SELECT upid, SUM(size) AS total
+FROM heap_profile_allocation
+WHERE size > 0
+GROUP BY 1;
+
 -- Join frames with symbols and mappings to get a textual representation.
 CREATE TABLE symbolized_frame AS
 SELECT
@@ -205,9 +216,14 @@
 SELECT HeapProfileCallsites_InstanceStats(
     'pid', process.pid,
     'process_name', process.name,
-    'callsites', repeated_callsite_proto
+    'callsites', repeated_callsite_proto,
+    'profile_delta_bytes', memory_delta.delta,
+    'profile_total_bytes', memory_total.total
 ) AS instance_stats_proto
-FROM process_callsite_proto JOIN process USING (upid);
+FROM process_callsite_proto
+JOIN memory_total USING (upid)
+JOIN memory_delta USING (upid)
+JOIN process USING (upid);
 
 CREATE VIEW heap_profile_callsites_output AS
 SELECT HeapProfileCallsites(
diff --git a/src/trace_processor/metrics/android/java_heap_stats.sql b/src/trace_processor/metrics/android/java_heap_stats.sql
new file mode 100644
index 0000000..d8f2f34
--- /dev/null
+++ b/src/trace_processor/metrics/android/java_heap_stats.sql
@@ -0,0 +1,58 @@
+--
+-- Copyright 2019 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     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.
+--
+
+CREATE VIEW total_size_samples AS
+SELECT upid, graph_sample_ts, SUM(self_size) AS total_size
+FROM heap_graph_object
+GROUP BY 1, 2;
+
+CREATE VIEW total_reachable_size_samples AS
+SELECT upid, graph_sample_ts, SUM(self_size) AS total_reachable_size
+FROM heap_graph_object
+WHERE reachable = TRUE
+GROUP BY 1, 2;
+
+CREATE TABLE heap_graph_samples AS
+SELECT upid, graph_sample_ts, total_size, total_reachable_size
+FROM total_size_samples JOIN total_reachable_size_samples
+USING (upid, graph_sample_ts);
+
+CREATE VIEW heap_graph_sample_protos AS
+SELECT
+  upid,
+  JavaHeapStats_Sample(
+    'ts', graph_sample_ts,
+    'heap_size', total_size,
+    'reachable_heap_size', total_reachable_size
+  ) sample_proto
+FROM heap_graph_samples;
+
+CREATE TABLE heap_graph_instance_stats AS
+SELECT
+  upid,
+  process.name process_name,
+  RepeatedField(sample_proto) AS sample_protos
+FROM heap_graph_sample_protos JOIN process USING (upid)
+GROUP BY 1, 2;
+
+CREATE VIEW java_heap_stats_output AS
+SELECT JavaHeapStats(
+  'instance_stats', RepeatedField(JavaHeapStats_InstanceStats(
+    'upid', upid,
+    'process_name', process_name,
+    'samples', sample_protos
+  )))
+FROM heap_graph_instance_stats;
diff --git a/src/trace_processor/metrics/metrics.descriptor.h b/src/trace_processor/metrics/metrics.descriptor.h
index 5700d0b..df7696c 100644
--- a/src/trace_processor/metrics/metrics.descriptor.h
+++ b/src/trace_processor/metrics/metrics.descriptor.h
@@ -12,14 +12,14 @@
 // SHA1(tools/gen_binary_descriptors)
 // 192b582ae52bb07b3d3ba66a94bcfd3127a5f42f
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// 3617cd492d3a717441de21b0b04ce363e0669f57
+// 4279eeace6a7d9647e484e65b8265ea211a02b6c
 
 // This is the proto Metrics encoded as a ProtoFileDescriptor to allow
 // for reflection without libprotobuf full/non-lite protos.
 
 namespace perfetto {
 
-constexpr std::array<uint8_t, 9666> kMetricsDescriptor{
+constexpr std::array<uint8_t, 10312> kMetricsDescriptor{
     {0x0a, 0x98, 0x03, 0x0a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
      0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
@@ -540,14 +540,14 @@
      0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x54,
      0x6f, 0x46, 0x69, 0x72, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52,
      0x0c, 0x74, 0x6f, 0x46, 0x69, 0x72, 0x73, 0x74, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xd7, 0x06, 0x0a, 0x3c, 0x70, 0x72,
+     0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xb7, 0x07, 0x0a, 0x3c, 0x70, 0x72,
      0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
      0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70,
      0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73,
      0x69, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f,
      0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x22, 0x81, 0x06, 0x0a, 0x14, 0x48, 0x65, 0x61, 0x70,
+     0x74, 0x6f, 0x73, 0x22, 0xe1, 0x06, 0x0a, 0x14, 0x48, 0x65, 0x61, 0x70,
      0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73,
      0x69, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74,
      0x61, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01,
@@ -599,7 +599,7 @@
      0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73,
      0x69, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
      0x73, 0x52, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x41, 0x6c, 0x6c, 0x6f,
-     0x63, 0x73, 0x1a, 0x92, 0x01, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61,
+     0x63, 0x73, 0x1a, 0xf2, 0x01, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61,
      0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03,
      0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70,
      0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
@@ -611,7 +611,15 @@
      0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69,
      0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x2e,
      0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x52, 0x09, 0x63, 0x61,
-     0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x42, 0x02, 0x48, 0x03, 0x0a,
+     0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70,
+     0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+     0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x11, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c,
+     0x74, 0x61, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x70,
+     0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+     0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x11, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x6f, 0x74,
+     0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x42, 0x02, 0x48, 0x03, 0x0a,
      0x8c, 0x02, 0x0a, 0x32, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
      0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72,
      0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
@@ -656,176 +664,222 @@
      0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61,
      0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
      0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x02, 0x48,
-     0x03, 0x0a, 0xee, 0x0f, 0x0a, 0x25, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x1a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x62, 0x61,
-     0x74, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x2f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x36, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x75, 0x6e, 0x61,
-     0x67, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x2f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x67, 0x72, 0x6f,
-     0x77, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x69, 0x6f, 0x6e, 0x5f, 0x6d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
-     0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b,
-     0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x1a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70,
-     0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75,
-     0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x1a, 0x3c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
-     0x68, 0x65, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
-     0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x03, 0x0a, 0xa4, 0x03, 0x0a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
      0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
      0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69,
-     0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x39, 0x70, 0x72,
+     0x64, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f,
+     0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+     0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x22, 0xd5, 0x02, 0x0a, 0x0d, 0x4a, 0x61, 0x76,
+     0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x53,
+     0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x73,
+     0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c,
+     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61,
+     0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61,
+     0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0d, 0x69, 0x6e,
+     0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x1a,
+     0x65, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x0e, 0x0a,
+     0x02, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x74,
+     0x73, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69,
+     0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x68, 0x65,
+     0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65,
+     0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x70,
+     0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x11, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x48, 0x65,
+     0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x87, 0x01, 0x0a, 0x0d, 0x49,
+     0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73,
+     0x12, 0x12, 0x0a, 0x04, 0x75, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x0d, 0x52, 0x04, 0x75, 0x70, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x07, 0x73,
+     0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
+     0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x48,
+     0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x53, 0x61, 0x6d,
+     0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
+     0x42, 0x02, 0x48, 0x03, 0x0a, 0xed, 0x10, 0x0a, 0x25, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x1a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x2f, 0x62, 0x61, 0x74, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x2f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72,
      0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62,
-     0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf1, 0x01, 0x0a, 0x0d,
-     0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
-     0x61, 0x12, 0x50, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73,
-     0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01,
-     0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54,
-     0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-     0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65, 0x72, 0x72, 0x6f,
-     0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
-     0x2a, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x64, 0x75, 0x72,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
-     0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63, 0x65, 0x55, 0x75,
-     0x69, 0x64, 0x1a, 0x43, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
-     0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x69,
-     0x64, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x69, 0x64,
-     0x78, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
-     0xb8, 0x08, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x73, 0x12, 0x48, 0x0a, 0x0c, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x74, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x36,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f,
+     0x75, 0x6e, 0x61, 0x67, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
+     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f,
+     0x67, 0x72, 0x6f, 0x77, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x69, 0x6f,
+     0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72,
+     0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+     0x6c, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x2f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x5f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     0x34, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x74, 0x61,
+     0x72, 0x74, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x3c, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x2f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x66,
+     0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65,
+     0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
+     0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     0x39, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x75, 0x6e, 0x73,
+     0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72,
+     0x61, 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x35,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6a, 0x61, 0x76, 0x61,
+     0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf1, 0x01, 0x0a, 0x0d, 0x54, 0x72,
+     0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
+     0x50, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61,
+     0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03,
+     0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61,
+     0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45,
+     0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x53,
+     0x74, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x2a, 0x0a,
+     0x11, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74,
+     0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74,
+     0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x72, 0x61,
+     0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x09, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64,
+     0x1a, 0x43, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x78,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x69, 0x64, 0x78, 0x12,
+     0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x80, 0x09,
+     0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x12, 0x48, 0x0a, 0x0c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x5f, 0x62, 0x61, 0x74, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x4d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x52, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x42, 0x61, 0x74, 0x74, 0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x06, 0x20, 0x01, 0x28,
+     0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72,
+     0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75,
+     0x12, 0x45, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f,
+     0x6d, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d,
+     0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52,
+     0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x12,
+     0x5c, 0x0a, 0x11, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6d,
+     0x65, 0x6d, 0x5f, 0x75, 0x6e, 0x61, 0x67, 0x67, 0x18, 0x0b, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
      0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x4d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x42, 0x61, 0x74, 0x74, 0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x06, 0x20,
-     0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x6e,
+     0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x4d, 0x65, 0x6d, 0x55, 0x6e, 0x61, 0x67, 0x67, 0x12, 0x55, 0x0a,
+     0x14, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x70, 0x61, 0x63,
+     0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0c, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
      0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43,
-     0x70, 0x75, 0x12, 0x45, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65,
-     0x6d, 0x12, 0x5c, 0x0a, 0x11, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x75, 0x6e, 0x61, 0x67, 0x67, 0x18, 0x0b,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
-     0x55, 0x6e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64,
-     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x55, 0x6e, 0x61, 0x67, 0x67, 0x12,
-     0x55, 0x0a, 0x14, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x70,
-     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18,
-     0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61,
-     0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x12, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69,
-     0x73, 0x74, 0x12, 0x5b, 0x0a, 0x16, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x67, 0x72,
-     0x6f, 0x77, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25,
-     0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x77, 0x74,
-     0x68, 0x52, 0x14, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x77, 0x74, 0x68, 0x12,
-     0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x69,
-     0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
+     0x4c, 0x69, 0x73, 0x74, 0x52, 0x12, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74,
+     0x12, 0x5b, 0x0a, 0x16, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f,
+     0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x77,
+     0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
      0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f,
-     0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0b, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6c, 0x6d, 0x6b, 0x18, 0x08,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x4d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x4c, 0x6d, 0x6b, 0x12, 0x4d, 0x0a, 0x10, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x5f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18,
-     0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72,
-     0x52, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x50, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e,
-     0x0a, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x73, 0x74,
-     0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x52, 0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53,
-     0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x5b, 0x0a, 0x16, 0x68, 0x65,
-     0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63,
-     0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61,
-     0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c,
-     0x73, 0x69, 0x74, 0x65, 0x73, 0x52, 0x14, 0x68, 0x65, 0x61, 0x70, 0x50,
+     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x77, 0x74, 0x68, 0x52,
+     0x14, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x77, 0x74, 0x68, 0x12, 0x42, 0x0a,
+     0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x69, 0x6f, 0x6e,
+     0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72,
+     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x4d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+     0x69, 0x64, 0x49, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6c, 0x6d, 0x6b, 0x18, 0x08, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x4d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d,
+     0x6b, 0x12, 0x4d, 0x0a, 0x10, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x5f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x07, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e,
+     0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61,
+     0x69, 0x6c, 0x73, 0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x50, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x0f,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53,
+     0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x52, 0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74, 0x61,
+     0x72, 0x74, 0x75, 0x70, 0x12, 0x5b, 0x0a, 0x16, 0x68, 0x65, 0x61, 0x70,
+     0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c,
+     0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50,
      0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69,
-     0x74, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65,
-     0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20,
+     0x74, 0x65, 0x73, 0x52, 0x14, 0x68, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f,
+     0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65,
+     0x73, 0x12, 0x45, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x6d,
+     0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63,
+     0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x74,
+     0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+     0x12, 0x54, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+     0x69, 0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18,
+     0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+     0x55, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64,
+     0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x12, 0x75, 0x6e, 0x73, 0x79,
+     0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d,
+     0x65, 0x73, 0x12, 0x46, 0x0a, 0x0f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68,
+     0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x11, 0x20,
      0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72,
-     0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
-     0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
-     0x74, 0x61, 0x12, 0x54, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62,
-     0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
-     0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x55, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a,
-     0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x12, 0x75, 0x6e,
-     0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x73, 0x2a, 0x06, 0x08, 0xc2, 0x03, 0x10, 0xf4, 0x03,
-     0x2a, 0x06, 0x08, 0xf4, 0x03, 0x10, 0xe9, 0x07, 0x4a, 0x04, 0x08, 0x04,
-     0x10, 0x05, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e,
-     0x10, 0x0f, 0x42, 0x02, 0x48, 0x03}};
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61,
+     0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52,
+     0x0d, 0x6a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61,
+     0x74, 0x73, 0x2a, 0x06, 0x08, 0xc2, 0x03, 0x10, 0xf4, 0x03, 0x2a, 0x06,
+     0x08, 0xf4, 0x03, 0x10, 0xe9, 0x07, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05,
+     0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f,
+     0x42, 0x02, 0x48, 0x03}};
 
 }  // namespace perfetto
 
diff --git a/src/trace_processor/process_table.cc b/src/trace_processor/process_table.cc
index 9d0129a..adc9d1e 100644
--- a/src/trace_processor/process_table.cc
+++ b/src/trace_processor/process_table.cc
@@ -61,7 +61,7 @@
   // because we can do that filter efficiently.
   const auto& cs = qc.constraints();
   auto fn = [](const QueryConstraints::Constraint& c) {
-    return c.iColumn == Column::kUpid && sqlite_utils::IsOpEq(c.op);
+    return c.column == Column::kUpid && sqlite_utils::IsOpEq(c.op);
   };
   info->estimated_cost = std::find_if(cs.begin(), cs.end(), fn) != cs.end()
                              ? 1
@@ -74,57 +74,56 @@
 
 int ProcessTable::Cursor::Filter(const QueryConstraints& qc,
                                  sqlite3_value** argv) {
-  min = 0;
-  max = static_cast<uint32_t>(storage_->process_count()) - 1;
-  desc = false;
+  min_ = 0;
+  max_ = static_cast<uint32_t>(storage_->process_count());
+  desc_ = false;
 
   for (size_t j = 0; j < qc.constraints().size(); j++) {
     const auto& cs = qc.constraints()[j];
-    if (cs.iColumn == Column::kUpid) {
+    if (cs.column == Column::kUpid) {
       auto constraint_upid = static_cast<UniquePid>(sqlite3_value_int(argv[j]));
       // Set the range of upids that we are interested in, based on the
-      // constraints in the query. Everything between min and max (inclusive)
+      // constraints in the query. Everything between min and max (exclusive)
       // will be returned.
       if (IsOpEq(cs.op)) {
-        min = constraint_upid;
-        max = constraint_upid;
+        min_ = constraint_upid;
+        max_ = constraint_upid + 1;
       } else if (IsOpGe(cs.op) || IsOpGt(cs.op)) {
-        min = IsOpGt(cs.op) ? constraint_upid + 1 : constraint_upid;
+        min_ = IsOpGt(cs.op) ? constraint_upid + 1 : constraint_upid;
       } else if (IsOpLe(cs.op) || IsOpLt(cs.op)) {
-        max = IsOpLt(cs.op) ? constraint_upid - 1 : constraint_upid;
+        max_ = IsOpLt(cs.op) ? constraint_upid : constraint_upid + 1;
       }
     }
   }
 
   for (const auto& ob : qc.order_by()) {
     if (ob.iColumn == Column::kUpid) {
-      desc = ob.desc;
+      desc_ = ob.desc;
     }
   }
-  current = desc ? max : min;
+  index_ = 0;
 
   return SQLITE_OK;
 }
 
 int ProcessTable::Cursor::Column(sqlite3_context* context, int N) {
+  uint32_t current = desc_ ? max_ - index_ - 1 : min_ + index_;
+  const auto& process = storage_->GetProcess(current);
   switch (N) {
     case Column::kUpid: {
       sqlite3_result_int64(context, current);
       break;
     }
     case Column::kName: {
-      const auto& process = storage_->GetProcess(current);
       const auto& name = storage_->GetString(process.name_id);
       sqlite3_result_text(context, name.c_str(), -1, kSqliteStatic);
       break;
     }
     case Column::kPid: {
-      const auto& process = storage_->GetProcess(current);
       sqlite3_result_int64(context, process.pid);
       break;
     }
     case Column::kStartTs: {
-      const auto& process = storage_->GetProcess(current);
       if (process.start_ns != 0) {
         sqlite3_result_int64(context, process.start_ns);
       } else {
@@ -133,7 +132,6 @@
       break;
     }
     case Column::kEndTs: {
-      const auto& process = storage_->GetProcess(current);
       if (process.end_ns != 0) {
         sqlite3_result_int64(context, process.end_ns);
       } else {
@@ -142,7 +140,6 @@
       break;
     }
     case Column::kParentUpid: {
-      const auto& process = storage_->GetProcess(current);
       if (process.parent_upid.has_value()) {
         sqlite3_result_int64(context, process.parent_upid.value());
       } else {
@@ -158,16 +155,12 @@
 }
 
 int ProcessTable::Cursor::Next() {
-  if (desc) {
-    --current;
-  } else {
-    ++current;
-  }
+  ++index_;
   return SQLITE_OK;
 }
 
 int ProcessTable::Cursor::Eof() {
-  return desc ? current < min : current > max;
+  return index_ >= (max_ - min_);
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/process_table.h b/src/trace_processor/process_table.h
index 4c8b208..c4c1ed5 100644
--- a/src/trace_processor/process_table.h
+++ b/src/trace_processor/process_table.h
@@ -50,10 +50,10 @@
 
    private:
     const TraceStorage* const storage_;
-    UniquePid min;
-    UniquePid max;
-    UniquePid current;
-    bool desc;
+    UniquePid min_ = 0;
+    UniquePid max_ = 0;
+    uint32_t index_ = 0;
+    bool desc_ = false;
   };
 
   static void RegisterTable(sqlite3* db, const TraceStorage* storage);
diff --git a/src/trace_processor/raw_table.cc b/src/trace_processor/raw_table.cc
index bf4f111..b4c2969 100644
--- a/src/trace_processor/raw_table.cc
+++ b/src/trace_processor/raw_table.cc
@@ -70,12 +70,13 @@
 
 int RawTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
   info->estimated_cost = RowCount();
+  info->sqlite_omit_order_by = true;
 
   // Only the string columns are handled by SQLite
-  info->order_by_consumed = true;
   size_t name_index = schema().ColumnIndexFromName("name");
   for (size_t i = 0; i < qc.constraints().size(); i++) {
-    info->omit[i] = qc.constraints()[i].iColumn != static_cast<int>(name_index);
+    info->sqlite_omit_constraint[i] =
+        qc.constraints()[i].column != static_cast<int>(name_index);
   }
 
   return SQLITE_OK;
diff --git a/src/trace_processor/sched_slice_table.cc b/src/trace_processor/sched_slice_table.cc
index 05657c4..633e62f 100644
--- a/src/trace_processor/sched_slice_table.cc
+++ b/src/trace_processor/sched_slice_table.cc
@@ -51,8 +51,9 @@
 
   // We should be able to handle any constraint and any order by clause given
   // to us.
-  info->order_by_consumed = true;
-  std::fill(info->omit.begin(), info->omit.end(), true);
+  info->sqlite_omit_order_by = true;
+  auto& omit_cs = info->sqlite_omit_constraint;
+  std::fill(omit_cs.begin(), omit_cs.end(), true);
 
   return SQLITE_OK;
 }
@@ -62,7 +63,7 @@
 
   size_t ts_idx = schema().ColumnIndexFromName("ts");
   auto has_ts_column = [ts_idx](const QueryConstraints::Constraint& c) {
-    return c.iColumn == static_cast<int>(ts_idx);
+    return c.column == static_cast<int>(ts_idx);
   };
   bool has_time_constraint = std::any_of(cs.begin(), cs.end(), has_ts_column);
   if (has_time_constraint) {
@@ -73,8 +74,7 @@
 
   size_t utid_idx = schema().ColumnIndexFromName("utid");
   auto has_utid_eq_cs = [utid_idx](const QueryConstraints::Constraint& c) {
-    return c.iColumn == static_cast<int>(utid_idx) &&
-           sqlite_utils::IsOpEq(c.op);
+    return c.column == static_cast<int>(utid_idx) && sqlite_utils::IsOpEq(c.op);
   };
   bool has_utid_eq = std::any_of(cs.begin(), cs.end(), has_utid_eq_cs);
   if (has_utid_eq) {
diff --git a/src/trace_processor/slice_table.cc b/src/trace_processor/slice_table.cc
index d2dca83..2570674 100644
--- a/src/trace_processor/slice_table.cc
+++ b/src/trace_processor/slice_table.cc
@@ -53,15 +53,15 @@
 
 int SliceTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
   info->estimated_cost = EstimateCost(qc);
+  info->sqlite_omit_order_by = true;
 
   // Only the string columns are handled by SQLite
-  info->order_by_consumed = true;
   size_t name_index = schema().ColumnIndexFromName("name");
   size_t cat_index = schema().ColumnIndexFromName("category");
   size_t ref_type_index = schema().ColumnIndexFromName("ref_type");
   for (size_t i = 0; i < qc.constraints().size(); i++) {
-    auto col = static_cast<size_t>(qc.constraints()[i].iColumn);
-    info->omit[i] =
+    auto col = static_cast<size_t>(qc.constraints()[i].column);
+    info->sqlite_omit_constraint[i] =
         col != name_index && col != cat_index && col != ref_type_index;
   }
   return SQLITE_OK;
diff --git a/src/trace_processor/span_join_operator_table.cc b/src/trace_processor/span_join_operator_table.cc
index 57b1598..931289c 100644
--- a/src/trace_processor/span_join_operator_table.cc
+++ b/src/trace_processor/span_join_operator_table.cc
@@ -182,7 +182,7 @@
   std::vector<std::string> constraints;
   for (size_t i = 0; i < qc.constraints().size(); i++) {
     const auto& cs = qc.constraints()[i];
-    auto col_name = GetNameForGlobalColumnIndex(defn, cs.iColumn);
+    auto col_name = GetNameForGlobalColumnIndex(defn, cs.column);
     if (col_name == "")
       continue;
 
diff --git a/src/trace_processor/sql_stats_table.cc b/src/trace_processor/sql_stats_table.cc
index 3356f8e..0d434a0 100644
--- a/src/trace_processor/sql_stats_table.cc
+++ b/src/trace_processor/sql_stats_table.cc
@@ -57,7 +57,7 @@
 }
 
 int SqlStatsTable::BestIndex(const QueryConstraints&, BestIndexInfo* info) {
-  info->order_by_consumed = false;  // Delegate sorting to SQLite.
+  info->sqlite_omit_order_by = true;
   return SQLITE_OK;
 }
 
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index b2298ca..8b3dfc8 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -133,7 +133,7 @@
   // This means we have at least one constraint. Check if any of the constraints
   // is an equality constraint on an id column.
   auto id_filter = [this](const QueryConstraints::Constraint& c) {
-    uint32_t col_idx = static_cast<uint32_t>(c.iColumn);
+    uint32_t col_idx = static_cast<uint32_t>(c.column);
     const auto& col = table_->GetColumn(col_idx);
     return sqlite_utils::IsOpEq(c.op) && col.IsId();
   };
@@ -167,7 +167,7 @@
   constraints_.resize(qc.constraints().size());
   for (size_t i = 0; i < qc.constraints().size(); ++i) {
     const auto& cs = qc.constraints()[i];
-    uint32_t col = static_cast<uint32_t>(cs.iColumn);
+    uint32_t col = static_cast<uint32_t>(cs.column);
 
     FilterOp op = SqliteOpToFilterOp(cs.op);
     SqlValue value = SqliteValueToSqlValue(argv[i]);
diff --git a/src/trace_processor/sqlite/query_constraints.cc b/src/trace_processor/sqlite/query_constraints.cc
index cc57838..9ff242f 100644
--- a/src/trace_processor/sqlite/query_constraints.cc
+++ b/src/trace_processor/sqlite/query_constraints.cc
@@ -42,7 +42,7 @@
   }
 
   for (size_t i = 0; i < constraints().size(); ++i) {
-    if ((constraints()[i].iColumn != other.constraints()[i].iColumn) ||
+    if ((constraints()[i].column != other.constraints()[i].column) ||
         (constraints()[i].op != other.constraints()[i].op)) {
       return false;
     }
@@ -58,10 +58,13 @@
   return true;
 }
 
-void QueryConstraints::AddConstraint(int column, unsigned char op) {
+void QueryConstraints::AddConstraint(int column,
+                                     unsigned char op,
+                                     int aconstraint_idx) {
   Constraint c{};
-  c.iColumn = column;
+  c.column = column;
   c.op = op;
+  c.a_constraint_idx = aconstraint_idx;
   constraints_.emplace_back(c);
 }
 
@@ -79,7 +82,7 @@
   str_result.append(std::to_string(constraints_.size()));
   str_result.append(",");
   for (const auto& cs : constraints_) {
-    str_result.append(std::to_string(cs.iColumn));
+    str_result.append(std::to_string(cs.column));
     str_result.append(",");
     str_result.append(std::to_string(cs.op));
     str_result.append(",");
@@ -117,7 +120,7 @@
     PERFETTO_CHECK(splitter.Next());
     unsigned char op =
         static_cast<unsigned char>(strtol(splitter.cur_token(), nullptr, 10));
-    qc.AddConstraint(col, op);
+    qc.AddConstraint(col, op, 0);
   }
 
   PERFETTO_CHECK(splitter.Next() && splitter.cur_token_size() > 1);
diff --git a/src/trace_processor/sqlite/query_constraints.h b/src/trace_processor/sqlite/query_constraints.h
index 2c47562..54c1b45 100644
--- a/src/trace_processor/sqlite/query_constraints.h
+++ b/src/trace_processor/sqlite/query_constraints.h
@@ -38,7 +38,19 @@
 // in the xBestIndex call and passed to each corresponding xFilter call.
 class QueryConstraints {
  public:
-  using Constraint = sqlite3_index_info::sqlite3_index_constraint;
+  struct Constraint {
+    // Column this constraint refers to.
+    int column;
+
+    // SQLite op for the constraint.
+    int op;
+
+    // The original index of this constraint in the aConstraint array.
+    // Used internally by SqliteTable for xBestIndex - this should not be
+    // read or modified by subclasses of SqliteTable.
+    int a_constraint_idx;
+  };
+
   using OrderBy = sqlite3_index_info::sqlite3_index_orderby;
 
   static int FreeSqliteString(char* resource);
@@ -54,7 +66,7 @@
   // are equal.
   bool operator==(const QueryConstraints& other) const;
 
-  void AddConstraint(int column, unsigned char op);
+  void AddConstraint(int column, unsigned char op, int aconstraint_idx);
 
   void AddOrderBy(int column, unsigned char desc);
 
@@ -73,6 +85,10 @@
 
   const std::vector<Constraint>& constraints() const { return constraints_; }
 
+  std::vector<OrderBy>* mutable_order_by() { return &order_by_; }
+
+  std::vector<Constraint>* mutable_constraints() { return &constraints_; }
+
  private:
   QueryConstraints(const QueryConstraints&) = delete;
   QueryConstraints& operator=(const QueryConstraints&) = delete;
diff --git a/src/trace_processor/sqlite/query_constraints_unittest.cc b/src/trace_processor/sqlite/query_constraints_unittest.cc
index 276cec1..402bc70 100644
--- a/src/trace_processor/sqlite/query_constraints_unittest.cc
+++ b/src/trace_processor/sqlite/query_constraints_unittest.cc
@@ -36,7 +36,7 @@
 
 TEST_F(QueryConstraintsTest, ConvertToAndFromSqlString) {
   QueryConstraints qc;
-  qc.AddConstraint(12, 0);
+  qc.AddConstraint(12, 0, 0);
 
   QueryConstraints::SqliteString only_constraint = qc.ToNewSqlite3String();
   ASSERT_TRUE(strcmp(only_constraint.get(), "C1,12,0,O0") == 0);
diff --git a/src/trace_processor/sqlite/sqlite_table.cc b/src/trace_processor/sqlite/sqlite_table.cc
index 211ff7e..512d672 100644
--- a/src/trace_processor/sqlite/sqlite_table.cc
+++ b/src/trace_processor/sqlite/sqlite_table.cc
@@ -19,6 +19,7 @@
 #include <ctype.h>
 #include <string.h>
 #include <algorithm>
+#include <map>
 
 #include "perfetto/base/logging.h"
 
@@ -58,60 +59,69 @@
 }
 
 int SqliteTable::BestIndexInternal(sqlite3_index_info* idx) {
-  QueryConstraints query_constraints;
-
-  for (int i = 0; i < idx->nOrderBy; i++) {
-    int column = idx->aOrderBy[i].iColumn;
-    bool desc = idx->aOrderBy[i].desc;
-    query_constraints.AddOrderBy(column, desc);
-  }
+  QueryConstraints qc;
 
   for (int i = 0; i < idx->nConstraint; i++) {
     const auto& cs = idx->aConstraint[i];
     if (!cs.usable)
       continue;
-    query_constraints.AddConstraint(cs.iColumn, cs.op);
-
-    // argvIndex is 1-based so use the current size of the vector.
-    int argv_index = static_cast<int>(query_constraints.constraints().size());
-    idx->aConstraintUsage[i].argvIndex = argv_index;
+    qc.AddConstraint(cs.iColumn, cs.op, i);
   }
 
-  BestIndexInfo info;
-  info.omit.resize(query_constraints.constraints().size());
-
-  int ret = BestIndex(query_constraints, &info);
-
-  if (SqliteTable::debug) {
-    PERFETTO_LOG(
-        "[%s::BestIndex] constraints=%s orderByConsumed=%d estimatedCost=%d",
-        name_.c_str(), query_constraints.ToNewSqlite3String().get(),
-        info.order_by_consumed, info.estimated_cost);
+  for (int i = 0; i < idx->nOrderBy; i++) {
+    int column = idx->aOrderBy[i].iColumn;
+    bool desc = idx->aOrderBy[i].desc;
+    qc.AddOrderBy(column, desc);
   }
 
+  int ret = ModifyConstraints(&qc);
   if (ret != SQLITE_OK)
     return ret;
 
-  idx->orderByConsumed = info.order_by_consumed;
+  BestIndexInfo info;
+  info.sqlite_omit_constraint.resize(qc.constraints().size());
+
+  ret = BestIndex(qc, &info);
+  if (ret != SQLITE_OK)
+    return ret;
+
+  idx->orderByConsumed = qc.order_by().empty() || info.sqlite_omit_order_by;
   idx->estimatedCost = info.estimated_cost;
 
-  size_t j = 0;
-  for (int i = 0; i < idx->nConstraint; i++) {
-    const auto& cs = idx->aConstraint[i];
-    if (cs.usable)
-      idx->aConstraintUsage[i].omit = info.omit[j++];
+  // First pass: mark all constraints as omitted to ensure that any pruned
+  // constraints are not checked for by SQLite.
+  for (int i = 0; i < idx->nConstraint; ++i) {
+    auto& u = idx->aConstraintUsage[i];
+    u.omit = true;
   }
 
-  if (!info.order_by_consumed)
-    query_constraints.ClearOrderBy();
+  // Second pass: actually set the correct omit and index values for all
+  // retained constraints.
+  for (uint32_t i = 0; i < qc.constraints().size(); ++i) {
+    auto& u = idx->aConstraintUsage[qc.constraints()[i].a_constraint_idx];
+    u.omit = info.sqlite_omit_constraint[i];
+    u.argvIndex = static_cast<int>(i) + 1;
+  }
 
-  idx->idxStr = query_constraints.ToNewSqlite3String().release();
+  auto out_qc_str = qc.ToNewSqlite3String();
+  if (SqliteTable::debug) {
+    PERFETTO_LOG(
+        "[%s::BestIndex] constraints=%s orderByConsumed=%d estimatedCost=%d",
+        name_.c_str(), out_qc_str.get(), idx->orderByConsumed,
+        info.estimated_cost);
+  }
+
+  idx->idxStr = out_qc_str.release();
   idx->needToFreeIdxStr = true;
   idx->idxNum = ++best_index_num_;
 
   return SQLITE_OK;
 }
 
+int SqliteTable::ModifyConstraints(QueryConstraints*) {
+  return SQLITE_OK;
+}
+
 int SqliteTable::FindFunction(const char*, FindFunctionFn, void**) {
   return 0;
 }
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index 51557bf..c53baaf 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -136,9 +136,23 @@
   // Populated by a BestIndex call to allow subclasses to tweak SQLite's
   // handling of sets of constraints.
   struct BestIndexInfo {
-    bool order_by_consumed = false;
+    // Contains bools which indicate whether SQLite should omit double checking
+    // the constraint at that index.
+    //
+    // If there are no constraints, SQLite will be told it can omit checking for
+    // the whole query.
+    std::vector<bool> sqlite_omit_constraint;
+
+    // Indicates that SQLite should not double check the result of the order by
+    // clause.
+    //
+    // If there are no order by clauses, this value will be ignored and SQLite
+    // will be told that it can omit double checking (i.e. this value will
+    // implicitly be taken to be true).
+    bool sqlite_omit_order_by = false;
+
+    // Stores the estimated cost of this query.
     uint32_t estimated_cost = 0;
-    std::vector<bool> omit;
   };
 
   template <typename Context>
@@ -279,6 +293,7 @@
 
   // Optional metods to implement.
   using FindFunctionFn = void (**)(sqlite3_context*, int, sqlite3_value**);
+  virtual int ModifyConstraints(QueryConstraints* qc);
   virtual int FindFunction(const char* name, FindFunctionFn fn, void** args);
 
   // At registration time, the function should also pass true for |read_write|.
diff --git a/src/trace_processor/stack_profile_frame_table.cc b/src/trace_processor/stack_profile_frame_table.cc
index eb750e3..6939873 100644
--- a/src/trace_processor/stack_profile_frame_table.cc
+++ b/src/trace_processor/stack_profile_frame_table.cc
@@ -46,7 +46,7 @@
 
 int StackProfileFrameTable::BestIndex(const QueryConstraints& qc,
                                       BestIndexInfo* info) {
-  info->order_by_consumed = true;
+  info->sqlite_omit_order_by = true;
   info->estimated_cost = HasEqConstraint(qc, "id") ? 1 : RowCount();
   return SQLITE_OK;
 }
diff --git a/src/trace_processor/stack_profile_mapping_table.cc b/src/trace_processor/stack_profile_mapping_table.cc
index a7644db..52a9038 100644
--- a/src/trace_processor/stack_profile_mapping_table.cc
+++ b/src/trace_processor/stack_profile_mapping_table.cc
@@ -50,7 +50,7 @@
 
 int StackProfileMappingTable::BestIndex(const QueryConstraints& qc,
                                         BestIndexInfo* info) {
-  info->order_by_consumed = true;
+  info->sqlite_omit_order_by = true;
   info->estimated_cost = HasEqConstraint(qc, "id") ? 1 : RowCount();
   return SQLITE_OK;
 }
diff --git a/src/trace_processor/stack_profile_tracker.cc b/src/trace_processor/stack_profile_tracker.cc
index aa6b764..25815ea 100644
--- a/src/trace_processor/stack_profile_tracker.cc
+++ b/src/trace_processor/stack_profile_tracker.cc
@@ -82,12 +82,35 @@
       static_cast<int64_t>(mapping.load_bias),
       context_->storage->InternString(base::StringView(path))};
 
-  int64_t cur_row;
+  TraceStorage::StackProfileMappings* mappings =
+      context_->storage->mutable_stack_profile_mappings();
+  int64_t cur_row = -1;
   auto it = mapping_idx_.find(row);
   if (it != mapping_idx_.end()) {
     cur_row = it->second;
   } else {
-    cur_row = context_->storage->mutable_stack_profile_mappings()->Insert(row);
+    std::vector<int64_t> db_mappings =
+        mappings->FindMappingRow(row.name_id, row.build_id);
+    for (const int64_t preexisting_mapping : db_mappings) {
+      PERFETTO_DCHECK(preexisting_mapping >= 0);
+      size_t preexisting_row_id = static_cast<size_t>(preexisting_mapping);
+      TraceStorage::StackProfileMappings::Row preexisting_row{
+          mappings->build_ids()[preexisting_row_id],
+          mappings->exact_offsets()[preexisting_row_id],
+          mappings->start_offsets()[preexisting_row_id],
+          mappings->starts()[preexisting_row_id],
+          mappings->ends()[preexisting_row_id],
+          mappings->load_biases()[preexisting_row_id],
+          mappings->names()[preexisting_row_id]};
+
+      if (row == preexisting_row) {
+        cur_row = preexisting_mapping;
+      }
+    }
+    if (cur_row == -1) {
+      cur_row =
+          context_->storage->mutable_stack_profile_mappings()->Insert(row);
+    }
     mapping_idx_.emplace(row, cur_row);
   }
   mappings_.emplace(id, cur_row);
@@ -118,12 +141,31 @@
   TraceStorage::StackProfileFrames::Row row{str_id, mapping_row,
                                             static_cast<int64_t>(frame.rel_pc)};
 
-  int64_t cur_row;
+  TraceStorage::StackProfileFrames* frames =
+      context_->storage->mutable_stack_profile_frames();
+
+  int64_t cur_row = -1;
   auto it = frame_idx_.find(row);
   if (it != frame_idx_.end()) {
     cur_row = it->second;
   } else {
-    cur_row = context_->storage->mutable_stack_profile_frames()->Insert(row);
+    std::vector<int64_t> db_frames =
+        frames->FindFrameRow(static_cast<size_t>(mapping_row), frame.rel_pc);
+    for (const int64_t preexisting_frame : db_frames) {
+      PERFETTO_DCHECK(preexisting_frame >= 0);
+      size_t preexisting_row_id = static_cast<size_t>(preexisting_frame);
+      TraceStorage::StackProfileFrames::Row preexisting_row{
+          frames->names()[preexisting_row_id],
+          frames->mappings()[preexisting_row_id],
+          frames->rel_pcs()[preexisting_row_id]};
+
+      if (row == preexisting_row) {
+        cur_row = preexisting_frame;
+      }
+    }
+    if (cur_row == -1) {
+      cur_row = context_->storage->mutable_stack_profile_frames()->Insert(row);
+    }
     frame_idx_.emplace(row, cur_row);
   }
   frames_.emplace(id, cur_row);
diff --git a/src/trace_processor/stack_profile_tracker.h b/src/trace_processor/stack_profile_tracker.h
index 309c376..9496929 100644
--- a/src/trace_processor/stack_profile_tracker.h
+++ b/src/trace_processor/stack_profile_tracker.h
@@ -166,6 +166,8 @@
   std::unordered_map<SourceCallstack, int64_t> callstacks_from_frames_;
   std::unordered_map<SourceCallstackId, int64_t> callstacks_;
 
+  // TODO(oysteine): Share these indices between the StackProfileTrackers,
+  // since they're not sequence-specific.
   std::unordered_map<TraceStorage::StackProfileMappings::Row, int64_t>
       mapping_idx_;
   std::unordered_map<TraceStorage::StackProfileFrames::Row, int64_t> frame_idx_;
diff --git a/src/trace_processor/stats.h b/src/trace_processor/stats.h
index 8c4452c..583dfa2 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/stats.h
@@ -121,7 +121,10 @@
   F(packages_list_has_parse_errors,           kSingle,  kError,    kTrace),    \
   F(packages_list_has_read_errors,            kSingle,  kError,    kTrace),    \
   F(compact_sched_has_parse_errors,           kSingle,  kError,    kTrace),    \
-  F(misplaced_end_event,                      kSingle,  kDataLoss, kAnalysis)
+  F(misplaced_end_event,                      kSingle,  kDataLoss, kAnalysis), \
+  F(sched_waking_out_of_order,                kSingle,  kError,    kAnalysis), \
+  F(compact_sched_switch_skipped,             kSingle,  kInfo,     kAnalysis), \
+  F(compact_sched_waking_skipped,             kSingle,  kInfo,     kAnalysis)
 // clang-format on
 
 enum Type {
diff --git a/src/trace_processor/storage_table.cc b/src/trace_processor/storage_table.cc
index 8cac8b2..aec5810 100644
--- a/src/trace_processor/storage_table.cc
+++ b/src/trace_processor/storage_table.cc
@@ -69,7 +69,7 @@
   std::vector<size_t> bitvector_cs;
   for (size_t i = 0; i < cs.size(); i++) {
     const auto& c = cs[i];
-    size_t column = static_cast<size_t>(c.iColumn);
+    size_t column = static_cast<size_t>(c.column);
     auto bounds = schema_.GetColumn(column).BoundFilter(c.op, argv[i]);
 
     min_idx = std::max(min_idx, bounds.min_idx);
@@ -90,7 +90,7 @@
     const auto& c = cs[c_idx];
     auto* value = argv[c_idx];
 
-    const auto& schema_col = schema_.GetColumn(static_cast<size_t>(c.iColumn));
+    const auto& schema_col = schema_.GetColumn(static_cast<size_t>(c.column));
     schema_col.Filter(c.op, value, &index);
 
     if (!index.error().empty())
@@ -119,7 +119,7 @@
   std::set<int> equality_cols;
   for (const auto& c : cs) {
     if (sqlite_utils::IsOpEq(c.op))
-      equality_cols.emplace(c.iColumn);
+      equality_cols.emplace(c.column);
   }
   for (const auto& o : obs) {
     if (equality_cols.count(o.iColumn) > 0)
@@ -160,7 +160,7 @@
                                    const std::string& col_name) {
   size_t c_idx = schema().ColumnIndexFromName(col_name);
   auto fn = [c_idx](const QueryConstraints::Constraint& c) {
-    return c.iColumn == static_cast<int>(c_idx) && sqlite_utils::IsOpEq(c.op);
+    return c.column == static_cast<int>(c_idx) && sqlite_utils::IsOpEq(c.op);
   };
   const auto& cs = qc.constraints();
   return std::find_if(cs.begin(), cs.end(), fn) != cs.end();
diff --git a/src/trace_processor/string_pool.cc b/src/trace_processor/string_pool.cc
index 6c25229..6574e36 100644
--- a/src/trace_processor/string_pool.cc
+++ b/src/trace_processor/string_pool.cc
@@ -66,9 +66,13 @@
 
 const uint8_t* StringPool::Block::TryInsert(base::StringView str) {
   auto str_size = str.size();
-  if (static_cast<uint64_t>(pos_) + str_size + kMaxMetadataSize > size_)
+  size_t max_pos = static_cast<size_t>(pos_) + str_size + kMaxMetadataSize;
+  if (max_pos > size_)
     return nullptr;
 
+  // Ensure that we commit up until the end of the string to memory.
+  mem_.EnsureCommitted(max_pos);
+
   // Get where we should start writing this string.
   uint8_t* begin = Get(pos_);
 
@@ -86,6 +90,7 @@
 
   // Update the end of the block and return the pointer to the string.
   pos_ = OffsetOf(end);
+
   return begin;
 }
 
diff --git a/src/trace_processor/string_pool.h b/src/trace_processor/string_pool.h
index e2502e7..3724d5e 100644
--- a/src/trace_processor/string_pool.h
+++ b/src/trace_processor/string_pool.h
@@ -123,7 +123,9 @@
 
   struct Block {
     explicit Block(size_t size)
-        : mem_(base::PagedMemory::Allocate(size)), size_(size) {}
+        : mem_(base::PagedMemory::Allocate(size,
+                                           base::PagedMemory::kDontCommit)),
+          size_(size) {}
     ~Block() = default;
 
     // Allow std::move().
@@ -151,7 +153,7 @@
    private:
     base::PagedMemory mem_;
     uint32_t pos_ = 0;
-    size_t size_;
+    size_t size_ = 0;
   };
 
   friend class Iterator;
diff --git a/src/trace_processor/thread_table.cc b/src/trace_processor/thread_table.cc
index a951812..c98127d 100644
--- a/src/trace_processor/thread_table.cc
+++ b/src/trace_processor/thread_table.cc
@@ -62,7 +62,7 @@
   // because we can do that filter efficiently.
   const auto& constraints = qc.constraints();
   for (const auto& cs : qc.constraints()) {
-    if (cs.iColumn == Column::kUtid) {
+    if (cs.column == Column::kUtid) {
       info->estimated_cost = IsOpEq(constraints.front().op) ? 1 : 10;
     }
   }
@@ -76,40 +76,41 @@
                                 sqlite3_value** argv) {
   *this = Cursor(table_);
 
-  min = 0;
-  max = static_cast<uint32_t>(storage_->thread_count()) - 1;
-  desc = false;
+  min_ = 0;
+  max_ = static_cast<uint32_t>(storage_->thread_count());
+  desc_ = false;
 
   for (size_t j = 0; j < qc.constraints().size(); j++) {
     const auto& cs = qc.constraints()[j];
-    if (cs.iColumn == Column::kUtid) {
+    if (cs.column == Column::kUtid) {
       UniqueTid constraint_utid =
           static_cast<UniqueTid>(sqlite3_value_int(argv[j]));
       // Filter the range of utids that we are interested in, based on the
-      // constraints in the query. Everything between min and max (inclusive)
+      // constraints in the query. Everything between min and max (exclusive)
       // will be returned.
       if (IsOpEq(cs.op)) {
-        min = constraint_utid;
-        max = constraint_utid;
+        min_ = constraint_utid;
+        max_ = constraint_utid + 1;
       } else if (IsOpGe(cs.op) || IsOpGt(cs.op)) {
-        min = IsOpGt(cs.op) ? constraint_utid + 1 : constraint_utid;
+        min_ = IsOpGt(cs.op) ? constraint_utid + 1 : constraint_utid;
       } else if (IsOpLe(cs.op) || IsOpLt(cs.op)) {
-        max = IsOpLt(cs.op) ? constraint_utid - 1 : constraint_utid;
+        max_ = IsOpLt(cs.op) ? constraint_utid : constraint_utid + 1;
       }
     }
   }
 
   for (const auto& ob : qc.order_by()) {
     if (ob.iColumn == Column::kUtid) {
-      desc = ob.desc;
+      desc_ = ob.desc;
     }
   }
-  current = desc ? max : min;
+  index_ = 0;
 
   return SQLITE_OK;
 }
 
 int ThreadTable::Cursor::Column(sqlite3_context* context, int N) {
+  uint32_t current = desc_ ? max_ - index_ - 1 : min_ + index_;
   const auto& thread = storage_->GetThread(current);
   switch (N) {
     case Column::kUtid: {
@@ -158,16 +159,12 @@
 }
 
 int ThreadTable::Cursor::Next() {
-  if (desc) {
-    --current;
-  } else {
-    ++current;
-  }
+  ++index_;
   return SQLITE_OK;
 }
 
 int ThreadTable::Cursor::Eof() {
-  return desc ? current < min : current > max;
+  return index_ >= (max_ - min_);
 }
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/thread_table.h b/src/trace_processor/thread_table.h
index a72ed23..43484d1 100644
--- a/src/trace_processor/thread_table.h
+++ b/src/trace_processor/thread_table.h
@@ -55,10 +55,10 @@
     Cursor(Cursor&&) noexcept = default;
     Cursor& operator=(Cursor&&) = default;
 
-    UniqueTid min;
-    UniqueTid max;
-    UniqueTid current;
-    bool desc;
+    UniqueTid min_ = 0;
+    UniqueTid max_ = 0;
+    uint32_t index_ = 0;
+    bool desc_ = false;
 
     const TraceStorage* storage_ = nullptr;
     ThreadTable* table_ = nullptr;
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
index 84e21fb..83b4932 100644
--- a/src/trace_processor/timestamped_trace_piece.h
+++ b/src/trace_processor/timestamped_trace_piece.h
@@ -44,10 +44,17 @@
   StringId next_comm;
 };
 
+struct InlineSchedWaking {
+  int32_t pid;
+  int32_t target_cpu;
+  int32_t prio;
+  StringId comm;
+};
+
 // Discriminated union of events that are cannot be easily read from the
 // mapped trace.
 struct InlineEvent {
-  enum class Type { kInvalid = 0, kSchedSwitch };
+  enum class Type { kInvalid = 0, kSchedSwitch, kSchedWaking };
 
   static InlineEvent SchedSwitch(InlineSchedSwitch content) {
     InlineEvent evt;
@@ -56,9 +63,17 @@
     return evt;
   }
 
+  static InlineEvent SchedWaking(InlineSchedWaking content) {
+    InlineEvent evt;
+    evt.type = Type::kSchedWaking;
+    evt.sched_waking = content;
+    return evt;
+  }
+
   Type type = Type::kInvalid;
   union {
     InlineSchedSwitch sched_switch;
+    InlineSchedWaking sched_waking;
   };
 };
 
@@ -135,12 +150,16 @@
                               sequence_state,
                               InlineEvent{}) {}
 
+  // TODO(rsavitski): each "empty" TraceBlobView created by this constructor
+  // still allocates ref-counting structures for the nonexistent memory.
+  // It's not a significant overhead, but consider making the class have a
+  // legitimate empty state.
   TimestampedTracePiece(int64_t ts, uint64_t idx, InlineEvent inline_evt)
       : TimestampedTracePiece(ts,
                               /*thread_ts=*/0,
                               /*thread_instructions=*/0,
                               idx,
-                              /*tbv=*/TraceBlobView(nullptr, 0, 0),
+                              TraceBlobView(nullptr, 0, 0),
                               /*value=*/nullptr,
                               /*fpv=*/nullptr,
                               /*sequence_state=*/nullptr,
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index f845dbb..b96e628 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -108,6 +108,26 @@
   ASSERT_EQ(it.Get(0).long_value, static_cast<int64_t>(0xa9cb070fdc15f7a4));
 }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#define MAYBE_Demangle DISABLED_Demangle
+#else
+#define MAYBE_Demangle Demangle
+#endif
+TEST_F(TraceProcessorIntegrationTest, MAYBE_Demangle) {
+  auto it = Query("select DEMANGLE('_Znwm')");
+  ASSERT_TRUE(it.Next());
+  ASSERT_STRCASEEQ(it.Get(0).string_value, "operator new(unsigned long)");
+
+  it = Query("select DEMANGLE('_ZN3art6Thread14CreateCallbackEPv')");
+  ASSERT_TRUE(it.Next());
+  ASSERT_STRCASEEQ(it.Get(0).string_value,
+                   "art::Thread::CreateCallback(void*)");
+
+  it = Query("select DEMANGLE('test')");
+  ASSERT_TRUE(it.Next());
+  ASSERT_TRUE(it.Get(0).is_null());
+}
+
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON_IMPORT)
 TEST_F(TraceProcessorIntegrationTest, Sfgate) {
   ASSERT_TRUE(LoadTrace("sfgate.json", strlen("{\"traceEvents\":[")).ok());
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 80a7c9d..a51913a 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -56,6 +56,10 @@
 #include "src/trace_processor/export_json.h"
 #endif
 
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <cxxabi.h>
+#endif
+
 // In Android and Chromium tree builds, we don't have the percentile module.
 // Just don't include it.
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE)
@@ -138,7 +142,7 @@
                "CREATE VIEW counter_definitions AS "
                "SELECT "
                "  *, "
-               "  id as counter_id "
+               "  id AS counter_id "
                "FROM counter_track",
                0, 0, &error);
   if (error) {
@@ -147,9 +151,23 @@
   }
 
   sqlite3_exec(db,
+               "CREATE VIEW counter_values AS "
+               "SELECT "
+               "  *, "
+               "  track_id as counter_id "
+               "FROM counter",
+               0, 0, &error);
+  if (error) {
+    PERFETTO_ELOG("Error initializing: %s", error);
+    sqlite3_free(error);
+  }
+
+  sqlite3_exec(db,
                "CREATE VIEW counters AS "
-               "SELECT * FROM counter_values "
-               "INNER JOIN counter_definitions USING(counter_id) "
+               "SELECT * "
+               "FROM counter_values v "
+               "INNER JOIN counter_track t "
+               "ON v.track_id = t.id "
                "ORDER BY ts;",
                0, 0, &error);
   if (error) {
@@ -161,7 +179,7 @@
                "CREATE VIEW slice AS "
                "SELECT "
                "  *, "
-               "  category as cat, "
+               "  category AS cat, "
                "  CASE ref_type "
                "    WHEN 'utid' THEN ref "
                "    ELSE NULL "
@@ -256,6 +274,32 @@
   sqlite3_result_int64(ctx, static_cast<int64_t>(hash.digest()));
 }
 
+void Demangle(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+  if (argc != 1) {
+    sqlite3_result_error(ctx, "Unsupported number of arg passed to DEMANGLE",
+                         -1);
+    return;
+  }
+  sqlite3_value* value = argv[0];
+  if (sqlite3_value_type(value) != SQLITE_TEXT) {
+    sqlite3_result_error(ctx, "Unsupported type of arg passed to DEMANGLE", -1);
+    return;
+  }
+  const char* ptr = reinterpret_cast<const char*>(sqlite3_value_text(value));
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  int ignored = 0;
+  // This memory was allocated by malloc and will be passed to SQLite to free.
+  char* demangled_name = abi::__cxa_demangle(ptr, nullptr, nullptr, &ignored);
+  if (!demangled_name) {
+    sqlite3_result_null(ctx);
+    return;
+  }
+  sqlite3_result_text(ctx, demangled_name, -1, free);
+#else
+  sqlite3_result_text(ctx, ptr, -1, sqlite_utils::kSqliteTransient);
+#endif
+}
+
 void CreateHashFunction(sqlite3* db) {
   auto ret = sqlite3_create_function_v2(
       db, "HASH", -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, &Hash,
@@ -265,6 +309,15 @@
   }
 }
 
+void CreateDemangledNameFunction(sqlite3* db) {
+  auto ret = sqlite3_create_function_v2(
+      db, "DEMANGLE", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, &Demangle,
+      nullptr, nullptr, nullptr);
+  if (ret != SQLITE_OK) {
+    PERFETTO_ELOG("Error initializing DEMANGLE: %s", sqlite3_errmsg(db));
+  }
+}
+
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_METRICS)
 void SetupMetrics(TraceProcessor* tp,
                   sqlite3* db,
@@ -314,6 +367,7 @@
   CreateJsonExportFunction(this->context_.storage.get(), db);
 #endif
   CreateHashFunction(db);
+  CreateDemangledNameFunction(db);
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_METRICS)
   SetupMetrics(this, *db_, &sql_metrics_);
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index a832e9f..7243b42 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -787,15 +787,17 @@
       rel_pcs_.emplace_back(row.rel_pc);
       symbol_set_ids_.emplace_back(0);
       size_t row_number = names_.size() - 1;
-      index_.emplace(std::make_pair(row.mapping_row, row.rel_pc), row_number);
+      index_[std::make_pair(row.mapping_row, row.rel_pc)].emplace_back(
+          row_number);
       return static_cast<uint32_t>(row_number);
     }
 
-    ssize_t FindFrameRow(size_t mapping_row, uint64_t rel_pc) const {
+    std::vector<int64_t> FindFrameRow(size_t mapping_row,
+                                      uint64_t rel_pc) const {
       auto it = index_.find(std::make_pair(mapping_row, rel_pc));
       if (it == index_.end())
-        return -1;
-      return static_cast<ssize_t>(it->second);
+        return {};
+      return it->second;
     }
 
     void SetSymbolSetId(size_t row_idx, uint32_t symbol_set_id) {
@@ -816,7 +818,8 @@
     std::deque<int64_t> rel_pcs_;
     std::deque<uint32_t> symbol_set_ids_;
 
-    std::map<std::pair<size_t /* mapping row */, uint64_t /* rel_pc */>, size_t>
+    std::map<std::pair<size_t /* mapping row */, uint64_t /* rel_pc */>,
+             std::vector<int64_t>>
         index_;
   };
 
diff --git a/src/trace_processor/window_operator_table.cc b/src/trace_processor/window_operator_table.cc
index cbc8d78..a7a49e5 100644
--- a/src/trace_processor/window_operator_table.cc
+++ b/src/trace_processor/window_operator_table.cc
@@ -61,13 +61,16 @@
   return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
 }
 
-int WindowOperatorTable::BestIndex(const QueryConstraints& qc,
-                                   BestIndexInfo* info) {
+int WindowOperatorTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
+  return SQLITE_OK;
+}
+
+int WindowOperatorTable::ModifyConstraints(QueryConstraints* qc) {
   // Remove ordering on timestamp if it is the only ordering as we are already
   // sorted on TS. This makes span joining significantly faster.
-  if (qc.order_by().size() == 1 && qc.order_by()[0].iColumn == Column::kTs &&
-      !qc.order_by()[0].desc) {
-    info->order_by_consumed = true;
+  const auto& ob = qc->order_by();
+  if (ob.size() == 1 && ob[0].iColumn == Column::kTs && !ob[0].desc) {
+    qc->mutable_order_by()->clear();
   }
   return SQLITE_OK;
 }
@@ -111,7 +114,7 @@
   // Set return first if there is a equals constraint on the row id asking to
   // return the first row.
   bool return_first = qc.constraints().size() == 1 &&
-                      qc.constraints()[0].iColumn == Column::kRowId &&
+                      qc.constraints()[0].column == Column::kRowId &&
                       IsOpEq(qc.constraints()[0].op) &&
                       sqlite3_value_int(argv[0]) == 0;
   if (return_first) {
diff --git a/src/trace_processor/window_operator_table.h b/src/trace_processor/window_operator_table.h
index 5959174..41fbbb7 100644
--- a/src/trace_processor/window_operator_table.h
+++ b/src/trace_processor/window_operator_table.h
@@ -78,6 +78,7 @@
   util::Status Init(int, const char* const*, Schema* schema) override;
   std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
   int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+  int ModifyConstraints(QueryConstraints* qc) override;
   int Update(int, sqlite3_value**, sqlite3_int64*) override;
 
  private:
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index ddac4e8..11a84ef 100644
--- a/src/traced/probes/ftrace/BUILD.gn
+++ b/src/traced/probes/ftrace/BUILD.gn
@@ -109,7 +109,6 @@
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced",
     "../../../../protos/perfetto/config/ftrace:cpp",
-    "../../../../protos/perfetto/config/ftrace:lite",
     "../../../android_internal:lazy_library_loader",
     "../../../base",
     "../../../protozero",
diff --git a/src/traced/probes/ftrace/compact_sched.cc b/src/traced/probes/ftrace/compact_sched.cc
index 6bc7619..35309d8 100644
--- a/src/traced/probes/ftrace/compact_sched.cc
+++ b/src/traced/probes/ftrace/compact_sched.cc
@@ -23,6 +23,7 @@
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
 #include "src/traced/probes/ftrace/event_info_constants.h"
+#include "src/traced/probes/ftrace/ftrace_config_utils.h"
 
 namespace perfetto {
 
@@ -37,12 +38,6 @@
 
   CompactSchedSwitchFormat switch_format;
   switch_format.event_id = event.ftrace_event_id;
-
-  // We make a compile-time buffer capacity decision based on the expected event
-  // size per a set of pages. Check that the assumption holds.
-  if (event.size < CompactSchedBundleState::kMinSupportedSchedSwitchSize) {
-    return base::nullopt;
-  }
   switch_format.size = event.size;
 
   bool prev_state_valid = false;
@@ -81,7 +76,7 @@
 
         next_comm_valid =
             (field.ftrace_type == kFtraceFixedCString &&
-             field.ftrace_size == CompactSchedBundleState::kExpectedCommLength);
+             field.ftrace_size == CommInterner::kExpectedCommLength);
         break;
       default:
         break;
@@ -90,13 +85,69 @@
 
   if (!prev_state_valid || !next_pid_valid || !next_prio_valid ||
       !next_comm_valid) {
-    PERFETTO_ELOG("unexpected sched_switch format");
     return base::nullopt;
   }
-
   return base::make_optional(switch_format);
 }
 
+// Pre-parse the format of sched_waking, checking if our simplifying
+// assumptions about possible widths/signedness hold, and record the subset
+// of the format that will be used during parsing.
+base::Optional<CompactSchedWakingFormat> ValidateSchedWakingFormat(
+    const Event& event) {
+  using protos::pbzero::SchedWakingFtraceEvent;
+
+  CompactSchedWakingFormat waking_format;
+  waking_format.event_id = event.ftrace_event_id;
+  waking_format.size = event.size;
+
+  bool pid_valid = false;
+  bool target_cpu_valid = false;
+  bool prio_valid = false;
+  bool comm_valid = false;
+  for (const auto& field : event.fields) {
+    switch (field.proto_field_id) {
+      case SchedWakingFtraceEvent::kPidFieldNumber:
+        waking_format.pid_offset = field.ftrace_offset;
+        waking_format.pid_type = field.ftrace_type;
+
+        // kernel type: pid_t
+        pid_valid = (field.ftrace_type == kFtracePid32);
+        break;
+
+      case SchedWakingFtraceEvent::kTargetCpuFieldNumber:
+        waking_format.target_cpu_offset = field.ftrace_offset;
+        waking_format.target_cpu_type = field.ftrace_type;
+
+        // kernel type: int
+        target_cpu_valid = (field.ftrace_type == kFtraceInt32);
+        break;
+
+      case SchedWakingFtraceEvent::kPrioFieldNumber:
+        waking_format.prio_offset = field.ftrace_offset;
+        waking_format.prio_type = field.ftrace_type;
+
+        // kernel type: int
+        prio_valid = (field.ftrace_type == kFtraceInt32);
+        break;
+
+      case SchedWakingFtraceEvent::kCommFieldNumber:
+        waking_format.comm_offset = field.ftrace_offset;
+
+        comm_valid = (field.ftrace_type == kFtraceFixedCString &&
+                      field.ftrace_size == CommInterner::kExpectedCommLength);
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (!pid_valid || !target_cpu_valid || !prio_valid || !comm_valid) {
+    return base::nullopt;
+  }
+  return base::make_optional(waking_format);
+}
+
 }  // namespace
 
 // TODO(rsavitski): could avoid looping over all events if the caller did the
@@ -107,31 +158,36 @@
   using protos::pbzero::FtraceEvent;
 
   base::Optional<CompactSchedSwitchFormat> switch_format;
+  base::Optional<CompactSchedWakingFormat> waking_format;
   for (const Event& event : events) {
     if (event.proto_field_id == FtraceEvent::kSchedSwitchFieldNumber) {
       switch_format = ValidateSchedSwitchFormat(event);
     }
+    if (event.proto_field_id == FtraceEvent::kSchedWakingFieldNumber) {
+      waking_format = ValidateSchedWakingFormat(event);
+    }
   }
 
-  if (switch_format.has_value()) {
-    return CompactSchedEventFormat{/*format_valid=*/true,
-                                   switch_format.value()};
+  if (switch_format.has_value() && waking_format.has_value()) {
+    return CompactSchedEventFormat{/*format_valid=*/true, switch_format.value(),
+                                   waking_format.value()};
   } else {
+    PERFETTO_ELOG("Unexpected sched_switch or sched_waking format.");
     return CompactSchedEventFormat{/*format_valid=*/false,
-                                   CompactSchedSwitchFormat{}};
+                                   CompactSchedSwitchFormat{},
+                                   CompactSchedWakingFormat{}};
   }
 }
 
 CompactSchedEventFormat InvalidCompactSchedEventFormatForTesting() {
   return CompactSchedEventFormat{/*format_valid=*/false,
-                                 CompactSchedSwitchFormat{}};
+                                 CompactSchedSwitchFormat{},
+                                 CompactSchedWakingFormat{}};
 }
 
 // TODO(rsavitski): find the correct place in the trace for, and method of,
 // reporting rejection of compact_sched due to compile-time assumptions not
 // holding at runtime.
-// TODO(rsavitski): consider checking if the ftrace config correctly enables
-// sched_switch, for at least an informative print for now?
 CompactSchedConfig CreateCompactSchedConfig(
     const FtraceConfig& request,
     const CompactSchedEventFormat& compact_format) {
@@ -153,37 +209,76 @@
 }
 
 // Sanity check size of stack-allocated bundle state.
-static_assert(sizeof(CompactSchedBundleState) <= 1 << 20,
-              "CompactSchedBundleState excessively large (used on the stack).");
+static_assert(sizeof(CompactSchedBuffer) <= 1 << 18,
+              "CompactSchedBuffer's on-stack size excessively large.");
 
-void CompactSchedBundleState::WriteAndReset(
+void CompactSchedSwitchBuffer::Write(
+    protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const {
+  compact_out->set_switch_timestamp(timestamp_);
+  compact_out->set_switch_next_pid(next_pid_);
+  compact_out->set_switch_prev_state(prev_state_);
+  compact_out->set_switch_next_prio(next_prio_);
+  compact_out->set_switch_next_comm_index(next_comm_index_);
+}
+
+void CompactSchedSwitchBuffer::Reset() {
+  last_timestamp_ = 0;
+  timestamp_.Reset();
+  next_pid_.Reset();
+  prev_state_.Reset();
+  next_prio_.Reset();
+  next_comm_index_.Reset();
+}
+
+void CompactSchedWakingBuffer::Write(
+    protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const {
+  compact_out->set_waking_timestamp(timestamp_);
+  compact_out->set_waking_pid(pid_);
+  compact_out->set_waking_target_cpu(target_cpu_);
+  compact_out->set_waking_prio(prio_);
+  compact_out->set_waking_comm_index(comm_index_);
+}
+
+void CompactSchedWakingBuffer::Reset() {
+  last_timestamp_ = 0;
+  timestamp_.Reset();
+  pid_.Reset();
+  target_cpu_.Reset();
+  prio_.Reset();
+  comm_index_.Reset();
+}
+
+void CommInterner::Write(
+    protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const {
+  for (size_t i = 0; i < interned_comms_size_; i++) {
+    compact_out->add_intern_table(interned_comms_[i].data(),
+                                  interned_comms_[i].size());
+  }
+}
+
+void CommInterner::Reset() {
+  intern_buf_write_pos_ = 0;
+  interned_comms_size_ = 0;
+}
+
+void CompactSchedBuffer::WriteAndReset(
     protos::pbzero::FtraceEventBundle* bundle) {
-  // If we buffered at least one event (using the interner as a proxy),
-  // write the state out.
-  if (interned_switch_comms_size_ > 0) {
-    auto compact_out = bundle->set_compact_sched();
+  if (switch_.size() > 0 || waking_.size() > 0) {
+    auto* compact_out = bundle->set_compact_sched();
 
-    compact_out->set_switch_timestamp(switch_timestamp_);
-    compact_out->set_switch_next_pid(switch_next_pid_);
-    compact_out->set_switch_prev_state(switch_prev_state_);
-    compact_out->set_switch_next_prio(switch_next_prio_);
+    PERFETTO_DCHECK(interner_.interned_comms_size() > 0);
+    interner_.Write(compact_out);
 
-    for (size_t i = 0; i < interned_switch_comms_size_; i++) {
-      compact_out->add_switch_next_comm_table(interned_switch_comms_[i].data(),
-                                              interned_switch_comms_[i].size());
-    }
-    compact_out->set_switch_next_comm_index(switch_next_comm_index_);
+    if (switch_.size() > 0)
+      switch_.Write(compact_out);
+
+    if (waking_.size() > 0)
+      waking_.Write(compact_out);
   }
 
-  // Reset internal state.
-  last_switch_timestamp_ = 0;
-  switch_timestamp_.Reset();
-  switch_next_pid_.Reset();
-  switch_prev_state_.Reset();
-  switch_next_prio_.Reset();
-  switch_next_comm_index_.Reset();
-  intern_buf_write_pos_ = 0;
-  interned_switch_comms_size_ = 0;
+  interner_.Reset();
+  switch_.Reset();
+  waking_.Reset();
 }
 
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/compact_sched.h b/src/traced/probes/ftrace/compact_sched.h
index 6dc4692..657d651 100644
--- a/src/traced/probes/ftrace/compact_sched.h
+++ b/src/traced/probes/ftrace/compact_sched.h
@@ -28,7 +28,7 @@
 
 class FtraceConfig;
 
-// The subset of the sched_switch event's format that is used when parsing &
+// The subset of the sched_switch event's format that is used when parsing and
 // encoding into the compact format.
 struct CompactSchedSwitchFormat {
   uint32_t event_id;
@@ -43,6 +43,21 @@
   uint16_t next_comm_offset;
 };
 
+// The subset of the sched_waking event's format that is used when parsing and
+// encoding into the compact format.
+struct CompactSchedWakingFormat {
+  uint32_t event_id;
+  uint16_t size;
+
+  uint16_t pid_offset;
+  FtraceFieldType pid_type;
+  uint16_t target_cpu_offset;
+  FtraceFieldType target_cpu_type;
+  uint16_t prio_offset;
+  FtraceFieldType prio_type;
+  uint16_t comm_offset;
+};
+
 // Pre-parsed format of a subset of scheduling events, for use during ftrace
 // parsing if compact encoding is enabled. Holds a flag, |format_valid| to
 // state whether the compile-time assumptions about the format held at runtime.
@@ -50,7 +65,9 @@
 struct CompactSchedEventFormat {
   // If false, the rest of the struct is considered invalid.
   const bool format_valid;
+
   const CompactSchedSwitchFormat sched_switch;
+  const CompactSchedWakingFormat sched_waking;
 };
 
 CompactSchedEventFormat ValidateFormatForCompactSched(
@@ -62,8 +79,8 @@
 struct CompactSchedConfig {
   CompactSchedConfig(bool _enabled) : enabled(_enabled) {}
 
-  // If true, and sched_switch event is enabled, encode it in a compact format
-  // instead of the normal form.
+  // If true, and sched_switch and/or sched_waking events are enabled, encode
+  // them in a compact format instead of the normal form.
   const bool enabled = false;
 };
 
@@ -74,97 +91,145 @@
 CompactSchedConfig EnabledCompactSchedConfigForTesting();
 CompactSchedConfig DisabledCompactSchedConfigForTesting();
 
-// Mutable state for buffering parts of scheduling events, that can later be
-// written out in a compact format with |WriteAndReset|. Used by the ftrace
-// reader, allocated on the stack.
-class CompactSchedBundleState {
+// Collects fields of sched_switch events, allowing them to be written out
+// in a compact encoding.
+class CompactSchedSwitchBuffer {
  public:
-  // Most of the state is stack-allocated, with a compile-time
-  // size. We work in batches of pages (see kParsingBufferSizePages in
-  // ftrace_controller.cc), and assume a minimum size of a sched event as
-  // written by the kernel (validated at runtime). We therefore can calculate
-  // the maximum necessary capacity for a given parsing buffer size (as
-  // statically asserted in ftrace_controller.cc).
-  // Note: be careful not to align the individual buffers at a multiple of the
-  // cache size.
-  // TODO(rsavitski): this will need a slight rework once we add sched_waking,
-  // as it'll be the min size of the two events.
-  static constexpr size_t kMaxElements = 2560;
-  static constexpr size_t kMinSupportedSchedSwitchSize = 56;
+  protozero::PackedVarInt& timestamp() { return timestamp_; }
+  protozero::PackedVarInt& prev_state() { return prev_state_; }
+  protozero::PackedVarInt& next_pid() { return next_pid_; }
+  protozero::PackedVarInt& next_prio() { return next_prio_; }
+  protozero::PackedVarInt& next_comm_index() { return next_comm_index_; }
+
+  size_t size() const {
+    // Caller should fill all per-field buffers at the same rate.
+    return timestamp_.size();
+  }
+
+  inline void AppendTimestamp(uint64_t timestamp) {
+    timestamp_.Append(timestamp - last_timestamp_);
+    last_timestamp_ = timestamp;
+  }
+
+  void Write(
+      protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
+  void Reset();
+
+ private:
+  // First timestamp in a bundle is absolute. The rest are all delta-encoded,
+  // each relative to the preceding sched_switch timestamp.
+  uint64_t last_timestamp_ = 0;
+
+  protozero::PackedVarInt timestamp_;
+  protozero::PackedVarInt prev_state_;
+  protozero::PackedVarInt next_pid_;
+  protozero::PackedVarInt next_prio_;
+  // Interning indices of the next_comm values. See |CommInterner|.
+  protozero::PackedVarInt next_comm_index_;
+};
+
+// As |CompactSchedSwitchBuffer|, but for sched_waking events.
+class CompactSchedWakingBuffer {
+ public:
+  protozero::PackedVarInt& pid() { return pid_; }
+  protozero::PackedVarInt& target_cpu() { return target_cpu_; }
+  protozero::PackedVarInt& prio() { return prio_; }
+  protozero::PackedVarInt& comm_index() { return comm_index_; }
+
+  size_t size() const {
+    // Caller should fill all per-field buffers at the same rate.
+    return timestamp_.size();
+  }
+
+  inline void AppendTimestamp(uint64_t timestamp) {
+    timestamp_.Append(timestamp - last_timestamp_);
+    last_timestamp_ = timestamp;
+  }
+
+  void Write(
+      protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
+  void Reset();
+
+ private:
+  uint64_t last_timestamp_ = 0;
+
+  protozero::PackedVarInt timestamp_;
+  protozero::PackedVarInt pid_;
+  protozero::PackedVarInt target_cpu_;
+  protozero::PackedVarInt prio_;
+  // Interning indices of the comm values. See |CommInterner|.
+  protozero::PackedVarInt comm_index_;
+};
+
+class CommInterner {
+ public:
   static constexpr size_t kExpectedCommLength = 16;
 
-  protozero::PackedVarInt* switch_timestamp() { return &switch_timestamp_; }
-
-  protozero::PackedVarInt* switch_prev_state() { return &switch_prev_state_; }
-
-  protozero::PackedVarInt* switch_next_pid() { return &switch_next_pid_; }
-
-  protozero::PackedVarInt* switch_next_prio() { return &switch_next_prio_; }
-
-  size_t interned_switch_comms_size() const {
-    return interned_switch_comms_size_;
-  }
-
-  inline void AppendSwitchTimestamp(uint64_t timestamp) {
-    switch_timestamp_.Append(timestamp - last_switch_timestamp_);
-    last_switch_timestamp_ = timestamp;
-  }
-
-  // TODO(rsavitski): see if we can use the fact that comms are <16 bytes
-  // long when comparing them.
-  void InternSwitchNextComm(const char* ptr) {
+  size_t InternComm(const char* ptr) {
     // Linearly scan existing string views, ftrace reader will
     // make sure this set doesn't grow too large.
     base::StringView transient_view(ptr);
-    for (size_t i = 0; i < interned_switch_comms_size_; i++) {
-      if (transient_view == interned_switch_comms_[i]) {
-        switch_next_comm_index_.Append(i);
-        return;
+    for (size_t i = 0; i < interned_comms_size_; i++) {
+      if (transient_view == interned_comms_[i]) {
+        return i;
       }
     }
 
-    // Unique next_comm, intern it. Null byte is not copied over.
+    // Unique comm, intern it. Null byte is not copied over.
     char* start = intern_buf_ + intern_buf_write_pos_;
     size_t size = transient_view.size();
     memcpy(start, ptr, size);
     intern_buf_write_pos_ += size;
 
-    switch_next_comm_index_.Append(interned_switch_comms_size_);
+    size_t idx = interned_comms_size_;
     base::StringView safe_view(start, size);
-    interned_switch_comms_[interned_switch_comms_size_++] = safe_view;
+    interned_comms_[interned_comms_size_++] = safe_view;
 
     PERFETTO_DCHECK(intern_buf_write_pos_ <= sizeof(intern_buf_));
+    PERFETTO_DCHECK(interned_comms_size_ < kMaxElements);
+    return idx;
   }
 
+  size_t interned_comms_size() const { return interned_comms_size_; }
+
+  void Write(
+      protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
+  void Reset();
+
+ private:
+  // TODO(rsavitski): Consider making the storage dynamically-expandable instead
+  // to not rely on sizing the buffer for the worst case.
+  static constexpr size_t kMaxElements = 4096;
+
+  char intern_buf_[kMaxElements * (kExpectedCommLength - 1)];
+  size_t intern_buf_write_pos_ = 0;
+
+  // Views into unique interned comm strings. Even if every event carries a
+  // unique comm, the ftrace reader is expected to flush the compact buffer way
+  // before this reaches capacity. This is since the cost of processing each
+  // event grows with every unique interned comm (as the interning needs to
+  // search all existing internings).
+  std::array<base::StringView, kMaxElements> interned_comms_;
+  uint32_t interned_comms_size_ = 0;
+};
+
+// Mutable state for buffering parts of scheduling events, that can later be
+// written out in a compact format with |WriteAndReset|. Used by the ftrace
+// reader.
+class CompactSchedBuffer {
+ public:
+  CompactSchedSwitchBuffer& sched_switch() { return switch_; }
+  CompactSchedWakingBuffer& sched_waking() { return waking_; }
+  CommInterner& interner() { return interner_; }
+
   // Writes out the currently buffered events, and starts the next batch
   // internally.
   void WriteAndReset(protos::pbzero::FtraceEventBundle* bundle);
 
  private:
-  // First timestamp in a bundle is absolute. The rest are all delta-encoded,
-  // each relative to the preceding sched_switch timestamp.
-  uint64_t last_switch_timestamp_ = 0;
-
-  protozero::PackedVarInt switch_timestamp_;
-  protozero::PackedVarInt switch_prev_state_;
-  protozero::PackedVarInt switch_next_pid_;
-  protozero::PackedVarInt switch_next_prio_;
-
-  // Storage for interned strings (without null bytes).
-  char intern_buf_[kMaxElements * (kExpectedCommLength - 1)];
-  size_t intern_buf_write_pos_ = 0;
-
-  // Views into unique interned next_comm strings. Even if every sched_switch
-  // carries a unique next_comm, the ftrace reader is expected to flush the
-  // compact buffer way before this reaches capacity. This is since the cost of
-  // processing each event grows with every unique interned next_comm (as the
-  // interning needs to search all existing internings).
-  std::array<base::StringView, kMaxElements> interned_switch_comms_;
-  uint32_t interned_switch_comms_size_ = 0;
-
-  // One entry per sched_switch event, contains the index of the interned
-  // next_comm string view (i.e. array index into |interned_switch_comms|).
-  protozero::PackedVarInt switch_next_comm_index_;
+  CommInterner interner_;
+  CompactSchedSwitchBuffer switch_;
+  CompactSchedWakingBuffer waking_;
 };
 
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index 96f114a..6010e90 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -16,8 +16,8 @@
 
 #include "src/traced/probes/ftrace/cpu_reader.h"
 
-#include <signal.h>
 #include <dirent.h>
+#include <signal.h>
 
 #include <utility>
 
@@ -279,7 +279,7 @@
     const ProtoTranslationTable* table) {
   // Begin an FtraceEventBundle, and allocate the buffer for compact scheduler
   // events (which will be unused if the compact option isn't enabled).
-  CompactSchedBundleState compact_sched;
+  CompactSchedBuffer compact_sched;
   auto packet = trace_writer->NewTracePacket();
   auto* bundle = packet->set_ftrace_events();
 
@@ -312,8 +312,9 @@
     //   a threshold. We need to flush the compact buffer to make the
     //   interning lookups cheap again.
     bool interner_past_threshold =
-        compact_sched_enabled && compact_sched.interned_switch_comms_size() >
-                                     kCompactSchedInternerThreshold;
+        compact_sched_enabled &&
+        compact_sched.interner().interned_comms_size() >
+            kCompactSchedInternerThreshold;
     if (page_header->lost_events || interner_past_threshold) {
       if (compact_sched_enabled)
         compact_sched.WriteAndReset(bundle);
@@ -330,9 +331,9 @@
         ParsePagePayload(parse_pos, &page_header.value(), table, ds_config,
                          &compact_sched, bundle, metadata);
 
-    // TODO(b/140866160): compare against header->size once padding size
-    // off-by-4 is fixed.
-    PERFETTO_DCHECK(evt_size > 0);
+    // TODO(rsavitski): propagate error to trace processor in release builds.
+    // (FtraceMetadata -> FtraceStats in trace).
+    PERFETTO_DCHECK(evt_size == page_header->size);
   }
 
   if (compact_sched_enabled)
@@ -398,14 +399,13 @@
 // binary ftrace events. See |ParsePageHeader| for the format of the earlier.
 //
 // This method is deliberately static so it can be tested independently.
-size_t CpuReader::ParsePagePayload(
-    const uint8_t* start_of_payload,
-    const PageHeader* page_header,
-    const ProtoTranslationTable* table,
-    const FtraceDataSourceConfig* ds_config,
-    CompactSchedBundleState* compact_sched_buffer,
-    FtraceEventBundle* bundle,
-    FtraceMetadata* metadata) {
+size_t CpuReader::ParsePagePayload(const uint8_t* start_of_payload,
+                                   const PageHeader* page_header,
+                                   const ProtoTranslationTable* table,
+                                   const FtraceDataSourceConfig* ds_config,
+                                   CompactSchedBuffer* compact_sched_buffer,
+                                   FtraceEventBundle* bundle,
+                                   FtraceMetadata* metadata) {
   const uint8_t* ptr = start_of_payload;
   const uint8_t* const end = ptr + page_header->size;
 
@@ -429,7 +429,10 @@
         uint32_t length;
         if (!ReadAndAdvance<uint32_t>(&ptr, end, &length))
           return 0;
-        ptr += length;
+        // length includes itself (4 bytes)
+        if (length < 4)
+          return 0;
+        ptr += length - 4;
         break;
       }
       case kTypeTimeExtend: {
@@ -484,7 +487,10 @@
           bool compact_sched_enabled = ds_config->compact_sched.enabled;
           const CompactSchedSwitchFormat& sched_switch_format =
               table->compact_sched_format().sched_switch;
+          const CompactSchedWakingFormat& sched_waking_format =
+              table->compact_sched_format().sched_waking;
 
+          // compact sched_switch
           if (compact_sched_enabled &&
               ftrace_event_id == sched_switch_format.event_id) {
             if (event_size < sched_switch_format.size)
@@ -492,8 +498,18 @@
 
             ParseSchedSwitchCompact(start, timestamp, &sched_switch_format,
                                     compact_sched_buffer, metadata);
+
+            // compact sched_waking
+          } else if (compact_sched_enabled &&
+                     ftrace_event_id == sched_waking_format.event_id) {
+            if (event_size < sched_waking_format.size)
+              return 0;
+
+            ParseSchedWakingCompact(start, timestamp, &sched_waking_format,
+                                    compact_sched_buffer, metadata);
+
           } else {
-            // Parse all other types of enabled events.
+            // Common case: parse all other types of enabled events.
             protos::pbzero::FtraceEvent* event = bundle->add_event();
             event->set_timestamp(timestamp);
             if (!ParseEvent(ftrace_event_id, start, next, table, event,
@@ -663,31 +679,56 @@
 // |CompactSchedSwitchFormat| for the assumptions made around the format, which
 // this code is closely tied to.
 // static
-void CpuReader::ParseSchedSwitchCompact(
-    const uint8_t* start,
-    uint64_t timestamp,
-    const CompactSchedSwitchFormat* format,
-    CompactSchedBundleState* compact_sched_buffer,
-    FtraceMetadata* metadata) {
-  compact_sched_buffer->AppendSwitchTimestamp(timestamp);
+void CpuReader::ParseSchedSwitchCompact(const uint8_t* start,
+                                        uint64_t timestamp,
+                                        const CompactSchedSwitchFormat* format,
+                                        CompactSchedBuffer* compact_buf,
+                                        FtraceMetadata* metadata) {
+  compact_buf->sched_switch().AppendTimestamp(timestamp);
 
   int32_t next_pid = ReadValue<int32_t>(start + format->next_pid_offset);
-  compact_sched_buffer->switch_next_pid()->Append(next_pid);
+  compact_buf->sched_switch().next_pid().Append(next_pid);
   metadata->AddPid(next_pid);
 
   int32_t next_prio = ReadValue<int32_t>(start + format->next_prio_offset);
-  compact_sched_buffer->switch_next_prio()->Append(next_prio);
+  compact_buf->sched_switch().next_prio().Append(next_prio);
 
   // Varint encoding of int32 and int64 is the same, so treat the value as
   // int64 after reading.
   int64_t prev_state = ReadSignedFtraceValue(start + format->prev_state_offset,
                                              format->prev_state_type);
-  compact_sched_buffer->switch_prev_state()->Append(prev_state);
+  compact_buf->sched_switch().prev_state().Append(prev_state);
 
   // next_comm
   const char* comm_ptr =
       reinterpret_cast<const char*>(start + format->next_comm_offset);
-  compact_sched_buffer->InternSwitchNextComm(comm_ptr);
+  size_t iid = compact_buf->interner().InternComm(comm_ptr);
+  compact_buf->sched_switch().next_comm_index().Append(iid);
+}
+
+// static
+void CpuReader::ParseSchedWakingCompact(const uint8_t* start,
+                                        uint64_t timestamp,
+                                        const CompactSchedWakingFormat* format,
+                                        CompactSchedBuffer* compact_buf,
+                                        FtraceMetadata* metadata) {
+  compact_buf->sched_waking().AppendTimestamp(timestamp);
+
+  int32_t pid = ReadValue<int32_t>(start + format->pid_offset);
+  compact_buf->sched_waking().pid().Append(pid);
+  metadata->AddPid(pid);
+
+  int32_t target_cpu = ReadValue<int32_t>(start + format->target_cpu_offset);
+  compact_buf->sched_waking().target_cpu().Append(target_cpu);
+
+  int32_t prio = ReadValue<int32_t>(start + format->prio_offset);
+  compact_buf->sched_waking().prio().Append(prio);
+
+  // comm
+  const char* comm_ptr =
+      reinterpret_cast<const char*>(start + format->comm_offset);
+  size_t iid = compact_buf->interner().InternComm(comm_ptr);
+  compact_buf->sched_waking().comm_index().Append(iid);
 }
 
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index 139eebe..f329044 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -169,7 +169,7 @@
                                  const PageHeader* page_header,
                                  const ProtoTranslationTable* table,
                                  const FtraceDataSourceConfig* ds_config,
-                                 CompactSchedBundleState* compact_sched_buffer,
+                                 CompactSchedBuffer* compact_sched_buffer,
                                  FtraceEventBundle* bundle,
                                  FtraceMetadata* metadata);
 
@@ -198,7 +198,15 @@
   static void ParseSchedSwitchCompact(const uint8_t* start,
                                       uint64_t timestamp,
                                       const CompactSchedSwitchFormat* format,
-                                      CompactSchedBundleState* bundle_state,
+                                      CompactSchedBuffer* compact_buf,
+                                      FtraceMetadata* metadata);
+
+  // Parse a sched_waking event according to pre-validated format, and buffer
+  // the individual fields in the given compact encoding batch.
+  static void ParseSchedWakingCompact(const uint8_t* start,
+                                      uint64_t timestamp,
+                                      const CompactSchedWakingFormat* format,
+                                      CompactSchedBuffer* compact_buf,
                                       FtraceMetadata* metadata);
 
   // Parses & encodes the given range of contiguous tracing pages. Called by
diff --git a/src/traced/probes/ftrace/cpu_reader_benchmark.cc b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
index b4d942f..58b4e20 100644
--- a/src/traced/probes/ftrace/cpu_reader_benchmark.cc
+++ b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
@@ -289,7 +289,7 @@
 
 }  // namespace
 
-using perfetto::CompactSchedBundleState;
+using perfetto::CompactSchedBuffer;
 using perfetto::CpuReader;
 using perfetto::DisabledCompactSchedConfigForTesting;
 using perfetto::EventFilter;
@@ -324,7 +324,7 @@
   while (state.KeepRunning()) {
     writer.Reset(&stream);
 
-    CompactSchedBundleState compact_buffer;
+    CompactSchedBuffer compact_buffer;
     const uint8_t* parse_pos = page.get();
     perfetto::base::Optional<CpuReader::PageHeader> page_header =
         CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index d008744..f8b58cf 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -381,7 +381,7 @@
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -510,7 +510,7 @@
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -560,7 +560,7 @@
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -599,7 +599,7 @@
                                    DisabledCompactSchedConfigForTesting()};
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -665,7 +665,7 @@
       table->EventToFtraceId(GroupAndName("ftrace", "print")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -775,7 +775,7 @@
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -822,7 +822,7 @@
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -846,7 +846,8 @@
   EXPECT_FALSE(bundle->has_compact_sched());
 
   // Instead, sched switch fields were buffered:
-  EXPECT_LT(0u, compact_buffer.interned_switch_comms_size());
+  EXPECT_LT(0u, compact_buffer.sched_switch().size());
+  EXPECT_LT(0u, compact_buffer.interner().interned_comms_size());
 
   // Write the buffer out & check the serialized format:
   compact_buffer.WriteAndReset(bundle_provider.writer());
@@ -861,7 +862,7 @@
   EXPECT_EQ(6, compact_sched.switch_next_pid().size());
   EXPECT_EQ(6, compact_sched.switch_next_prio().size());
   // 4 unique interned next_comm strings:
-  EXPECT_EQ(4, compact_sched.switch_next_comm_table().size());
+  EXPECT_EQ(4, compact_sched.intern_table().size());
   EXPECT_EQ(6, compact_sched.switch_next_comm_index().size());
 
   // First event exactly as expected (absolute timestamp):
@@ -870,7 +871,7 @@
   EXPECT_EQ(1, compact_sched.switch_prev_state(0));
   EXPECT_EQ(3733, compact_sched.switch_next_pid(0));
   EXPECT_EQ(120, compact_sched.switch_next_prio(0));
-  std::string next_comm = compact_sched.switch_next_comm_table(
+  std::string next_comm = compact_sched.intern_table(
       static_cast<int>(compact_sched.switch_next_comm_index(0)));
   EXPECT_EQ("sleep", next_comm);
 }
@@ -1620,7 +1621,7 @@
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -2065,7 +2066,7 @@
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
   FtraceMetadata metadata{};
-  CompactSchedBundleState compact_buffer;
+  CompactSchedBuffer compact_buffer;
   const uint8_t* parse_pos = page.get();
   base::Optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index 8901cb5..890e07a 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -661,8 +661,9 @@
 TEST_F(FtraceConfigMuxerTest, CompactSchedConfig) {
   // Set scheduling event format as validated. The pre-parsed format itself
   // doesn't need to be sensible, as the tests won't use it.
-  auto valid_compact_format = CompactSchedEventFormat{
-      /*format_valid=*/true, CompactSchedSwitchFormat{}};
+  auto valid_compact_format =
+      CompactSchedEventFormat{/*format_valid=*/true, CompactSchedSwitchFormat{},
+                              CompactSchedWakingFormat{}};
 
   NiceMock<MockFtraceProcfs> ftrace;
   table_ = CreateFakeTable(valid_compact_format);
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 0732527..a66ed05 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -34,7 +34,6 @@
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
-#include "src/traced/probes/ftrace/compact_sched.h"
 #include "src/traced/probes/ftrace/cpu_reader.h"
 #include "src/traced/probes/ftrace/cpu_stats_parser.h"
 #include "src/traced/probes/ftrace/event_info.h"
@@ -64,21 +63,10 @@
 // data, which should fit in a typical L2D cache. Furthermore, the batching
 // limits the memory usage of traced_probes.
 //
-// Note that the compact scheduler event encoding buffers operate on the same
-// buffer size, and their maximum capacity is set at compile-time as it lives on
-// the stack. So the maximum possible number of events encoded in this many
-// tracing pages must not exceed compact_sched's capacity. See the static_assert
-// below.
 // TODO(rsavitski): consider making buffering & parsing page counts independent,
 // should be a single counter in the cpu_reader, similar to lost_events case.
 constexpr size_t kParsingBufferSizePages = 32;
 
-static_assert(kParsingBufferSizePages *
-                      (base::kPageSize /
-                       CompactSchedBundleState::kMinSupportedSchedSwitchSize) <=
-                  CompactSchedBundleState::kMaxElements,
-              "insufficient compact_sched buffer capacity");
-
 uint32_t ClampDrainPeriodMs(uint32_t drain_period_ms) {
   if (drain_period_ms == 0) {
     return kDefaultDrainPeriodMs;
diff --git a/src/traced/probes/ftrace/proto_translation_table_unittest.cc b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
index f3841d9..8f54e48 100644
--- a/src/traced/probes/ftrace/proto_translation_table_unittest.cc
+++ b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
@@ -259,9 +259,10 @@
 
 INSTANTIATE_TEST_SUITE_P(BySize, TranslationTableCreationTest, Values(4, 8));
 
-TEST(TranslationTableTest, CompactSchedFormatParsingSeedData) {
+TEST(TranslationTableTest, CompactSchedFormatParsingWalleyeData) {
   std::string path =
-      "src/traced/probes/ftrace/test/data/android_seed_N2F62_3.10.49/";
+      "src/traced/probes/ftrace/test/data/"
+      "android_walleye_OPM5.171019.017.A1_4.4.88/";
   FtraceProcfs ftrace_procfs(path);
   auto table = ProtoTranslationTable::Create(
       &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
@@ -271,30 +272,7 @@
   // Format matches compile-time assumptions.
   ASSERT_TRUE(format.format_valid);
 
-  // Check exact format (note: 32 bit long prev_state).
-  EXPECT_EQ(68u, format.sched_switch.event_id);
-  EXPECT_EQ(60u, format.sched_switch.size);
-  EXPECT_EQ(52u, format.sched_switch.next_pid_offset);
-  EXPECT_EQ(FtraceFieldType::kFtracePid32, format.sched_switch.next_pid_type);
-  EXPECT_EQ(56u, format.sched_switch.next_prio_offset);
-  EXPECT_EQ(FtraceFieldType::kFtraceInt32, format.sched_switch.next_prio_type);
-  EXPECT_EQ(32u, format.sched_switch.prev_state_offset);
-  EXPECT_EQ(FtraceFieldType::kFtraceInt32, format.sched_switch.prev_state_type);
-  EXPECT_EQ(36u, format.sched_switch.next_comm_offset);
-}
-
-TEST(TranslationTableTest, CompactSchedFormatParsingSyntheticData) {
-  std::string path = "src/traced/probes/ftrace/test/data/synthetic/";
-  FtraceProcfs ftrace_procfs(path);
-  auto table = ProtoTranslationTable::Create(
-      &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
-  PERFETTO_CHECK(table);
-  const CompactSchedEventFormat& format = table->compact_sched_format();
-
-  // Format matches compile-time assumptions.
-  ASSERT_TRUE(format.format_valid);
-
-  // Check exact format (note: 64 bit long prev_state).
+  // Check exact sched_switch format (note: 64 bit long prev_state).
   EXPECT_EQ(47u, format.sched_switch.event_id);
   EXPECT_EQ(64u, format.sched_switch.size);
   EXPECT_EQ(56u, format.sched_switch.next_pid_offset);
@@ -304,6 +282,33 @@
   EXPECT_EQ(32u, format.sched_switch.prev_state_offset);
   EXPECT_EQ(FtraceFieldType::kFtraceInt64, format.sched_switch.prev_state_type);
   EXPECT_EQ(40u, format.sched_switch.next_comm_offset);
+
+  // Check exact sched_waking format.
+  EXPECT_EQ(44u, format.sched_waking.event_id);
+  EXPECT_EQ(40u, format.sched_waking.size);
+  EXPECT_EQ(24u, format.sched_waking.pid_offset);
+  EXPECT_EQ(FtraceFieldType::kFtracePid32, format.sched_waking.pid_type);
+  EXPECT_EQ(36u, format.sched_waking.target_cpu_offset);
+  EXPECT_EQ(FtraceFieldType::kFtraceInt32, format.sched_waking.target_cpu_type);
+  EXPECT_EQ(28u, format.sched_waking.prio_offset);
+  EXPECT_EQ(FtraceFieldType::kFtraceInt32, format.sched_waking.prio_type);
+  EXPECT_EQ(8u, format.sched_waking.comm_offset);
+}
+
+TEST(TranslationTableTest, CompactSchedFormatParsingSeedData) {
+  std::string path =
+      "src/traced/probes/ftrace/test/data/android_seed_N2F62_3.10.49/";
+  FtraceProcfs ftrace_procfs(path);
+  auto table = ProtoTranslationTable::Create(
+      &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
+  PERFETTO_CHECK(table);
+  const CompactSchedEventFormat& format = table->compact_sched_format();
+
+  // We consider the entire format invalid as there's no sched_waking event
+  // available. This is a simplifying assumption. We could instead look at each
+  // event independently (and in this case, sched_switch does match compile-time
+  // assumptions).
+  ASSERT_FALSE(format.format_valid);
 }
 
 TEST(TranslationTableTest, InferFtraceType) {
diff --git a/src/traced/probes/ftrace/test/data/synthetic/available_events b/src/traced/probes/ftrace/test/data/synthetic/available_events
index 0a0ea6f..28798fa 100644
--- a/src/traced/probes/ftrace/test/data/synthetic/available_events
+++ b/src/traced/probes/ftrace/test/data/synthetic/available_events
@@ -1,4 +1,5 @@
 sched:sched_switch
+sched:sched_waking
 kmem:ion_heap_grow
 kmem:ion_heap_shrink
 kmem:rss_stat
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/sched/sched_waking/format b/src/traced/probes/ftrace/test/data/synthetic/events/sched/sched_waking/format
new file mode 100644
index 0000000..57bd43f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/sched/sched_waking/format
@@ -0,0 +1,15 @@
+name: sched_waking
+ID: 44
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
+	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
+	field:int common_pid;	offset:4;	size:4;	signed:1;
+
+	field:char comm[16];	offset:8;	size:16;	signed:0;
+	field:pid_t pid;	offset:24;	size:4;	signed:1;
+	field:int prio;	offset:28;	size:4;	signed:1;
+	field:int success;	offset:32;	size:4;	signed:1;
+	field:int target_cpu;	offset:36;	size:4;	signed:1;
+
+print fmt: "comm=%s pid=%d prio=%d target_cpu=%03d", REC->comm, REC->pid, REC->prio, REC->target_cpu
diff --git a/src/traced/probes/power/android_power_data_source.h b/src/traced/probes/power/android_power_data_source.h
index c4efd39..e178fc5 100644
--- a/src/traced/probes/power/android_power_data_source.h
+++ b/src/traced/probes/power/android_power_data_source.h
@@ -27,7 +27,6 @@
 
 namespace perfetto {
 
-class DataSourceConfig;
 class TraceWriter;
 namespace base {
 class TaskRunner;
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index 9c58d68..844da88 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -50,7 +50,6 @@
     "core/shared_memory_abi.cc",
     "core/shared_memory_arbiter_impl.cc",
     "core/shared_memory_arbiter_impl.h",
-    "core/sliced_protobuf_input_stream.cc",
     "core/startup_trace_writer.cc",
     "core/startup_trace_writer_registry.cc",
     "core/trace_buffer.cc",
@@ -64,9 +63,30 @@
   ]
 }
 
+# TODO(primiano): Remove this target as soon as chromium's
+# track_event_json_exporter.cc (which is the only user of this file) is no more.
+# This causes a dependency on libprotobuf, which we want to avoid for our
+# codebase.
+source_set("sliced_protobuf_input_stream") {
+  public_deps = [
+    "../../include/perfetto/base",
+    "../../include/perfetto/ext/tracing/core",
+    "../../protos/perfetto/common:lite",
+  ]
+  deps = [
+    ":tracing",
+    "../../gn:default_deps",
+  ]
+  sources = [
+    "../../include/perfetto/ext/tracing/core/sliced_protobuf_input_stream.h",
+    "core/sliced_protobuf_input_stream.cc",
+  ]
+}
+
 perfetto_unittest_source_set("unittests") {
   testonly = true
   deps = [
+    ":sliced_protobuf_input_stream",
     ":test_support",
     ":tracing",
     "../../gn:default_deps",
diff --git a/src/tracing/api_integrationtest.cc b/src/tracing/api_integrationtest.cc
index dee34a4..44fa477 100644
--- a/src/tracing/api_integrationtest.cc
+++ b/src/tracing/api_integrationtest.cc
@@ -311,6 +311,74 @@
     return log_messages;
   }
 
+  std::vector<std::string> ReadSlicesFromTrace(
+      perfetto::TracingSession* tracing_session) {
+    std::vector<char> raw_trace = tracing_session->ReadTraceBlocking();
+    EXPECT_GE(raw_trace.size(), 0u);
+
+    // Read back the trace, maintaining interning tables as we go.
+    std::vector<std::string> slices;
+    std::map<uint64_t, std::string> categories;
+    std::map<uint64_t, std::string> event_names;
+    perfetto::protos::Trace parsed_trace;
+    EXPECT_TRUE(
+        parsed_trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+
+    bool incremental_state_was_cleared = false;
+    uint32_t sequence_id = 0;
+    for (const auto& packet : parsed_trace.packet()) {
+      if (packet.incremental_state_cleared()) {
+        incremental_state_was_cleared = true;
+        categories.clear();
+        event_names.clear();
+      }
+
+      if (!packet.has_track_event())
+        continue;
+
+      // Make sure we only see track events on one sequence.
+      if (packet.trusted_packet_sequence_id()) {
+        if (!sequence_id)
+          sequence_id = packet.trusted_packet_sequence_id();
+        EXPECT_EQ(sequence_id, packet.trusted_packet_sequence_id());
+      }
+
+      // Update incremental state.
+      if (packet.has_interned_data()) {
+        const auto& interned_data = packet.interned_data();
+        for (const auto& it : interned_data.event_categories()) {
+          EXPECT_EQ(categories.find(it.iid()), categories.end());
+          categories[it.iid()] = it.name();
+        }
+        for (const auto& it : interned_data.event_names()) {
+          EXPECT_EQ(event_names.find(it.iid()), event_names.end());
+          event_names[it.iid()] = it.name();
+        }
+      }
+      const auto& track_event = packet.track_event();
+      std::string slice;
+      switch (track_event.type()) {
+        case perfetto::protos::TrackEvent::TYPE_SLICE_BEGIN:
+          slice += "B";
+          break;
+        case perfetto::protos::TrackEvent::TYPE_SLICE_END:
+          slice += "E";
+          break;
+        case perfetto::protos::TrackEvent::TYPE_INSTANT:
+          slice += "I";
+          break;
+        default:
+        case perfetto::protos::TrackEvent::TYPE_UNSPECIFIED:
+          EXPECT_FALSE(track_event.type());
+      }
+      slice += ":" + categories[track_event.category_iids().Get(0)] + "." +
+               event_names[track_event.name_iid()];
+      slices.push_back(slice);
+    }
+    EXPECT_TRUE(incremental_state_was_cleared);
+    return slices;
+  }
+
   std::map<std::string, TestDataSourceHandle> data_sources_;
   std::list<TestTracingSessionHandle> sessions_;  // Needs stable pointers.
 };
@@ -994,6 +1062,64 @@
               ElementsAre("SomeFunction(file.cc:123): To be, or not to be"));
 }
 
+TEST_F(PerfettoApiTest, TrackEventScoped) {
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+  ds_cfg->set_legacy_config("test");
+
+  // Create a new trace session.
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  {
+    uint64_t arg = 123;
+    TRACE_EVENT("test", "TestEventWithArgs",
+                [&](perfetto::TrackEventContext ctx) {
+                  ctx.track_event()->set_log_message()->set_body_iid(arg);
+                });
+  }
+
+  {
+    // Make sure you can have multiple scoped events in the same scope.
+    TRACE_EVENT("test", "TestEvent");
+    TRACE_EVENT("test", "AnotherEvent");
+    TRACE_EVENT("foo", "DisabledEvent");
+  }
+  perfetto::TrackEvent::Flush();
+
+  tracing_session->get()->StopBlocking();
+  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  EXPECT_THAT(slices, ElementsAre("B:test.TestEventWithArgs", "E:test.",
+                                  "B:test.TestEvent", "B:test.AnotherEvent",
+                                  "E:test.", "E:test."));
+}
+
+TEST_F(PerfettoApiTest, TrackEventInstant) {
+  // Setup the trace config.
+  perfetto::TraceConfig cfg;
+  cfg.set_duration_ms(500);
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("track_event");
+  ds_cfg->set_legacy_config("test");
+
+  // Create a new trace session.
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  TRACE_EVENT_INSTANT("test", "TestEvent");
+  TRACE_EVENT_INSTANT("test", "AnotherEvent");
+  perfetto::TrackEvent::Flush();
+
+  tracing_session->get()->StopBlocking();
+  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  EXPECT_THAT(slices, ElementsAre("I:test.TestEvent", "I:test.AnotherEvent"));
+}
+
 TEST_F(PerfettoApiTest, OneDataSourceOneEvent) {
   auto* data_source = &data_sources_["my_data_source"];
 
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
index 731d68b..4b0283e 100644
--- a/src/tracing/test/tracing_module.cc
+++ b/src/tracing/test/tracing_module.cc
@@ -69,4 +69,11 @@
   puts("Hello");
 }
 
+void FunctionWithOneScopedTrackEvent() {
+  TRACE_EVENT("cat1", "ScopedEventFromModule");
+  // Simulates the non-tracing work of this function, which should take priority
+  // over the above trace event in terms of instruction scheduling.
+  puts("Hello");
+}
+
 }  // namespace tracing_module
diff --git a/src/tracing/test/tracing_module.h b/src/tracing/test/tracing_module.h
index 73ac7fd..bb255a1 100644
--- a/src/tracing/test/tracing_module.h
+++ b/src/tracing/test/tracing_module.h
@@ -35,6 +35,7 @@
 // These functions are used to check the instruction size overhead track events.
 void FunctionWithOneTrackEvent();
 void FunctionWithOneTrackEventWithTypedArgument();
+void FunctionWithOneScopedTrackEvent();
 
 }  // namespace tracing_module
 
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index a1a6108..8ac3760 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -52,7 +52,7 @@
   helper.WaitForConsumerConnect();
 
   TraceConfig trace_config;
-  trace_config.add_buffers()->set_size_kb(10 * 1024);
+  trace_config.add_buffers()->set_size_kb(20 * 1024);
   trace_config.set_duration_ms(6000);
 
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
@@ -68,7 +68,8 @@
   helper.WaitForTracingDisabled(10000 /*ms*/);
   helper.ReadData();
   helper.WaitForReadData();
-
+  StopApp(app_name, "new.app.stopped", &task_runner);
+  task_runner.RunUntilCheckpoint("new.app.stopped", 1000 /*ms*/);
   return helper.trace();
 }
 
@@ -99,14 +100,12 @@
   std::string app_name = "android.perfetto.cts.app.debuggable";
   const auto& packets = ProfileRuntime(app_name);
   AssertGraphPresent(packets);
-  StopApp(app_name);
 }
 
 TEST(HeapprofdJavaCtsTest, ProfileableAppRuntime) {
   std::string app_name = "android.perfetto.cts.app.profileable";
   const auto& packets = ProfileRuntime(app_name);
   AssertGraphPresent(packets);
-  StopApp(app_name);
 }
 
 TEST(HeapprofdJavaCtsTest, ReleaseAppRuntime) {
@@ -117,8 +116,6 @@
     AssertGraphPresent(packets);
   else
     AssertNoProfileContents(packets);
-
-  StopApp(app_name);
 }
 
 }  // namespace
diff --git a/test/metrics/heap_profile_callsites.out b/test/metrics/heap_profile_callsites.out
index 6a975bb..5f8c14c 100644
--- a/test/metrics/heap_profile_callsites.out
+++ b/test/metrics/heap_profile_callsites.out
@@ -58,5 +58,7 @@
         delta_bytes: 1090
       }
     }
+    profile_delta_bytes: 1090
+    profile_total_bytes: 2100
   }
 }
diff --git a/test/metrics/index b/test/metrics/index
index a159f21..8f29693 100644
--- a/test/metrics/index
+++ b/test/metrics/index
@@ -21,5 +21,7 @@
 heap_profile.textproto heap_profile_callsites heap_profile_callsites.out
 heap_profile_no_symbols.textproto unsymbolized_frames unsymbolized_frames.out
 
+../trace_processor/heap_graph.textproto java_heap_stats java_heap_stats.out
+
 # Json output
 ../data/memory_counters.pb trace_metadata trace_metadata.json.out
diff --git a/test/metrics/java_heap_stats.out b/test/metrics/java_heap_stats.out
new file mode 100644
index 0000000..64db708
--- /dev/null
+++ b/test/metrics/java_heap_stats.out
@@ -0,0 +1,11 @@
+java_heap_stats {
+  instance_stats {
+    upid: 2
+    process_name: "system_server"
+    samples {
+      ts: 10
+      heap_size: 224
+      reachable_heap_size: 96
+    }
+  }
+}
diff --git a/test/trace_processor/heap_graph.textproto b/test/trace_processor/heap_graph.textproto
index eb32c54..b785578 100644
--- a/test/trace_processor/heap_graph.textproto
+++ b/test/trace_processor/heap_graph.textproto
@@ -16,6 +16,7 @@
   trusted_packet_sequence_id: 999
   timestamp: 10
   heap_graph {
+    pid: 2
     roots {
       root_type: ROOT_JAVA_FRAME
       object_ids: 0x01
@@ -32,12 +33,18 @@
       type_id: 2
       self_size: 32
     }
+    objects {
+      id: 0x03
+      type_id: 2
+      self_size: 128
+    }
     continued: true
     index: 1
   }
 }
 packet {
   heap_graph {
+    pid: 2
     type_names {
       iid: 1
       str: "FactoryProducerDelegateImplActor"
diff --git a/test/trace_processor/heap_graph_object.out b/test/trace_processor/heap_graph_object.out
index 48bc6b9..b0f9e43 100644
--- a/test/trace_processor/heap_graph_object.out
+++ b/test/trace_processor/heap_graph_object.out
@@ -1,3 +1,4 @@
 "id","type","upid","graph_sample_ts","object_id","self_size","retained_size","unique_retained_size","reference_set_id","reachable","type_name","root_type"
-0,"heap_graph_object",0,10,1,64,96,96,0,1,"FactoryProducerDelegateImplActor","ROOT_JAVA_FRAME"
-1,"heap_graph_object",0,10,2,32,32,32,1,1,"Foo","[NULL]"
+0,"heap_graph_object",2,10,1,64,96,96,0,1,"FactoryProducerDelegateImplActor","ROOT_JAVA_FRAME"
+1,"heap_graph_object",2,10,2,32,32,32,1,1,"Foo","[NULL]"
+2,"heap_graph_object",2,10,3,128,-1,0,1,0,"Foo","[NULL]"
diff --git a/test/trace_processor/index b/test/trace_processor/index
index bf3c781..958b11e 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -149,3 +149,8 @@
 
 # Config
 config.textproto metadata.sql config_metadata.out
+
+# Decoding of sched_waking events from a trace with compact scheduling events.
+# Verifies the contents of raw & instants tables.
+../data/compact_sched.pb sched_waking_raw.sql sched_waking_raw_compact_sched.out
+../data/compact_sched.pb sched_waking_instants.sql sched_waking_instants_compact_sched.out
diff --git a/test/trace_processor/sched_waking_instants.sql b/test/trace_processor/sched_waking_instants.sql
new file mode 100644
index 0000000..1fe4a76
--- /dev/null
+++ b/test/trace_processor/sched_waking_instants.sql
@@ -0,0 +1 @@
+SELECT ts, instants.name, thread.name, thread.tid FROM instants JOIN thread ON instants.ref == thread.utid WHERE instants.name == "sched_waking" ORDER BY ts ASC
diff --git a/test/trace_processor/sched_waking_instants_compact_sched.out b/test/trace_processor/sched_waking_instants_compact_sched.out
new file mode 100644
index 0000000..201d48b
--- /dev/null
+++ b/test/trace_processor/sched_waking_instants_compact_sched.out
@@ -0,0 +1,82 @@
+"ts","name","name","tid"
+250978439491994,"sched_waking","rcu_sched",8
+250978441664547,"sched_waking","ksoftirqd/1",18
+250978441702515,"sched_waking","kworker/u16:18",17473
+250978444958245,"sched_waking","rcuop/0",10
+250978444974443,"sched_waking","rcuop/1",21
+250978444984234,"sched_waking","rcu_preempt",7
+250978445783141,"sched_waking","rcu_sched",8
+250978451590173,"sched_waking","rcu_preempt",7
+250978451591891,"sched_waking","rcu_sched",8
+250978451609131,"sched_waking","kworker/u16:18",17473
+250978451646579,"sched_waking","rcuos/0",11
+250978451716891,"sched_waking","rcuos/1",22
+250978452047048,"sched_waking","kworker/u16:18",17473
+250978458337725,"sched_waking","sugov:0",625
+250978458362100,"sched_waking","rcu_preempt",7
+250978458398455,"sched_waking","rcuop/0",10
+250978458419496,"sched_waking","rcuop/1",21
+250978459118038,"sched_waking","sugov:0",625
+250978465172309,"sched_waking","ksoftirqd/3",34
+250978465266789,"sched_waking","kworker/u16:18",17473
+250978593466697,"sched_waking","kworker/2:0",17438
+250978593734510,"sched_waking","kworker/u16:18",17473
+250978595521125,"sched_waking","kworker/2:3",17754
+250978595696645,"sched_waking","kworker/2:0",17438
+250978596068468,"sched_waking","kworker/2:3",17754
+250978596565656,"sched_waking","kworker/2:0",17438
+250978600598417,"sched_waking","kworker/2:0",17438
+250978600639042,"sched_waking","ksoftirqd/0",6
+250978604651907,"sched_waking","ksoftirqd/0",6
+250978604739980,"sched_waking","kworker/2:0",17438
+250978605185448,"sched_waking","ksoftirqd/2",26
+250978605260240,"sched_waking","kworker/u16:18",17473
+250978608903886,"sched_waking","kworker/2:0",17438
+250978608942220,"sched_waking","ksoftirqd/0",6
+250978612887272,"sched_waking","ksoftirqd/0",6
+250978612971283,"sched_waking","kworker/2:0",17438
+250978616876595,"sched_waking","kworker/2:0",17438
+250978616921700,"sched_waking","ksoftirqd/0",6
+250978617016804,"sched_waking","kworker/u16:18",17473
+250978620859669,"sched_waking","ksoftirqd/0",6
+250978620939148,"sched_waking","kworker/2:0",17438
+250978624836805,"sched_waking","kworker/2:0",17438
+250978624875398,"sched_waking","ksoftirqd/0",6
+250978624978003,"sched_waking","kworker/u16:18",17473
+250978628278107,"sched_waking","logd.klogd",651
+250978629289722,"sched_waking","rcuop/2",29
+250978629405763,"sched_waking","rcu_preempt",7
+250978635024462,"sched_waking","rcu_preempt",7
+250978635060191,"sched_waking","kworker/u16:18",17473
+250978635219514,"sched_waking","rcuop/2",29
+250978683441081,"sched_waking","kworker/2:0",17438
+250978683732331,"sched_waking","kworker/u16:18",17473
+250978685694259,"sched_waking","kworker/u16:18",17473
+250978740127441,"sched_waking","kworker/2:0",17438
+250978740425410,"sched_waking","kworker/u16:18",17473
+250978743210358,"sched_waking","kworker/u16:18",17473
+250978744945567,"sched_waking","logd.klogd",651
+250978746028431,"sched_waking","rcuop/2",29
+250978746173171,"sched_waking","rcu_preempt",7
+250978751622494,"sched_waking","rcu_preempt",7
+250978751661348,"sched_waking","kworker/u16:18",17473
+250978751792859,"sched_waking","rcuop/2",29
+250978751971817,"sched_waking","rcu_preempt",7
+250978758784578,"sched_waking","rcu_preempt",7
+250978758873745,"sched_waking","rcuop/2",29
+250978805144375,"sched_waking","traced_probes",17772
+250978806359896,"sched_waking","kworker/5:1",17667
+250978806428646,"sched_waking","kworker/u16:18",17473
+250978808058698,"sched_waking","rcuop/0",10
+250978808519271,"sched_waking","rcu_preempt",7
+250978815488490,"sched_waking","rcu_preempt",7
+250978815535678,"sched_waking","kworker/u16:18",17473
+250978815675782,"sched_waking","rcuop/0",10
+250978815738022,"sched_waking","rcuop/1",21
+250978860203182,"sched_waking","kworker/u16:18",17473
+250978880151205,"sched_waking","kworker/2:0",17438
+250978880432143,"sched_waking","kworker/u16:18",17473
+250978884120216,"sched_waking","traced",17771
+250978885260685,"sched_waking","rcuop/0",10
+250978885784018,"sched_waking","rcu_preempt",7
+250978885786674,"sched_waking","traced_probes",17772
diff --git a/test/trace_processor/sched_waking_raw.sql b/test/trace_processor/sched_waking_raw.sql
new file mode 100644
index 0000000..259e2fe
--- /dev/null
+++ b/test/trace_processor/sched_waking_raw.sql
@@ -0,0 +1 @@
+SELECT ts, name, cpu, key, int_value, string_value FROM raw JOIN args ON raw.arg_set_id == args.arg_set_id WHERE name == "sched_waking" ORDER BY cpu ASC, ts ASC;
diff --git a/test/trace_processor/sched_waking_raw_compact_sched.out b/test/trace_processor/sched_waking_raw_compact_sched.out
new file mode 100644
index 0000000..7067a25
--- /dev/null
+++ b/test/trace_processor/sched_waking_raw_compact_sched.out
@@ -0,0 +1,406 @@
+"ts","name","cpu","key","int_value","string_value"
+250978451591891,"sched_waking",0,"comm","[NULL]","rcu_sched"
+250978451591891,"sched_waking",0,"pid",8,"[NULL]"
+250978451591891,"sched_waking",0,"prio",120,"[NULL]"
+250978451591891,"sched_waking",0,"success",1,"[NULL]"
+250978451591891,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978451609131,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978451609131,"sched_waking",0,"pid",17473,"[NULL]"
+250978451609131,"sched_waking",0,"prio",120,"[NULL]"
+250978451609131,"sched_waking",0,"success",1,"[NULL]"
+250978451609131,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978596565656,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978596565656,"sched_waking",0,"pid",17438,"[NULL]"
+250978596565656,"sched_waking",0,"prio",120,"[NULL]"
+250978596565656,"sched_waking",0,"success",1,"[NULL]"
+250978596565656,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978600598417,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978600598417,"sched_waking",0,"pid",17438,"[NULL]"
+250978600598417,"sched_waking",0,"prio",120,"[NULL]"
+250978600598417,"sched_waking",0,"success",1,"[NULL]"
+250978600598417,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978600639042,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978600639042,"sched_waking",0,"pid",6,"[NULL]"
+250978600639042,"sched_waking",0,"prio",120,"[NULL]"
+250978600639042,"sched_waking",0,"success",1,"[NULL]"
+250978600639042,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978604651907,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978604651907,"sched_waking",0,"pid",6,"[NULL]"
+250978604651907,"sched_waking",0,"prio",120,"[NULL]"
+250978604651907,"sched_waking",0,"success",1,"[NULL]"
+250978604651907,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978604739980,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978604739980,"sched_waking",0,"pid",17438,"[NULL]"
+250978604739980,"sched_waking",0,"prio",120,"[NULL]"
+250978604739980,"sched_waking",0,"success",1,"[NULL]"
+250978604739980,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978608903886,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978608903886,"sched_waking",0,"pid",17438,"[NULL]"
+250978608903886,"sched_waking",0,"prio",120,"[NULL]"
+250978608903886,"sched_waking",0,"success",1,"[NULL]"
+250978608903886,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978608942220,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978608942220,"sched_waking",0,"pid",6,"[NULL]"
+250978608942220,"sched_waking",0,"prio",120,"[NULL]"
+250978608942220,"sched_waking",0,"success",1,"[NULL]"
+250978608942220,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978612887272,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978612887272,"sched_waking",0,"pid",6,"[NULL]"
+250978612887272,"sched_waking",0,"prio",120,"[NULL]"
+250978612887272,"sched_waking",0,"success",1,"[NULL]"
+250978612887272,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978612971283,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978612971283,"sched_waking",0,"pid",17438,"[NULL]"
+250978612971283,"sched_waking",0,"prio",120,"[NULL]"
+250978612971283,"sched_waking",0,"success",1,"[NULL]"
+250978612971283,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978616876595,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978616876595,"sched_waking",0,"pid",17438,"[NULL]"
+250978616876595,"sched_waking",0,"prio",120,"[NULL]"
+250978616876595,"sched_waking",0,"success",1,"[NULL]"
+250978616876595,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978616921700,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978616921700,"sched_waking",0,"pid",6,"[NULL]"
+250978616921700,"sched_waking",0,"prio",120,"[NULL]"
+250978616921700,"sched_waking",0,"success",1,"[NULL]"
+250978616921700,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978617016804,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978617016804,"sched_waking",0,"pid",17473,"[NULL]"
+250978617016804,"sched_waking",0,"prio",120,"[NULL]"
+250978617016804,"sched_waking",0,"success",1,"[NULL]"
+250978617016804,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978620859669,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978620859669,"sched_waking",0,"pid",6,"[NULL]"
+250978620859669,"sched_waking",0,"prio",120,"[NULL]"
+250978620859669,"sched_waking",0,"success",1,"[NULL]"
+250978620859669,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978620939148,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978620939148,"sched_waking",0,"pid",17438,"[NULL]"
+250978620939148,"sched_waking",0,"prio",120,"[NULL]"
+250978620939148,"sched_waking",0,"success",1,"[NULL]"
+250978620939148,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978624836805,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978624836805,"sched_waking",0,"pid",17438,"[NULL]"
+250978624836805,"sched_waking",0,"prio",120,"[NULL]"
+250978624836805,"sched_waking",0,"success",1,"[NULL]"
+250978624836805,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978624875398,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978624875398,"sched_waking",0,"pid",6,"[NULL]"
+250978624875398,"sched_waking",0,"prio",120,"[NULL]"
+250978624875398,"sched_waking",0,"success",1,"[NULL]"
+250978624875398,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978624978003,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978624978003,"sched_waking",0,"pid",17473,"[NULL]"
+250978624978003,"sched_waking",0,"prio",120,"[NULL]"
+250978624978003,"sched_waking",0,"success",1,"[NULL]"
+250978624978003,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978441664547,"sched_waking",1,"comm","[NULL]","ksoftirqd/1"
+250978441664547,"sched_waking",1,"pid",18,"[NULL]"
+250978441664547,"sched_waking",1,"prio",120,"[NULL]"
+250978441664547,"sched_waking",1,"success",1,"[NULL]"
+250978441664547,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978441702515,"sched_waking",1,"comm","[NULL]","kworker/u16:18"
+250978441702515,"sched_waking",1,"pid",17473,"[NULL]"
+250978441702515,"sched_waking",1,"prio",120,"[NULL]"
+250978441702515,"sched_waking",1,"success",1,"[NULL]"
+250978441702515,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978805144375,"sched_waking",1,"comm","[NULL]","traced_probes"
+250978805144375,"sched_waking",1,"pid",17772,"[NULL]"
+250978805144375,"sched_waking",1,"prio",120,"[NULL]"
+250978805144375,"sched_waking",1,"success",1,"[NULL]"
+250978805144375,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978808058698,"sched_waking",1,"comm","[NULL]","rcuop/0"
+250978808058698,"sched_waking",1,"pid",10,"[NULL]"
+250978808058698,"sched_waking",1,"prio",120,"[NULL]"
+250978808058698,"sched_waking",1,"success",1,"[NULL]"
+250978808058698,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978884120216,"sched_waking",1,"comm","[NULL]","traced"
+250978884120216,"sched_waking",1,"pid",17771,"[NULL]"
+250978884120216,"sched_waking",1,"prio",120,"[NULL]"
+250978884120216,"sched_waking",1,"success",1,"[NULL]"
+250978884120216,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978885260685,"sched_waking",1,"comm","[NULL]","rcuop/0"
+250978885260685,"sched_waking",1,"pid",10,"[NULL]"
+250978885260685,"sched_waking",1,"prio",120,"[NULL]"
+250978885260685,"sched_waking",1,"success",1,"[NULL]"
+250978885260685,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978885786674,"sched_waking",1,"comm","[NULL]","traced_probes"
+250978885786674,"sched_waking",1,"pid",17772,"[NULL]"
+250978885786674,"sched_waking",1,"prio",120,"[NULL]"
+250978885786674,"sched_waking",1,"success",1,"[NULL]"
+250978885786674,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978444958245,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978444958245,"sched_waking",2,"pid",10,"[NULL]"
+250978444958245,"sched_waking",2,"prio",120,"[NULL]"
+250978444958245,"sched_waking",2,"success",1,"[NULL]"
+250978444958245,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978444974443,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978444974443,"sched_waking",2,"pid",21,"[NULL]"
+250978444974443,"sched_waking",2,"prio",120,"[NULL]"
+250978444974443,"sched_waking",2,"success",1,"[NULL]"
+250978444974443,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978444984234,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978444984234,"sched_waking",2,"pid",7,"[NULL]"
+250978444984234,"sched_waking",2,"prio",120,"[NULL]"
+250978444984234,"sched_waking",2,"success",1,"[NULL]"
+250978444984234,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978451590173,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978451590173,"sched_waking",2,"pid",7,"[NULL]"
+250978451590173,"sched_waking",2,"prio",120,"[NULL]"
+250978451590173,"sched_waking",2,"success",1,"[NULL]"
+250978451590173,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978451646579,"sched_waking",2,"comm","[NULL]","rcuos/0"
+250978451646579,"sched_waking",2,"pid",11,"[NULL]"
+250978451646579,"sched_waking",2,"prio",120,"[NULL]"
+250978451646579,"sched_waking",2,"success",1,"[NULL]"
+250978451646579,"sched_waking",2,"target_cpu",4,"[NULL]"
+250978451716891,"sched_waking",2,"comm","[NULL]","rcuos/1"
+250978451716891,"sched_waking",2,"pid",22,"[NULL]"
+250978451716891,"sched_waking",2,"prio",120,"[NULL]"
+250978451716891,"sched_waking",2,"success",1,"[NULL]"
+250978451716891,"sched_waking",2,"target_cpu",1,"[NULL]"
+250978452047048,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978452047048,"sched_waking",2,"pid",17473,"[NULL]"
+250978452047048,"sched_waking",2,"prio",120,"[NULL]"
+250978452047048,"sched_waking",2,"success",1,"[NULL]"
+250978452047048,"sched_waking",2,"target_cpu",0,"[NULL]"
+250978458337725,"sched_waking",2,"comm","[NULL]","sugov:0"
+250978458337725,"sched_waking",2,"pid",625,"[NULL]"
+250978458337725,"sched_waking",2,"prio",49,"[NULL]"
+250978458337725,"sched_waking",2,"success",1,"[NULL]"
+250978458337725,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978458362100,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978458362100,"sched_waking",2,"pid",7,"[NULL]"
+250978458362100,"sched_waking",2,"prio",120,"[NULL]"
+250978458362100,"sched_waking",2,"success",1,"[NULL]"
+250978458362100,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978458398455,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978458398455,"sched_waking",2,"pid",10,"[NULL]"
+250978458398455,"sched_waking",2,"prio",120,"[NULL]"
+250978458398455,"sched_waking",2,"success",1,"[NULL]"
+250978458398455,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978458419496,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978458419496,"sched_waking",2,"pid",21,"[NULL]"
+250978458419496,"sched_waking",2,"prio",120,"[NULL]"
+250978458419496,"sched_waking",2,"success",1,"[NULL]"
+250978458419496,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978459118038,"sched_waking",2,"comm","[NULL]","sugov:0"
+250978459118038,"sched_waking",2,"pid",625,"[NULL]"
+250978459118038,"sched_waking",2,"prio",49,"[NULL]"
+250978459118038,"sched_waking",2,"success",1,"[NULL]"
+250978459118038,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978593466697,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978593466697,"sched_waking",2,"pid",17438,"[NULL]"
+250978593466697,"sched_waking",2,"prio",120,"[NULL]"
+250978593466697,"sched_waking",2,"success",1,"[NULL]"
+250978593466697,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978593734510,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978593734510,"sched_waking",2,"pid",17473,"[NULL]"
+250978593734510,"sched_waking",2,"prio",120,"[NULL]"
+250978593734510,"sched_waking",2,"success",1,"[NULL]"
+250978593734510,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978595521125,"sched_waking",2,"comm","[NULL]","kworker/2:3"
+250978595521125,"sched_waking",2,"pid",17754,"[NULL]"
+250978595521125,"sched_waking",2,"prio",120,"[NULL]"
+250978595521125,"sched_waking",2,"success",1,"[NULL]"
+250978595521125,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978595696645,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978595696645,"sched_waking",2,"pid",17438,"[NULL]"
+250978595696645,"sched_waking",2,"prio",120,"[NULL]"
+250978595696645,"sched_waking",2,"success",1,"[NULL]"
+250978595696645,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978596068468,"sched_waking",2,"comm","[NULL]","kworker/2:3"
+250978596068468,"sched_waking",2,"pid",17754,"[NULL]"
+250978596068468,"sched_waking",2,"prio",120,"[NULL]"
+250978596068468,"sched_waking",2,"success",1,"[NULL]"
+250978596068468,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978605185448,"sched_waking",2,"comm","[NULL]","ksoftirqd/2"
+250978605185448,"sched_waking",2,"pid",26,"[NULL]"
+250978605185448,"sched_waking",2,"prio",120,"[NULL]"
+250978605185448,"sched_waking",2,"success",1,"[NULL]"
+250978605185448,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978605260240,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978605260240,"sched_waking",2,"pid",17473,"[NULL]"
+250978605260240,"sched_waking",2,"prio",120,"[NULL]"
+250978605260240,"sched_waking",2,"success",1,"[NULL]"
+250978605260240,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978628278107,"sched_waking",2,"comm","[NULL]","logd.klogd"
+250978628278107,"sched_waking",2,"pid",651,"[NULL]"
+250978628278107,"sched_waking",2,"prio",130,"[NULL]"
+250978628278107,"sched_waking",2,"success",1,"[NULL]"
+250978628278107,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978629289722,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978629289722,"sched_waking",2,"pid",29,"[NULL]"
+250978629289722,"sched_waking",2,"prio",120,"[NULL]"
+250978629289722,"sched_waking",2,"success",1,"[NULL]"
+250978629289722,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978629405763,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978629405763,"sched_waking",2,"pid",7,"[NULL]"
+250978629405763,"sched_waking",2,"prio",120,"[NULL]"
+250978629405763,"sched_waking",2,"success",1,"[NULL]"
+250978629405763,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978635024462,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978635024462,"sched_waking",2,"pid",7,"[NULL]"
+250978635024462,"sched_waking",2,"prio",120,"[NULL]"
+250978635024462,"sched_waking",2,"success",1,"[NULL]"
+250978635024462,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978635060191,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978635060191,"sched_waking",2,"pid",17473,"[NULL]"
+250978635060191,"sched_waking",2,"prio",120,"[NULL]"
+250978635060191,"sched_waking",2,"success",1,"[NULL]"
+250978635060191,"sched_waking",2,"target_cpu",0,"[NULL]"
+250978635219514,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978635219514,"sched_waking",2,"pid",29,"[NULL]"
+250978635219514,"sched_waking",2,"prio",120,"[NULL]"
+250978635219514,"sched_waking",2,"success",1,"[NULL]"
+250978635219514,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978683441081,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978683441081,"sched_waking",2,"pid",17438,"[NULL]"
+250978683441081,"sched_waking",2,"prio",120,"[NULL]"
+250978683441081,"sched_waking",2,"success",1,"[NULL]"
+250978683441081,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978683732331,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978683732331,"sched_waking",2,"pid",17473,"[NULL]"
+250978683732331,"sched_waking",2,"prio",120,"[NULL]"
+250978683732331,"sched_waking",2,"success",1,"[NULL]"
+250978683732331,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978685694259,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978685694259,"sched_waking",2,"pid",17473,"[NULL]"
+250978685694259,"sched_waking",2,"prio",120,"[NULL]"
+250978685694259,"sched_waking",2,"success",1,"[NULL]"
+250978685694259,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978740127441,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978740127441,"sched_waking",2,"pid",17438,"[NULL]"
+250978740127441,"sched_waking",2,"prio",120,"[NULL]"
+250978740127441,"sched_waking",2,"success",1,"[NULL]"
+250978740127441,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978740425410,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978740425410,"sched_waking",2,"pid",17473,"[NULL]"
+250978740425410,"sched_waking",2,"prio",120,"[NULL]"
+250978740425410,"sched_waking",2,"success",1,"[NULL]"
+250978740425410,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978743210358,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978743210358,"sched_waking",2,"pid",17473,"[NULL]"
+250978743210358,"sched_waking",2,"prio",120,"[NULL]"
+250978743210358,"sched_waking",2,"success",1,"[NULL]"
+250978743210358,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978744945567,"sched_waking",2,"comm","[NULL]","logd.klogd"
+250978744945567,"sched_waking",2,"pid",651,"[NULL]"
+250978744945567,"sched_waking",2,"prio",130,"[NULL]"
+250978744945567,"sched_waking",2,"success",1,"[NULL]"
+250978744945567,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978746028431,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978746028431,"sched_waking",2,"pid",29,"[NULL]"
+250978746028431,"sched_waking",2,"prio",120,"[NULL]"
+250978746028431,"sched_waking",2,"success",1,"[NULL]"
+250978746028431,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978746173171,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978746173171,"sched_waking",2,"pid",7,"[NULL]"
+250978746173171,"sched_waking",2,"prio",120,"[NULL]"
+250978746173171,"sched_waking",2,"success",1,"[NULL]"
+250978746173171,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751622494,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978751622494,"sched_waking",2,"pid",7,"[NULL]"
+250978751622494,"sched_waking",2,"prio",120,"[NULL]"
+250978751622494,"sched_waking",2,"success",1,"[NULL]"
+250978751622494,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751661348,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978751661348,"sched_waking",2,"pid",17473,"[NULL]"
+250978751661348,"sched_waking",2,"prio",120,"[NULL]"
+250978751661348,"sched_waking",2,"success",1,"[NULL]"
+250978751661348,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751792859,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978751792859,"sched_waking",2,"pid",29,"[NULL]"
+250978751792859,"sched_waking",2,"prio",120,"[NULL]"
+250978751792859,"sched_waking",2,"success",1,"[NULL]"
+250978751792859,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751971817,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978751971817,"sched_waking",2,"pid",7,"[NULL]"
+250978751971817,"sched_waking",2,"prio",120,"[NULL]"
+250978751971817,"sched_waking",2,"success",1,"[NULL]"
+250978751971817,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978758784578,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978758784578,"sched_waking",2,"pid",7,"[NULL]"
+250978758784578,"sched_waking",2,"prio",120,"[NULL]"
+250978758784578,"sched_waking",2,"success",1,"[NULL]"
+250978758784578,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978758873745,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978758873745,"sched_waking",2,"pid",29,"[NULL]"
+250978758873745,"sched_waking",2,"prio",120,"[NULL]"
+250978758873745,"sched_waking",2,"success",1,"[NULL]"
+250978758873745,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978808519271,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978808519271,"sched_waking",2,"pid",7,"[NULL]"
+250978808519271,"sched_waking",2,"prio",120,"[NULL]"
+250978808519271,"sched_waking",2,"success",1,"[NULL]"
+250978808519271,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815488490,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978815488490,"sched_waking",2,"pid",7,"[NULL]"
+250978815488490,"sched_waking",2,"prio",120,"[NULL]"
+250978815488490,"sched_waking",2,"success",1,"[NULL]"
+250978815488490,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815535678,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978815535678,"sched_waking",2,"pid",17473,"[NULL]"
+250978815535678,"sched_waking",2,"prio",120,"[NULL]"
+250978815535678,"sched_waking",2,"success",1,"[NULL]"
+250978815535678,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815675782,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978815675782,"sched_waking",2,"pid",10,"[NULL]"
+250978815675782,"sched_waking",2,"prio",120,"[NULL]"
+250978815675782,"sched_waking",2,"success",1,"[NULL]"
+250978815675782,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815738022,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978815738022,"sched_waking",2,"pid",21,"[NULL]"
+250978815738022,"sched_waking",2,"prio",120,"[NULL]"
+250978815738022,"sched_waking",2,"success",1,"[NULL]"
+250978815738022,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978860203182,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978860203182,"sched_waking",2,"pid",17473,"[NULL]"
+250978860203182,"sched_waking",2,"prio",120,"[NULL]"
+250978860203182,"sched_waking",2,"success",1,"[NULL]"
+250978860203182,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978880151205,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978880151205,"sched_waking",2,"pid",17438,"[NULL]"
+250978880151205,"sched_waking",2,"prio",120,"[NULL]"
+250978880151205,"sched_waking",2,"success",1,"[NULL]"
+250978880151205,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978880432143,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978880432143,"sched_waking",2,"pid",17473,"[NULL]"
+250978880432143,"sched_waking",2,"prio",120,"[NULL]"
+250978880432143,"sched_waking",2,"success",1,"[NULL]"
+250978880432143,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978885784018,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978885784018,"sched_waking",2,"pid",7,"[NULL]"
+250978885784018,"sched_waking",2,"prio",120,"[NULL]"
+250978885784018,"sched_waking",2,"success",1,"[NULL]"
+250978885784018,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978465172309,"sched_waking",3,"comm","[NULL]","ksoftirqd/3"
+250978465172309,"sched_waking",3,"pid",34,"[NULL]"
+250978465172309,"sched_waking",3,"prio",120,"[NULL]"
+250978465172309,"sched_waking",3,"success",1,"[NULL]"
+250978465172309,"sched_waking",3,"target_cpu",3,"[NULL]"
+250978465266789,"sched_waking",3,"comm","[NULL]","kworker/u16:18"
+250978465266789,"sched_waking",3,"pid",17473,"[NULL]"
+250978465266789,"sched_waking",3,"prio",120,"[NULL]"
+250978465266789,"sched_waking",3,"success",1,"[NULL]"
+250978465266789,"sched_waking",3,"target_cpu",0,"[NULL]"
+250978439491994,"sched_waking",4,"comm","[NULL]","rcu_sched"
+250978439491994,"sched_waking",4,"pid",8,"[NULL]"
+250978439491994,"sched_waking",4,"prio",120,"[NULL]"
+250978439491994,"sched_waking",4,"success",1,"[NULL]"
+250978439491994,"sched_waking",4,"target_cpu",1,"[NULL]"
+250978445783141,"sched_waking",5,"comm","[NULL]","rcu_sched"
+250978445783141,"sched_waking",5,"pid",8,"[NULL]"
+250978445783141,"sched_waking",5,"prio",120,"[NULL]"
+250978445783141,"sched_waking",5,"success",1,"[NULL]"
+250978445783141,"sched_waking",5,"target_cpu",5,"[NULL]"
+250978806359896,"sched_waking",5,"comm","[NULL]","kworker/5:1"
+250978806359896,"sched_waking",5,"pid",17667,"[NULL]"
+250978806359896,"sched_waking",5,"prio",120,"[NULL]"
+250978806359896,"sched_waking",5,"success",1,"[NULL]"
+250978806359896,"sched_waking",5,"target_cpu",5,"[NULL]"
+250978806428646,"sched_waking",5,"comm","[NULL]","kworker/u16:18"
+250978806428646,"sched_waking",5,"pid",17473,"[NULL]"
+250978806428646,"sched_waking",5,"prio",120,"[NULL]"
+250978806428646,"sched_waking",5,"success",1,"[NULL]"
+250978806428646,"sched_waking",5,"target_cpu",2,"[NULL]"
diff --git a/tools/compact_reencode/main.cc b/tools/compact_reencode/main.cc
index d54db1c..ae4a2c4 100644
--- a/tools/compact_reencode/main.cc
+++ b/tools/compact_reencode/main.cc
@@ -30,8 +30,14 @@
 
 // Re-encodes the given trace, converting sched events to their compact
 // representation.
-// Note: doesn't do bundle splitting/merging, the original trace must already
-// have multi-page bundles for the re-encoding to be realistic.
+//
+// Notes:
+// * doesn't do bundle splitting/merging, the original trace must already
+//   have multi-page bundles for the re-encoding to be realistic.
+// * when importing the resulting trace into trace_processor, a few leading
+//   switch/wakeup events can be skipped (since there's not enough info to
+//   reconstruct the full events at that point), and this might change the
+//   trace_bounds.
 
 namespace perfetto {
 namespace compact_reencode {
@@ -91,11 +97,20 @@
     return static_cast<uint32_t>(new_idx);
   };
 
+  // sched_waking pieces
+  protozero::PackedVarInt waking_timestamp;
+  protozero::PackedVarInt waking_pid;
+  protozero::PackedVarInt waking_target_cpu;
+  protozero::PackedVarInt waking_prio;
+  protozero::PackedVarInt waking_comm_index;
+
+  uint64_t last_waking_timestamp = 0;
+
   for (auto event_it = bundle.event(); event_it; ++event_it) {
     protos::pbzero::FtraceEvent::Decoder event(*event_it);
-    if (!event.has_sched_switch()) {
+    if (!event.has_sched_switch() && !event.has_sched_waking()) {
       CopyField(bundle_out, event_it.field());
-    } else {
+    } else if (event.has_sched_switch()) {
       switch_timestamp.Append(event.timestamp() - last_switch_timestamp);
       last_switch_timestamp = event.timestamp();
 
@@ -108,18 +123,38 @@
       switch_next_pid.Append(sswitch.next_pid());
       switch_next_prio.Append(sswitch.next_prio());
       switch_prev_state.Append(sswitch.prev_state());
+    } else {
+      waking_timestamp.Append(event.timestamp() - last_waking_timestamp);
+      last_waking_timestamp = event.timestamp();
+
+      protos::pbzero::SchedWakingFtraceEvent::Decoder swaking(
+          event.sched_waking());
+
+      auto iid = intern(swaking.comm().ToStdString());
+      waking_comm_index.Append(iid);
+
+      waking_pid.Append(swaking.pid());
+      waking_target_cpu.Append(swaking.target_cpu());
+      waking_prio.Append(swaking.prio());
     }
   }
+
   auto* compact_sched = bundle_out->set_compact_sched();
 
   for (const auto& s : string_table)
-    compact_sched->add_switch_next_comm_table(s.data(), s.size());
+    compact_sched->add_intern_table(s.data(), s.size());
 
   compact_sched->set_switch_timestamp(switch_timestamp);
   compact_sched->set_switch_next_comm_index(switch_next_comm_index);
   compact_sched->set_switch_next_pid(switch_next_pid);
   compact_sched->set_switch_next_prio(switch_next_prio);
   compact_sched->set_switch_prev_state(switch_prev_state);
+
+  compact_sched->set_waking_timestamp(waking_timestamp);
+  compact_sched->set_waking_pid(waking_pid);
+  compact_sched->set_waking_target_cpu(waking_target_cpu);
+  compact_sched->set_waking_prio(waking_prio);
+  compact_sched->set_waking_comm_index(waking_comm_index);
 }
 
 std::string ReEncode(const std::string& raw) {
diff --git a/tools/heap_profile b/tools/heap_profile
index 8f57b59..967299e 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -188,6 +188,11 @@
       "This is the default",
       action="store_true")
   parser.add_argument(
+      "--block-client-timeout",
+      help="If --block-client is given, do not block any allocation for "
+      "longer than this timeout (us).",
+      type=int)
+  parser.add_argument(
       "--no-block-client",
       help="When buffer is full, stop the "
       "profile early.",
@@ -245,6 +250,8 @@
   target_cfg = ""
   if not args.no_block_client:
     target_cfg += "block_client: true\n"
+  if args.block_client_timeout:
+    target_cfg += "block_client_timeout_us: %s\n" % args.block_client_timeout
   if args.idle_allocations:
     target_cfg += "idle_allocations: true\n"
   if args.no_startup:
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 1f454bb..a8cd83e 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -146,8 +146,8 @@
     # Example traces for regression tests.
     (
         'buildtools/test_data.zip',
-        'https://storage.googleapis.com/perfetto/test-data-20191025-102200.zip',
-        '25a61211f82cd73c6ce5b67da9d73f046de7b3f9',
+        'https://storage.googleapis.com/perfetto/test-data-20191107-164334.zip',
+        '499f11fbc2b04ef7742662a26b85ef03141e24bd',
         'all',
     ),
 
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 885cc4c..56afebd 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -25,11 +25,11 @@
   NewEngineMode,
   OmniboxState,
   RecordConfig,
+  RecordingTarget,
   SCROLLING_TRACK_GROUP,
   SelectedTimeRange,
   State,
   Status,
-  TargetOs,
   TraceSource,
   TraceTime,
   TrackState,
@@ -51,9 +51,9 @@
   const nextId = state.nextId;
   const recordConfig = state.recordConfig;
   const route = state.route;
-  const androidDeviceConnected = state.androidDeviceConnected;
+  const recordingTarget = state.recordingTarget;
   const extensionInstalled = state.extensionInstalled;
-  const availableDevices = state.availableDevices;
+  const availableAdbDevices = state.availableAdbDevices;
   const chromeCategories = state.chromeCategories;
   const newEngineMode = state.newEngineMode;
 
@@ -61,9 +61,9 @@
   state.nextId = nextId;
   state.recordConfig = recordConfig;
   state.route = route;
-  state.androidDeviceConnected = androidDeviceConnected;
+  state.recordingTarget = recordingTarget;
   state.extensionInstalled = extensionInstalled;
-  state.availableDevices = availableDevices;
+  state.availableAdbDevices = availableAdbDevices;
   state.chromeCategories = chromeCategories;
   state.newEngineMode = newEngineMode;
 }
@@ -348,7 +348,7 @@
     this.selectNote(state, {id});
   },
 
-  toggleVideo(state: StateDraft): void {
+  toggleVideo(state: StateDraft, _: {}): void {
     state.videoEnabled = !state.videoEnabled;
     if (!state.videoEnabled) {
       state.video = null;
@@ -360,13 +360,13 @@
     }
   },
 
-  toggleFlagPause(state: StateDraft): void {
+  toggleFlagPause(state: StateDraft, _: {}): void {
     if (state.video != null) {
       state.flagPauseEnabled = !state.flagPauseEnabled;
     }
   },
 
-  toggleScrubbing(state: StateDraft): void {
+  toggleScrubbing(state: StateDraft, _: {}): void {
     if (state.video != null) {
       state.scrubbingEnabled = !state.scrubbingEnabled;
     }
@@ -476,17 +476,17 @@
     state.logsPagination = args;
   },
 
-  startRecording(state: StateDraft): void {
+  startRecording(state: StateDraft, _: {}): void {
     state.recordingInProgress = true;
     state.lastRecordingError = undefined;
     state.recordingCancelled = false;
   },
 
-  stopRecording(state: StateDraft): void {
+  stopRecording(state: StateDraft, _: {}): void {
     state.recordingInProgress = false;
   },
 
-  cancelRecording(state: StateDraft): void {
+  cancelRecording(state: StateDraft, _: {}): void {
     state.recordingInProgress = false;
     state.recordingCancelled = true;
   },
@@ -499,17 +499,14 @@
     state.bufferUsage = args.percentage;
   },
 
-  setAndroidDevice(state: StateDraft, args: {target?: AdbRecordingTarget}):
-      void {
-        state.recordConfig.targetOS =
-            args.target ? args.target.os as TargetOs : 'Q';
-        state.androidDeviceConnected = args.target;
-      },
+  setRecordingTarget(state: StateDraft, args: {target: RecordingTarget}): void {
+    state.recordingTarget = args.target;
+  },
 
-  setAvailableDevices(state: StateDraft, args: {devices: AdbRecordingTarget[]}):
-      void {
-        state.availableDevices = args.devices;
-      },
+  setAvailableAdbDevices(
+      state: StateDraft, args: {devices: AdbRecordingTarget[]}): void {
+    state.availableAdbDevices = args.devices;
+  },
 
   setOmnibox(state: StateDraft, args: OmniboxState): void {
     state.frontendLocalState.omniboxState = args;
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 10d7924..158bf05 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -178,10 +178,13 @@
   count: number;
 }
 
-export interface AdbRecordingTarget {
-  serial: string;
+export interface RecordingTarget {
   name: string;
-  os: string;
+  os: TargetOs;
+}
+
+export interface AdbRecordingTarget extends RecordingTarget {
+  serial: string;
 }
 
 export interface State {
@@ -237,8 +240,8 @@
   recordingInProgress: boolean;
   recordingCancelled: boolean;
   extensionInstalled: boolean;
-  androidDeviceConnected?: AdbRecordingTarget;
-  availableDevices: AdbRecordingTarget[];
+  recordingTarget: RecordingTarget;
+  availableAdbDevices: AdbRecordingTarget[];
   lastRecordingError?: string;
   recordingStatus?: string;
 
@@ -256,23 +259,28 @@
 // 'Q','P','O' for Android, 'L' for Linux, 'C' for Chrome.
 export declare type TargetOs = 'Q' | 'P' | 'O' | 'C' | 'L';
 
-export function isAndroidTarget(target: TargetOs) {
-  return ['Q', 'P', 'O'].includes(target);
+export function isAndroidTarget(target: RecordingTarget) {
+  return ['Q', 'P', 'O'].includes(target.os);
 }
 
-export function isChromeTarget(target: TargetOs) {
-  return target === 'C';
+export function isChromeTarget(target: RecordingTarget) {
+  return target.os === 'C';
 }
 
-export function isLinuxTarget(target: TargetOs) {
-  return target === 'L';
+export function isLinuxTarget(target: RecordingTarget) {
+  return target.os === 'L';
+}
+
+export function isAdbTarget(target: RecordingTarget):
+    target is AdbRecordingTarget {
+  if ((target as AdbRecordingTarget).serial) return true;
+  return false;
 }
 
 export interface RecordConfig {
   [key: string]: null|number|boolean|string|string[];
 
   // Global settings
-  targetOS: TargetOs;
   mode: RecordMode;
   durationMs: number;
   bufferSizeMb: number;
@@ -330,7 +338,6 @@
 
 export function createEmptyRecordConfig(): RecordConfig {
   return {
-    targetOS: 'Q',
     mode: 'STOP_WHEN_FULL',
     durationMs: 10000.0,
     maxFileSizeMb: 100,
@@ -389,6 +396,16 @@
   };
 }
 
+export function getDefaultRecordingTargets(): RecordingTarget[] {
+  return [
+    {os: 'Q', name: 'Android Q+'},
+    {os: 'P', name: 'Android P'},
+    {os: 'O', name: 'Android O-'},
+    {os: 'C', name: 'Chrome'},
+    {os: 'L', name: 'Linux desktop'}
+  ];
+}
+
 export function createEmptyState(): State {
   return {
     route: null,
@@ -443,8 +460,8 @@
     recordingInProgress: false,
     recordingCancelled: false,
     extensionInstalled: false,
-    androidDeviceConnected: undefined,
-    availableDevices: [],
+    recordingTarget: getDefaultRecordingTargets()[0],
+    availableAdbDevices: [],
 
     chromeCategories: undefined,
   };
diff --git a/ui/src/controller/adb_base_controller.ts b/ui/src/controller/adb_base_controller.ts
index 6642c98..7f1afb1 100644
--- a/ui/src/controller/adb_base_controller.ts
+++ b/ui/src/controller/adb_base_controller.ts
@@ -14,6 +14,7 @@
 
 import {extractDurationFromTraceConfig} from '../base/extract_utils';
 import {extractTraceConfig} from '../base/extract_utils';
+import {isAdbTarget} from '../common/state';
 
 import {Adb} from './adb_interfaces';
 import {ReadBuffersResponse} from './consumer_port_types';
@@ -55,8 +56,9 @@
         this.device = await this.findDevice();
         if (!this.device) {
           this.state = AdbAuthState.DISCONNECTED;
+          const target = globals.state.recordingTarget;
           throw Error(`Device with serial ${
-              globals.state.serialAndroidDeviceConnected} not found.`);
+              isAdbTarget(target) ? target.serial : 'n/a'} not found.`);
         }
 
         this.sendStatus(`Please allow USB debugging on device.
@@ -110,9 +112,9 @@
   }
 
   async findDevice(): Promise<USBDevice|undefined> {
-    const deviceConnected = globals.state.androidDeviceConnected;
-    if (!deviceConnected) return undefined;
+    const connectedDevice = globals.state.recordingTarget;
+    if (!isAdbTarget(connectedDevice)) return undefined;
     const devices = await navigator.usb.getDevices();
-    return devices.find(d => d.serialNumber === deviceConnected.serial);
+    return devices.find(d => d.serialNumber === connectedDevice.serial);
   }
 }
\ No newline at end of file
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index dbbed80..acfe936 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -36,11 +36,11 @@
 import {MeminfoCounters, VmstatCounters} from '../common/protos';
 import {
   AdbRecordingTarget,
-  isAndroidTarget,
+  isAdbTarget,
   isChromeTarget,
   MAX_TIME,
   RecordConfig,
-  TargetOs
+  RecordingTarget
 } from '../common/state';
 
 import {AdbOverWebUsb} from './adb';
@@ -630,9 +630,8 @@
   // - Android device target: WebUSB is used to communicate using the adb
   // protocol. Actually, there is no full consumer_port implementation, but
   // only the support to start tracing and fetch the file.
-  async getTargetController(target: TargetOs, device?: AdbRecordingTarget):
-      Promise<RpcConsumerPort> {
-    const identifier = this.getTargetIdentifier(target, device);
+  async getTargetController(target: RecordingTarget): Promise<RpcConsumerPort> {
+    const identifier = this.getTargetIdentifier(target);
 
     // The reason why caching the target 'record controller' Promise is that
     // multiple rcp calls can happen while we are trying to understand if an
@@ -646,16 +645,16 @@
           if (isChromeTarget(target)) {
             controller =
                 new ChromeExtensionConsumerPort(this.extensionPort, this);
-          } else if (isAndroidTarget(target)) {
-            if (!device) throw Error(`No android device connected`);
-
+          } else if (isAdbTarget(target)) {
             this.onStatus(`Please allow USB debugging on device.
                  If you press cancel, reload the page.`);
-            const socketAccess = await this.hasSocketAccess(device);
+            const socketAccess = await this.hasSocketAccess(target);
 
             controller = socketAccess ?
                 new AdbSocketConsumerPort(this.adb, this) :
                 new AdbConsumerPort(this.adb, this);
+          } else {
+            throw Error(`No device connected`);
           }
 
           if (!controller) throw Error(`Unknown target: ${target}`);
@@ -666,9 +665,8 @@
     return controllerPromise;
   }
 
-  private getTargetIdentifier(target: TargetOs, device?: AdbRecordingTarget):
-      string {
-    return device ? device.serial : target;
+  private getTargetIdentifier(target: RecordingTarget): string {
+    return isAdbTarget(target) ? target.serial : target.os;
   }
 
   private async hasSocketAccess(target: AdbRecordingTarget) {
@@ -684,8 +682,7 @@
       _callback: RPCImplCallback) {
     try {
       const state = this.app.state;
-      (await this.getTargetController(
-           state.recordConfig.targetOS, state.androidDeviceConnected))
+      (await this.getTargetController(state.recordingTarget))
           .handleCommand(method.name, requestData);
     } catch (e) {
       console.error(`error invoking ${method}: ${e.message}`);
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 8c93273..7edcbba 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -494,7 +494,9 @@
           left join (select upid, sum(dur) as total_dur
               from sched join thread using(utid)
               group by upid
-            ) using(upid) group by utid, upid
+            ) using(upid)
+        where utid != 0
+        group by utid, upid
         order by total_dur desc, upid, utid`);
 
     const upidToUuid = new Map<number, string>();
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 5d87f7a..47c42e2 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -20,10 +20,12 @@
 import {MeminfoCounters, VmstatCounters} from '../common/protos';
 import {
   AdbRecordingTarget,
+  getDefaultRecordingTargets,
+  isAdbTarget,
   isAndroidTarget,
   isChromeTarget,
   isLinuxTarget,
-  TargetOs
+  RecordingTarget
 } from '../common/state';
 import {MAX_TIME, RecordMode} from '../common/state';
 import {AdbOverWebUsb} from '../controller/adb';
@@ -702,21 +704,20 @@
 function RecordingPlatformSelection() {
   if (globals.state.recordingInProgress) return [];
 
-  const baseTargets = [
-    m('option', {value: 'Q'}, 'Android Q+'),
-    m('option', {value: 'P'}, 'Android P'),
-    m('option', {value: 'O'}, 'Android O-'),
-    m('option', {value: 'C'}, 'Chrome'),
-    m('option', {value: 'L'}, 'Linux desktop')
-  ];
-  const availableAndroidDevices = globals.state.availableDevices;
-  const selected = globals.state.androidDeviceConnected;
-  const choices: m.Children[] =
-      availableAndroidDevices.map(d => m('option', {value: d.serial}, d.name));
+  const availableAndroidDevices = globals.state.availableAdbDevices;
+  const recordingTarget = globals.state.recordingTarget;
 
-  const selectedIndex = selected ? baseTargets.length +
-          availableAndroidDevices.findIndex(d => d.serial === selected.serial) :
-                                   0;
+  const targets = [];
+  for (const {os, name} of getDefaultRecordingTargets()) {
+    targets.push(m('option', {value: os}, name));
+  }
+  for (const d of availableAndroidDevices) {
+    targets.push(m('option', {value: d.serial}, d.name));
+  }
+
+  const selectedIndex = isAdbTarget(recordingTarget) ?
+      targets.findIndex(node => node.attrs.value === recordingTarget.serial) :
+      targets.findIndex(node => node.attrs.value === recordingTarget.os);
 
   return m(
       '.target',
@@ -725,8 +726,7 @@
           'Target platform:',
           m('select',
             {onchange: m.withAttr('value', onTargetChange), selectedIndex},
-            ...baseTargets,
-            ...choices),
+            ...targets),
           ),
       m('.chip',
         {onclick: addAndroidDevice},
@@ -734,16 +734,13 @@
         m('i.material-icons', 'add')));
 }
 
-// Target can be the targetOS or the android serial
+// |target| can be the TargetOs or the android serial.
 function onTargetChange(target: string) {
-  const adbDevice =
-      globals.state.availableDevices.find(d => d.serial === target);
-  globals.dispatch(Actions.setAndroidDevice({target: adbDevice}));
-
-  const traceCfg = produce(globals.state.recordConfig, draft => {
-    draft.targetOS = adbDevice ? adbDevice.os as TargetOs : target as TargetOs;
-  });
-  globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+  const recordingTarget: RecordingTarget =
+      globals.state.availableAdbDevices.find(d => d.serial === target) ||
+      getDefaultRecordingTargets().find(t => t.os === target) ||
+      getDefaultRecordingTargets()[0];
+  globals.dispatch(Actions.setRecordingTarget({target: recordingTarget}));
   globals.rafScheduler.scheduleFullRedraw();
 }
 
@@ -805,7 +802,7 @@
       output directory. `,
         doc);
 
-  switch (globals.state.recordConfig.targetOS) {
+  switch (globals.state.recordingTarget.os) {
     case 'Q':
       break;
     case 'P':
@@ -829,10 +826,10 @@
 }
 
 function RecordingSnippet() {
-  const targetOs = globals.state.recordConfig.targetOS;
+  const target = globals.state.recordingTarget;
 
   // We don't need commands to start tracing on chrome
-  if (isChromeTarget(targetOs)) {
+  if (isChromeTarget(target)) {
     return globals.state.extensionInstalled ?
         m('div',
           m('label',
@@ -840,11 +837,10 @@
          'Start Recording'.`)) :
         [];
   }
-  return m(
-      CodeSnippet, {text: getRecordCommand(targetOs), hardWhitespace: true});
+  return m(CodeSnippet, {text: getRecordCommand(target), hardWhitespace: true});
 }
 
-function getRecordCommand(targetOs: TargetOs) {
+function getRecordCommand(target: RecordingTarget) {
   const data = globals.trackDataStore.get('config') as
           {commandline: string, pbtxt: string} |
       null;
@@ -864,8 +860,7 @@
     cmd += `(sleep 0.5 && adb shell screenrecord --time-limit ${time}`;
     cmd += ' "/sdcard/tracescr.mp4") &\\\n';
   }
-  cmd +=
-      isAndroidTarget(targetOs) ? 'adb shell perfetto \\\n' : 'perfetto \\\n';
+  cmd += isAndroidTarget(target) ? 'adb shell perfetto \\\n' : 'perfetto \\\n';
   cmd += '  -c - --txt \\\n';
   cmd += '  -o /data/misc/perfetto-traces/trace \\\n';
   cmd += '<<EOF\n\n';
@@ -877,7 +872,7 @@
 
 function recordingButtons() {
   const state = globals.state;
-  const realDeviceTarget = state.androidDeviceConnected !== undefined;
+  const target = state.recordingTarget;
   const recInProgress = state.recordingInProgress;
 
   const start =
@@ -899,15 +894,14 @@
 
   const buttons: m.Children = [];
 
-  const targetOs = state.recordConfig.targetOS;
-  if (isAndroidTarget(targetOs)) {
+  if (isAndroidTarget(target)) {
     if (!recInProgress) {
       buttons.push(showCmd);
-      if (realDeviceTarget) buttons.push(start);
+      if (isAdbTarget(target)) buttons.push(start);
     }
-  } else if (isChromeTarget(targetOs) && state.extensionInstalled) {
+  } else if (isChromeTarget(target) && state.extensionInstalled) {
     buttons.push(start);
-  } else if (isLinuxTarget(targetOs)) {
+  } else if (isLinuxTarget(target)) {
     buttons.push(showCmd);
   }
 
@@ -934,8 +928,8 @@
   location.href = '#!/record?p=instructions';
   globals.rafScheduler.scheduleFullRedraw();
 
-  const targetOs = globals.state.recordConfig.targetOS;
-  if (isAndroidTarget(targetOs) || isChromeTarget(targetOs)) {
+  const target = globals.state.recordingTarget;
+  if (isAndroidTarget(target) || isChromeTarget(target)) {
     globals.dispatch(Actions.startRecording({}));
   }
 }
@@ -962,12 +956,19 @@
 // be inserted in the state, and the worker will be able to connect to the
 // correct device.
 async function addAndroidDevice() {
-  const device = await new AdbOverWebUsb().findDevice();
+  let device: USBDevice;
+  try {
+    device = await new AdbOverWebUsb().findDevice();
+  } catch (e) {
+    console.error('No device found: ${e.name}: ${e.message}');
+    return;
+  }
 
   if (!device.serialNumber) {
     console.error('serial number undefined');
     return;
   }
+
   // After the user has selected a device with the chrome UI, it will be
   // available when listing all the available device from WebUSB. Therefore,
   // we update the list of available devices.
@@ -990,7 +991,8 @@
     }
   });
 
-  globals.dispatch(Actions.setAvailableDevices({devices: availableAdbDevices}));
+  globals.dispatch(
+      Actions.setAvailableAdbDevices({devices: availableAdbDevices}));
   selectAndroidDeviceIfAvailable(availableAdbDevices);
   globals.rafScheduler.scheduleFullRedraw();
   return availableAdbDevices;
@@ -998,26 +1000,32 @@
 
 function selectAndroidDeviceIfAvailable(
     availableAdbDevices: AdbRecordingTarget[]) {
-  // If there is an android device attached, but not selected, select it by
-  // default.
-  if (!globals.state.androidDeviceConnected && availableAdbDevices.length) {
+  const recordingTarget = globals.state.recordingTarget;
+  const deviceConnected = isAdbTarget(recordingTarget);
+  const connectedDeviceDisconnected = deviceConnected &&
+      availableAdbDevices.find(
+          e => e.serial === (recordingTarget as AdbRecordingTarget).serial) ===
+          undefined;
+
+  // If there is an android device attached, but not selected (or the currently
+  // selected device was disconnected), select it by default.
+  if ((!deviceConnected || connectedDeviceDisconnected) &&
+      availableAdbDevices.length) {
     globals.dispatch(
-        Actions.setAndroidDevice({target: availableAdbDevices[0]}));
+        Actions.setRecordingTarget({target: availableAdbDevices[0]}));
     return;
   }
 
-  // If a device was selected, but it's not available anymore, reset the
-  // androidConnectedDevice to null.
-  const deviceConnected = globals.state.androidDeviceConnected;
-  if (deviceConnected &&
-      availableAdbDevices.find(e => e.serial === deviceConnected.serial) ===
-          undefined) {
-    globals.dispatch(Actions.setAndroidDevice({target: undefined}));
+  // If the currently selected device was disconnected, reset the recording
+  // target to the default one.
+  if (connectedDeviceDisconnected) {
+    globals.dispatch(
+        Actions.setRecordingTarget({target: getDefaultRecordingTargets()[0]}));
   }
 }
 
 function recordMenu(routePage: string) {
-  const targetOS = globals.state.recordConfig.targetOS;
+  const target = globals.state.recordingTarget;
   const chromeProbe =
       m('a[href="#!/record?p=chrome"]',
         m(`li${routePage === 'chrome' ? '.active' : ''}`,
@@ -1045,7 +1053,7 @@
             m('.title', 'Instructions'),
             m('.sub', 'Generate config and instructions')))),
       m('header', 'Probes'),
-      m('ul', isChromeTarget(targetOS) ? [chromeProbe] : [
+      m('ul', isChromeTarget(target) ? [chromeProbe] : [
         m('a[href="#!/record?p=cpu"]',
           m(`li${routePage === 'cpu' ? '.active' : ''}`,
             m('i.material-icons', 'subtitles'),