Merge "Add canned query for bytes per type."
diff --git a/Android.bp b/Android.bp
index dc329a6..c65df9d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,7 +32,7 @@
     "src/trace_processor/metrics/android/android_startup_cpu.sql",
     "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_callsite_stats.sql",
+    "src/trace_processor/metrics/android/heap_profile_callsites.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",
@@ -1859,7 +1859,7 @@
   srcs: [
     "protos/perfetto/metrics/android/batt_metric.proto",
     "protos/perfetto/metrics/android/cpu_metric.proto",
-    "protos/perfetto/metrics/android/heap_profile_callsite_stats.proto",
+    "protos/perfetto/metrics/android/heap_profile_callsites.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
     "protos/perfetto/metrics/android/mem_metric.proto",
@@ -1878,7 +1878,7 @@
   out: [
     "external/perfetto/protos/perfetto/metrics/android/batt_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.cc",
-    "external/perfetto/protos/perfetto/metrics/android/heap_profile_callsite_stats.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/lmk_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/mem_metric.pbzero.cc",
@@ -1897,7 +1897,7 @@
   srcs: [
     "protos/perfetto/metrics/android/batt_metric.proto",
     "protos/perfetto/metrics/android/cpu_metric.proto",
-    "protos/perfetto/metrics/android/heap_profile_callsite_stats.proto",
+    "protos/perfetto/metrics/android/heap_profile_callsites.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
     "protos/perfetto/metrics/android/lmk_metric.proto",
     "protos/perfetto/metrics/android/mem_metric.proto",
@@ -1916,7 +1916,7 @@
   out: [
     "external/perfetto/protos/perfetto/metrics/android/batt_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/cpu_metric.pbzero.h",
-    "external/perfetto/protos/perfetto/metrics/android/heap_profile_callsite_stats.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/lmk_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/mem_metric.pbzero.h",
@@ -4017,6 +4017,7 @@
     "src/trace_processor/proto_trace_parser.cc",
     "src/trace_processor/proto_trace_tokenizer.cc",
     "src/trace_processor/raw_table.cc",
+    "src/trace_processor/read_trace.cc",
     "src/trace_processor/row_iterators.cc",
     "src/trace_processor/sched_slice_table.cc",
     "src/trace_processor/slice_table.cc",
diff --git a/BUILD b/BUILD
index e560b11..1170b2c 100644
--- a/BUILD
+++ b/BUILD
@@ -339,6 +339,7 @@
     name = "include_perfetto_trace_processor_trace_processor",
     srcs = [
         "include/perfetto/trace_processor/basic_types.h",
+        "include/perfetto/trace_processor/read_trace.h",
         "include/perfetto/trace_processor/status.h",
         "include/perfetto/trace_processor/trace_processor.h",
     ],
@@ -520,7 +521,7 @@
         "src/trace_processor/metrics/android/android_startup_cpu.sql",
         "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_callsite_stats.sql",
+        "src/trace_processor/metrics/android/heap_profile_callsites.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",
@@ -660,6 +661,7 @@
         "src/trace_processor/proto_trace_tokenizer.h",
         "src/trace_processor/raw_table.cc",
         "src/trace_processor/raw_table.h",
+        "src/trace_processor/read_trace.cc",
         "src/trace_processor/row_iterators.cc",
         "src/trace_processor/row_iterators.h",
         "src/trace_processor/sched_slice_table.cc",
@@ -1396,7 +1398,7 @@
     srcs = [
         "protos/perfetto/metrics/android/batt_metric.proto",
         "protos/perfetto/metrics/android/cpu_metric.proto",
-        "protos/perfetto/metrics/android/heap_profile_callsite_stats.proto",
+        "protos/perfetto/metrics/android/heap_profile_callsites.proto",
         "protos/perfetto/metrics/android/ion_metric.proto",
         "protos/perfetto/metrics/android/lmk_metric.proto",
         "protos/perfetto/metrics/android/mem_metric.proto",
diff --git a/gn/standalone/toolchain/BUILD.gn b/gn/standalone/toolchain/BUILD.gn
index 1a428b2..7394802 100644
--- a/gn/standalone/toolchain/BUILD.gn
+++ b/gn/standalone/toolchain/BUILD.gn
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//gn/perfetto.gni")
 import("//gn/standalone/android.gni")
 import("//gn/standalone/wasm.gni")
 import("llvm.gni")
@@ -32,6 +33,7 @@
 # 2. setting is_system_compiler=true in args.gn and the env vars AR/CC/CXX.
 #    This is used by OSSFuzz.
 declare_args() {
+  sysroot = ""
   ar = "ar"
   if (is_linux_host) {
     linker = "gold"
@@ -61,28 +63,28 @@
 
 # Then determine the target toolchain.
 
-# GCC only: determine the cross-toolchain triplet.
 declare_args() {
-  if (is_clang || !is_cross_compiling) {
-    gcc_target_triplet = ""
+  target_sysroot = sysroot
+  if (!is_cross_compiling) {
+    target_triplet = ""
   } else if (target_os == "mac" && target_cpu == "x64") {
-    gcc_target_triplet = "x86_64-apple-darwin"
+    target_triplet = "x86_64-apple-darwin"
   } else if (target_os == "mac" && target_cpu == "x86") {
-    gcc_target_triplet = "i686-apple-darwin"
+    target_triplet = "i686-apple-darwin"
   } else if (target_os == "linux" && target_cpu == "arm64") {
-    gcc_target_triplet = "aarch64-linux-gnu"
+    target_triplet = "aarch64-linux-gnu"
   } else if (target_os == "linux" && target_cpu == "x64") {
-    gcc_target_triplet = "x86_64-linux-gnu"
+    target_triplet = "x86_64-linux-gnu"
   } else if (target_os == "linux" && target_cpu == "x86") {
-    gcc_target_triplet = "i686-linux-gnu"
+    target_triplet = "i686-linux-gnu"
   } else if (target_os == "android" && target_cpu == "arm64") {
-    gcc_target_triplet = "aarch64-linux-android"
+    target_triplet = "aarch64-linux-android"
   } else if (target_os == "android" && target_cpu == "arm") {
-    gcc_target_triplet = "arm-linux-androideabi"
+    target_triplet = "arm-linux-androideabi"
   } else if (target_os == "android" && target_cpu == "x86") {
-    gcc_target_triplet = "i686-linux-android"
+    target_triplet = "i686-linux-android"
   } else if (target_os == "android" && target_cpu == "x86_64") {
-    gcc_target_triplet = "x86_64-linux-android"
+    target_triplet = "x86_64-linux-android"
   } else {
     assert(false,
            "Unsupported cross-compilation for ${target_os}-${target_cpu}")
@@ -90,7 +92,7 @@
 }
 
 declare_args() {
-  if (!is_cross_compiling) {
+  if (!is_cross_compiling || is_perfetto_build_generator) {
     target_ar = ar
     target_cc = cc
     target_cxx = cxx
@@ -106,16 +108,18 @@
       target_ar = "$android_toolchain_root/bin/$android_abi_target-ar"
       target_cc = "$android_llvm_dir/bin/clang"
       target_cxx = "$android_llvm_dir/bin/clang++"
-    } else if (is_clang) {
-      target_cc = linux_clang_bin
-      target_cxx = linux_clangxx_bin
-      target_linker = linux_clang_linker
-    } else {  # GCC
-      assert(gcc_target_triplet != "",
-             "gcc_target_triplet must be non-empty when cross-compiling")
-      target_ar = "${gcc_target_triplet}-ar"
-      target_cc = "${gcc_target_triplet}-gcc"
-      target_cxx = "${gcc_target_triplet}-g++"
+    } else {
+      assert(target_triplet != "",
+             "target_triplet must be non-empty when cross-compiling")
+      if (is_clang) {
+        target_cc = "${linux_clang_bin} --target=${target_triplet}"
+        target_cxx = "${linux_clangxx_bin} --target=${target_triplet}"
+        target_linker = "${linux_clang_linker} --target=${target_triplet}"
+      } else {  # GCC
+        target_ar = "${target_triplet}-ar"
+        target_cc = "${target_triplet}-gcc"
+        target_cxx = "${target_triplet}-g++"
+      }
     }
   }
 }
@@ -128,10 +132,15 @@
     lib_switch = "-l"
     lib_dir_switch = "-L"
     ld_arg = ""
-    if (invoker.linker != "") {
+    if (defined(invoker.linker) && invoker.linker != "") {
       _invoker_linker = invoker.linker
       ld_arg = "-fuse-ld=$_invoker_linker"
     }
+    if (defined(invoker.sysroot) && invoker.sysroot != "") {
+      _invoker_sysroot = invoker.sysroot
+      cc = "$cc --sysroot=$_invoker_sysroot"
+      cxx = "$cxx --sysroot=$_invoker_sysroot"
+    }
 
     tool("cc") {
       depfile = "{{output}}.d"
@@ -229,6 +238,7 @@
   cc = target_cc
   cxx = target_cxx
   linker = target_linker
+  sysroot = target_sysroot
 }
 
 gcc_like_toolchain("gcc_like_host") {
@@ -238,6 +248,7 @@
   cc = cc
   cxx = cxx
   linker = linker
+  sysroot = sysroot
 }
 
 gcc_like_toolchain("wasm") {
@@ -247,5 +258,4 @@
   ar = "$emsdk_dir/emscripten/emar --em-config $em_config"
   cc = "$emsdk_dir/emscripten/emcc --em-config $em_config"
   cxx = "$emsdk_dir/emscripten/em++ --em-config $em_config"
-  linker = ""
 }
diff --git a/include/perfetto/trace_processor/BUILD.gn b/include/perfetto/trace_processor/BUILD.gn
index 90200fa..215be02 100644
--- a/include/perfetto/trace_processor/BUILD.gn
+++ b/include/perfetto/trace_processor/BUILD.gn
@@ -15,6 +15,7 @@
 source_set("trace_processor") {
   sources = [
     "basic_types.h",
+    "read_trace.h",
     "status.h",
     "trace_processor.h",
   ]
diff --git a/include/perfetto/trace_processor/read_trace.h b/include/perfetto/trace_processor/read_trace.h
new file mode 100644
index 0000000..ac2053d
--- /dev/null
+++ b/include/perfetto/trace_processor/read_trace.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACE_PROCESSOR_READ_TRACE_H_
+#define INCLUDE_PERFETTO_TRACE_PROCESSOR_READ_TRACE_H_
+
+#include <functional>
+
+#include "perfetto/base/export.h"
+#include "perfetto/trace_processor/status.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessor;
+
+util::Status PERFETTO_EXPORT ReadTrace(
+    TraceProcessor* tp,
+    const char* filename,
+    const std::function<void(uint64_t parsed_size)>& progress_callback = {});
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACE_PROCESSOR_READ_TRACE_H_
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index a0d48a8..9d1d001 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -18,7 +18,7 @@
   sources = [
     "batt_metric.proto",
     "cpu_metric.proto",
-    "heap_profile_callsite_stats.proto",
+    "heap_profile_callsites.proto",
     "ion_metric.proto",
     "lmk_metric.proto",
     "mem_metric.proto",
diff --git a/protos/perfetto/metrics/android/heap_profile_callsite_stats.proto b/protos/perfetto/metrics/android/heap_profile_callsite_stats.proto
deleted file mode 100644
index 74a836d..0000000
--- a/protos/perfetto/metrics/android/heap_profile_callsite_stats.proto
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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 HeapProfileCallsiteStats {
-  message Frame {
-    optional string name = 1;
-    optional string mapping_name = 2;
-  }
-
-  message Callsite { repeated Frame frame = 1; }
-
-  message CallsiteStats {
-    optional Callsite callsite = 1;
-
-    // Count of objects allocated at this callsite
-    optional int64 total_count = 2;
-    // Count of bytes allocated at this callsite
-    optional int64 total_bytes = 3;
-
-    // Count of allocated objects that were not freed
-    optional int64 delta_count = 4;
-    // Count of allocated bytes that were not freed
-    optional int64 delta_bytes = 5;
-  }
-
-  // Next id: 4
-  message InstanceStats {
-    optional uint32 pid = 3;
-    optional string process_name = 1;
-    repeated CallsiteStats callsite_stats = 2;
-  }
-
-  repeated InstanceStats instance_stats = 1;
-}
diff --git a/protos/perfetto/metrics/android/heap_profile_callsites.proto b/protos/perfetto/metrics/android/heap_profile_callsites.proto
new file mode 100644
index 0000000..ce0bdca
--- /dev/null
+++ b/protos/perfetto/metrics/android/heap_profile_callsites.proto
@@ -0,0 +1,61 @@
+/*
+ * 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 HeapProfileCallsites {
+  message Frame {
+    optional string name = 1;
+    optional string mapping_name = 2;
+  }
+
+  message Counters {
+    // Count of objects allocated
+    optional int64 total_count = 1;
+    // Count of bytes allocated
+    optional int64 total_bytes = 2;
+
+    // Count of allocated objects that were not freed
+    optional int64 delta_count = 3;
+    // Count of allocated bytes that were not freed
+    optional int64 delta_bytes = 4;
+  }
+
+  message Callsite {
+    // The hash unambiguously identifies a callsite in a heap profile (as a
+    // traversal from the root node). It is based on the symbol names (instead
+    // of the addresses).
+    optional int64 hash = 1;
+    optional int64 parent_hash = 2;
+
+    // Leaf frame of the callsite. Use parent_hash to traverse to parent nodes.
+    optional Frame frame = 3;
+
+    optional Counters self_allocs = 4;
+    optional Counters child_allocs = 5;
+  }
+
+  // Callsites per process instance.
+  message InstanceStats {
+    optional uint32 pid = 1;
+    optional string process_name = 2;
+    repeated Callsite callsites = 3;
+  }
+
+  repeated InstanceStats instance_stats = 1;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index 713ae1b..d0c7a1f 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -28,7 +28,7 @@
 import "protos/perfetto/metrics/android/lmk_metric.proto";
 import "protos/perfetto/metrics/android/powrails_metric.proto";
 import "protos/perfetto/metrics/android/startup_metric.proto";
-import "protos/perfetto/metrics/android/heap_profile_callsite_stats.proto";
+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";
 
@@ -47,9 +47,9 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 16
+// Next id: 17
 message TraceMetrics {
-  reserved 4, 13;
+  reserved 4, 13, 14;
 
   // Battery counters metric on Android.
   optional AndroidBatteryMetric android_batt = 5;
@@ -85,7 +85,7 @@
   optional AndroidStartupMetric android_startup = 2;
 
   // Heap profiler callsite statistics.
-  optional HeapProfileCallsiteStats heap_profile_callsite_stats = 14;
+  optional HeapProfileCallsites heap_profile_callsites = 16;
 
   // Trace metadata (applicable to all traces).
   optional TraceMetadata trace_metadata = 3;
diff --git a/protos/perfetto/trace/gpu/gpu_log.proto b/protos/perfetto/trace/gpu/gpu_log.proto
index 7e61139..1c39bc0 100644
--- a/protos/perfetto/trace/gpu/gpu_log.proto
+++ b/protos/perfetto/trace/gpu/gpu_log.proto
@@ -22,12 +22,12 @@
 // Message for logging events GPU data producer.
 message GpuLog {
   enum Severity {
-    SEVERITY_UNSPECIFIED = 0;
-    SEVERITY_VERBOSE = 1;
-    SEVERITY_DEBUG = 2;
-    SEVERITY_INFO = 3;
-    SEVERITY_WARNING = 4;
-    SEVERITY_ERROR = 5;
+    LOG_SEVERITY_UNSPECIFIED = 0;
+    LOG_SEVERITY_VERBOSE = 1;
+    LOG_SEVERITY_DEBUG = 2;
+    LOG_SEVERITY_INFO = 3;
+    LOG_SEVERITY_WARNING = 4;
+    LOG_SEVERITY_ERROR = 5;
   };
   optional Severity severity = 1;
 
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index b27257f..51a7a6a 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -4149,12 +4149,12 @@
 // Message for logging events GPU data producer.
 message GpuLog {
   enum Severity {
-    SEVERITY_UNSPECIFIED = 0;
-    SEVERITY_VERBOSE = 1;
-    SEVERITY_DEBUG = 2;
-    SEVERITY_INFO = 3;
-    SEVERITY_WARNING = 4;
-    SEVERITY_ERROR = 5;
+    LOG_SEVERITY_UNSPECIFIED = 0;
+    LOG_SEVERITY_VERBOSE = 1;
+    LOG_SEVERITY_DEBUG = 2;
+    LOG_SEVERITY_INFO = 3;
+    LOG_SEVERITY_WARNING = 4;
+    LOG_SEVERITY_ERROR = 5;
   };
   optional Severity severity = 1;
 
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6f20e97..fe3ce2a 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -95,8 +95,6 @@
     "heap_profile_tracker.h",
     "instants_table.cc",
     "instants_table.h",
-    "vulkan_memory_tracker.cc",
-    "vulkan_memory_tracker.h",
     "metadata.h",
     "metadata_table.cc",
     "metadata_table.h",
@@ -111,6 +109,7 @@
     "proto_trace_tokenizer.h",
     "raw_table.cc",
     "raw_table.h",
+    "read_trace.cc",
     "row_iterators.cc",
     "row_iterators.h",
     "sched_slice_table.cc",
@@ -167,6 +166,8 @@
     "track_tracker.h",
     "variadic.h",
     "virtual_destructors.cc",
+    "vulkan_memory_tracker.cc",
+    "vulkan_memory_tracker.h",
     "window_operator_table.cc",
     "window_operator_table.h",
   ]
diff --git a/src/trace_processor/heap_graph_tracker.cc b/src/trace_processor/heap_graph_tracker.cc
index c29b60d..1f5bb72 100644
--- a/src/trace_processor/heap_graph_tracker.cc
+++ b/src/trace_processor/heap_graph_tracker.cc
@@ -41,6 +41,11 @@
   interned_type_names_.emplace(intern_id, strid);
 }
 
+void HeapGraphTracker::AddInternedFieldName(uint64_t intern_id,
+                                            StringPool::Id strid) {
+  interned_field_names_.emplace(intern_id, strid);
+}
+
 void HeapGraphTracker::SetPacketIndex(uint64_t index) {
   if (prev_index_ != 0 && prev_index_ + 1 != index) {
     PERFETTO_ELOG("Missing packets between %" PRIu64 " and %" PRIu64,
@@ -61,8 +66,48 @@
     }
     context_->storage->mutable_heap_graph_object_table()->Insert(
         {current_upid_, current_ts_, static_cast<int64_t>(obj.object_id),
-         static_cast<int64_t>(obj.self_size), it->second});
+         static_cast<int64_t>(obj.self_size), -1, it->second});
+    object_id_to_row_.emplace(
+        obj.object_id, context_->storage->heap_graph_object_table().size() - 1);
   }
+
+  for (const SourceObject& obj : current_objects_) {
+    auto it = object_id_to_row_.find(obj.object_id);
+    if (it == object_id_to_row_.end())
+      continue;
+    int64_t owner_row = it->second;
+
+    int64_t reference_set_id =
+        context_->storage->heap_graph_reference_table().size();
+    for (const SourceObject::Reference& ref : obj.references) {
+      // This is true for unset reference fields.
+      if (ref.owned_object_id == 0)
+        continue;
+
+      it = object_id_to_row_.find(ref.owned_object_id);
+      // This can only happen for an invalid type string id, which is already
+      // reported as an error. Silently continue here.
+      if (it == object_id_to_row_.end())
+        continue;
+
+      int64_t owned_row = it->second;
+      auto field_name_it = interned_field_names_.find(obj.type_id);
+      if (field_name_it == interned_field_names_.end()) {
+        context_->storage->IncrementIndexedStats(
+            stats::heap_graph_invalid_string_id,
+            static_cast<int>(current_upid_));
+        continue;
+      }
+      StringPool::Id field_name = field_name_it->second;
+      context_->storage->mutable_heap_graph_reference_table()->Insert(
+          {reference_set_id, owner_row, owned_row, field_name});
+    }
+    context_->storage->mutable_heap_graph_object_table()
+        ->mutable_reference_set_id()
+        ->Set(static_cast<uint32_t>(owner_row), reference_set_id);
+  }
+  interned_field_names_.clear();
+  object_id_to_row_.clear();
   interned_type_names_.clear();
   current_objects_.clear();
   current_upid_ = 0;
diff --git a/src/trace_processor/heap_graph_tracker.h b/src/trace_processor/heap_graph_tracker.h
index 3431fbc..c65a7b1 100644
--- a/src/trace_processor/heap_graph_tracker.h
+++ b/src/trace_processor/heap_graph_tracker.h
@@ -34,15 +34,23 @@
 class HeapGraphTracker {
  public:
   struct SourceObject {
+    // All ids in this are in the trace iid space, not in the trace processor
+    // id space.
+    struct Reference {
+      uint64_t field_name_id = 0;
+      uint64_t owned_object_id = 0;
+    };
     uint64_t object_id = 0;
     uint64_t self_size = 0;
     uint64_t type_id = 0;
+    std::vector<Reference> references;
   };
 
   explicit HeapGraphTracker(TraceProcessorContext* context);
 
   void AddObject(UniquePid upid, int64_t ts, SourceObject obj);
   void AddInternedTypeName(uint64_t intern_id, StringPool::Id strid);
+  void AddInternedFieldName(uint64_t intern_id, StringPool::Id strid);
   void FinalizeProfile();
   void SetPacketIndex(uint64_t index);
 
@@ -53,6 +61,8 @@
   int64_t current_ts_ = 0;
   std::vector<SourceObject> current_objects_;
   std::map<uint64_t, StringPool::Id> interned_type_names_;
+  std::map<uint64_t, StringPool::Id> interned_field_names_;
+  std::map<uint64_t, int64_t> object_id_to_row_;
   uint64_t prev_index_ = 0;
 };
 
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 5ca6401..6e40711 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -31,7 +31,7 @@
   "android/android_startup.sql",
   "android/android_startup_cpu.sql",
   "android/android_package_list.sql",
-  "android/heap_profile_callsite_stats.sql",
+  "android/heap_profile_callsites.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_callsite_stats.sql b/src/trace_processor/metrics/android/heap_profile_callsite_stats.sql
deleted file mode 100644
index f81b3d3..0000000
--- a/src/trace_processor/metrics/android/heap_profile_callsite_stats.sql
+++ /dev/null
@@ -1,124 +0,0 @@
---
--- 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 totals_per_callsite AS
-SELECT upid, callsite_id, SUM(count) AS total_count, SUM(size) AS total_size
-FROM heap_profile_allocation
-WHERE count > 0
-GROUP BY 1, 2;
-
-CREATE VIEW deltas_per_callsite AS
-SELECT upid, callsite_id, SUM(count) AS delta_count, SUM(size) AS delta_size
-FROM heap_profile_allocation
-GROUP BY 1, 2;
-
--- For each callsite ID traverse all frames to the root.
-CREATE TABLE flattened_callsite AS
-WITH RECURSIVE callsite_parser(callsite_id, current_id, position) AS (
-  SELECT id, id, 0 FROM stack_profile_callsite
-  UNION
-  SELECT callsite_id, parent_id, position + 1
-  FROM callsite_parser JOIN stack_profile_callsite ON stack_profile_callsite.id = current_id
-  WHERE stack_profile_callsite.depth > 0
-)
-SELECT *
-FROM callsite_parser;
-
--- Join frames with symbols
-CREATE TABLE symbolized_frame AS
-SELECT
-  spf.id AS id,
-  spf.mapping AS mapping,
-  IFNULL(
-    (SELECT name FROM stack_profile_symbol symbol
-      WHERE symbol.symbol_set_id = spf.symbol_set_id
-      LIMIT 1),
-    spf.name
-  ) AS name
-FROM stack_profile_frame spf;
-
-CREATE UNIQUE INDEX symbolized_frame_idx ON symbolized_frame(id);
-
--- Join with the frames table to get the symbol names.
--- Output order for position matters (as will be the order in the subsequent aggregate operations).
--- We use the cross join to force the join order between virtual and non-virtual tables.
-CREATE VIEW frames_by_callsite_id AS
-SELECT
-  callsite_id,
-  position,
-  HeapProfileCallsiteStats_Frame(
-    'name', spf.name,
-    'mapping_name', stack_profile_mapping.name
-  ) AS frame_proto
-FROM flattened_callsite
-CROSS JOIN stack_profile_callsite
-CROSS JOIN symbolized_frame spf
-CROSS JOIN stack_profile_mapping
-WHERE
-  flattened_callsite.current_id = stack_profile_callsite.id
-  AND stack_profile_callsite.frame_id = spf.id
-  AND spf.mapping = stack_profile_mapping.id
-ORDER BY callsite_id, position;
-
--- Map callsite ID to proto.
-CREATE TABLE callsites_by_id AS
-SELECT
-  callsite_id,
-  HeapProfileCallsiteStats_Callsite('frame', RepeatedField(frame_proto))
-    AS callsite_proto
-FROM frames_by_callsite_id
-GROUP BY callsite_id;
-
-CREATE VIEW callsite_stats AS
-SELECT
-  upid,
-  callsite_proto,
-  totals.total_count,
-  totals.total_size,
-  deltas.delta_count,
-  deltas.delta_size
-FROM totals_per_callsite totals
-JOIN deltas_per_callsite deltas USING (upid, callsite_id)
-JOIN callsites_by_id USING (callsite_id);
-
-CREATE VIEW callsite_stats_proto AS
-SELECT
-  upid,
-  RepeatedField(
-    HeapProfileCallsiteStats_CallsiteStats(
-      'callsite', callsite_proto,
-      'total_count', total_count,
-      'total_bytes', total_size,
-      'delta_count', delta_count,
-      'delta_bytes', delta_size
-    )
-  ) AS repeated_callsite_stats
-FROM callsite_stats
-GROUP BY 1;
-
-CREATE VIEW instance_stats_view AS
-SELECT HeapProfileCallsiteStats_InstanceStats(
-    'pid', process.pid,
-    'process_name', process.name,
-    'callsite_stats', repeated_callsite_stats
-) AS instance_stats_proto
-FROM callsite_stats_proto JOIN process USING (upid);
-
-CREATE VIEW heap_profile_callsite_stats_output AS
-SELECT HeapProfileCallsiteStats(
-  'instance_stats',
-  (SELECT RepeatedField(instance_stats_proto) FROM instance_stats_view)
-);
diff --git a/src/trace_processor/metrics/android/heap_profile_callsites.sql b/src/trace_processor/metrics/android/heap_profile_callsites.sql
new file mode 100644
index 0000000..a0cd0c4
--- /dev/null
+++ b/src/trace_processor/metrics/android/heap_profile_callsites.sql
@@ -0,0 +1,216 @@
+--
+-- 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.
+--
+
+-- Join frames with symbols and mappings to get a textual representation.
+CREATE TABLE symbolized_frame AS
+SELECT
+  frame_id,
+  symbol_name,
+  mapping_name,
+  HASH(symbol_name, mapping_name) frame_hash,
+  HeapProfileCallsites_Frame(
+    'name', symbol_name,
+    'mapping_name', mapping_name
+  ) AS frame_proto
+FROM (
+  SELECT
+    spf.id AS frame_id,
+    IFNULL(
+      (SELECT name FROM stack_profile_symbol symbol
+        WHERE symbol.symbol_set_id = spf.symbol_set_id
+        LIMIT 1),
+      spf.name
+    ) AS symbol_name,
+    spm.name AS mapping_name
+  FROM stack_profile_frame spf
+  JOIN stack_profile_mapping spm
+  ON spf.mapping = spm.id
+);
+
+-- Required to join with callsites
+CREATE UNIQUE INDEX symbolized_frame_idx ON symbolized_frame(frame_id);
+
+-- View that joins callsites with frames. Allocation-agnostic, only used to
+-- generated the hashed callsites.
+CREATE TABLE callsites AS
+SELECT cs.id, cs.parent_id, cs.depth, sf.frame_hash
+FROM stack_profile_callsite cs
+JOIN symbolized_frame sf USING(frame_id);
+
+DROP INDEX symbolized_frame_idx;
+
+-- heapprofd-based callsite ids are based on frame addresses, whereas we want
+-- to group by symbol names.
+-- Create a unique ID for each subtree by traversing from the root.
+-- 1 self_hash can correspond to N callsite_ids (which can then be used to join
+-- with allocs).
+CREATE TABLE hashed_callsites AS
+WITH RECURSIVE callsite_hasher(id, self_hash, parent_hash, frame_hash) AS (
+  SELECT
+    cs.id,
+    cs.frame_hash,
+    -1,
+    cs.frame_hash
+  FROM callsites cs
+  WHERE cs.depth = 0
+  UNION ALL
+  SELECT
+    child.id,
+    HASH(child.frame_hash, parent.self_hash),
+    parent.self_hash,
+    child.frame_hash
+  FROM callsite_hasher parent
+  JOIN callsites child
+  ON parent.id = child.parent_id
+)
+SELECT
+  self_hash,
+  parent_hash,
+  frame_hash,
+  id callsite_id
+FROM callsite_hasher;
+
+DROP TABLE callsites;
+
+CREATE VIEW hashed_callsite_tree AS
+SELECT DISTINCT self_hash, parent_hash, frame_hash
+FROM hashed_callsites;
+
+-- Required to join with allocs
+CREATE INDEX hashed_callsites_id_idx ON hashed_callsites(callsite_id);
+
+-- Computes the allocations for each hash-based callsite.
+CREATE TABLE self_allocs AS
+SELECT
+  hc.self_hash,
+  alloc.upid,
+  SUM(alloc.count) AS delta_count,
+  SUM(CASE WHEN alloc.count > 0 THEN alloc.count ELSE 0 END) AS total_count,
+  SUM(alloc.size) AS delta_bytes,
+  SUM(CASE WHEN alloc.size > 0 THEN alloc.size ELSE 0 END) AS total_bytes
+FROM hashed_callsites hc
+JOIN heap_profile_allocation alloc USING (callsite_id)
+GROUP BY 1, 2;
+
+DROP INDEX hashed_callsites_id_idx;
+
+-- For each allocation (each self_alloc), emit a row for each ancestor and
+-- aggregate them by self_hash.
+CREATE TABLE child_allocs AS
+WITH RECURSIVE parent_traversal(
+  self_hash, parent_hash, upid,
+  delta_count, total_count, delta_bytes, total_bytes) AS (
+  SELECT
+    sa.self_hash,
+    hc.parent_hash,
+    sa.upid,
+    sa.delta_count,
+    sa.total_count,
+    sa.delta_bytes,
+    sa.total_bytes
+  FROM self_allocs sa
+  JOIN hashed_callsite_tree hc ON sa.self_hash = hc.self_hash
+  UNION ALL
+  SELECT
+    parent.self_hash,
+    parent.parent_hash,
+    child.upid,
+    child.delta_count,
+    child.total_count,
+    child.delta_bytes,
+    child.total_bytes
+  FROM parent_traversal child
+  JOIN hashed_callsite_tree parent
+  ON child.parent_hash = parent.self_hash
+)
+SELECT
+  self_hash,
+  upid,
+  SUM(delta_count) AS delta_count,
+  SUM(total_count) AS total_count,
+  SUM(delta_bytes) AS delta_bytes,
+  SUM(total_bytes) AS total_bytes
+FROM parent_traversal
+GROUP BY 1, 2;
+
+CREATE VIEW self_allocs_proto AS
+SELECT
+  self_hash,
+  upid,
+  HeapProfileCallsites_Counters(
+    'delta_count', delta_count, 'total_count', total_count,
+    'delta_bytes', delta_bytes, 'total_bytes', total_bytes
+  ) AS allocs_proto
+FROM self_allocs;
+
+CREATE VIEW child_allocs_proto AS
+SELECT
+  self_hash,
+  upid,
+  HeapProfileCallsites_Counters(
+    'delta_count', delta_count, 'total_count', total_count,
+    'delta_bytes', delta_bytes, 'total_bytes', total_bytes
+  ) AS allocs_proto
+FROM child_allocs;
+
+-- Required to map back to the symbol.
+CREATE INDEX symbolized_frame_hash_idx ON symbolized_frame(frame_hash);
+
+CREATE TABLE process_callsite AS
+SELECT
+  ca.upid,
+  ca.self_hash,
+  tree.parent_hash,
+  frame.frame_proto,
+  sa.allocs_proto AS self_allocs_proto,
+  ca.allocs_proto AS child_allocs_proto
+FROM hashed_callsite_tree tree
+JOIN (SELECT DISTINCT frame_hash, frame_proto FROM symbolized_frame) frame
+  USING (frame_hash)
+JOIN child_allocs_proto ca
+  USING (self_hash)
+LEFT JOIN self_allocs_proto sa
+  USING (self_hash, upid)
+ORDER BY 1, 2;
+
+DROP INDEX symbolized_frame_hash_idx;
+
+CREATE VIEW process_callsite_proto AS
+SELECT
+  upid,
+  RepeatedField(HeapProfileCallsites_Callsite(
+    'hash', self_hash,
+    'parent_hash', parent_hash,
+    'frame', frame_proto,
+    'self_allocs', self_allocs_proto,
+    'child_allocs', child_allocs_proto
+  )) AS repeated_callsite_proto
+FROM process_callsite
+GROUP BY 1;
+
+CREATE VIEW instance_stats_view AS
+SELECT HeapProfileCallsites_InstanceStats(
+    'pid', process.pid,
+    'process_name', process.name,
+    'callsites', repeated_callsite_proto
+) AS instance_stats_proto
+FROM process_callsite_proto JOIN process USING (upid);
+
+CREATE VIEW heap_profile_callsites_output AS
+SELECT HeapProfileCallsites(
+  'instance_stats',
+  (SELECT RepeatedField(instance_stats_proto) FROM instance_stats_view)
+);
diff --git a/src/trace_processor/metrics/metrics.descriptor.h b/src/trace_processor/metrics/metrics.descriptor.h
index 23afda4..f50e3ee 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)
 // 5600f41770c36931db20de64a430f87e1a38fe05
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// e37a486a88d9234259ae74e68ffdc43aa424891c
+// 9410d04aaa3afacc42cdfa881f34ab41f3bff56b
 
 // 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, 9549> kMetricsDescriptor{
+constexpr std::array<uint8_t, 9635> 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,282 +540,289 @@
      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, 0xf5, 0x05, 0x0a, 0x41, 0x70, 0x72,
+     0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0xd7, 0x06, 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, 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, 0x9a, 0x05, 0x0a,
-     0x18, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
-     0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74,
-     0x73, 0x12, 0x5e, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
-     0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x37, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70,
+     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,
      0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73,
-     0x69, 0x74, 0x65, 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, 0x3e, 0x0a, 0x05, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x12,
-     0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61,
-     0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e,
-     0x67, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x51, 0x0a, 0x08, 0x43, 0x61, 0x6c,
-     0x6c, 0x73, 0x69, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x66, 0x72, 0x61,
-     0x6d, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 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, 0x53, 0x74,
-     0x61, 0x74, 0x73, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x1a, 0xe3, 0x01, 0x0a, 0x0d, 0x43, 0x61, 0x6c,
-     0x6c, 0x73, 0x69, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x4e,
-     0x0a, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x69, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74,
+     0x61, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 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, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,
-     0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x52, 0x08, 0x63, 0x61,
-     0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f,
-     0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20,
-     0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f,
-     0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c,
-     0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73,
-     0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x6f,
-     0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64,
-     0x65, 0x6c, 0x74, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a,
-     0x0b, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73,
-     0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x74,
-     0x61, 0x42, 0x79, 0x74, 0x65, 0x73, 0x1a, 0xa4, 0x01, 0x0a, 0x0d, 0x49,
+     0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 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,
-     0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
-     0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72,
-     0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73,
-     0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5e, 0x0a, 0x0e, 0x63, 0x61, 0x6c,
-     0x6c, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18,
-     0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 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, 0x53, 0x74, 0x61, 0x74, 0x73,
-     0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x53, 0x74, 0x61,
-     0x74, 0x73, 0x52, 0x0d, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65,
-     0x53, 0x74, 0x61, 0x74, 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, 0x70, 0x61,
-     0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xc0, 0x01,
-     0x0a, 0x12, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50, 0x61, 0x63,
-     0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x08,
-     0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
+     0x1a, 0x3e, 0x0a, 0x05, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x70,
+     0x70, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+     0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
+     0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x8e, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74,
+     0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75,
+     0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
+     0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12,
+     0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x63, 0x6f, 0x75,
+     0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65,
+     0x6c, 0x74, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b,
+     0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
+     0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x74, 0x61,
+     0x42, 0x79, 0x74, 0x65, 0x73, 0x1a, 0xa6, 0x02, 0x0a, 0x08, 0x43, 0x61,
+     0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61,
+     0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x68, 0x61,
+     0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
+     0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
+     0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12,
+     0x41, 0x0a, 0x05, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
      0x28, 0x0b, 0x32, 0x2b, 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, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52,
-     0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x61, 0x0a,
-     0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c,
-     0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
-     0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b,
-     0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75,
-     0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75, 0x69,
-     0x64, 0x12, 0x21, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
-     0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
-     0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65,
-     0x42, 0x02, 0x48, 0x03, 0x0a, 0x80, 0x02, 0x0a, 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,
+     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, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52,
+     0x05, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x0b, 0x73, 0x65,
+     0x6c, 0x66, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x04, 0x20,
+     0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+     0x65, 0x72, 0x73, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x66, 0x41, 0x6c, 0x6c,
+     0x6f, 0x63, 0x73, 0x12, 0x51, 0x0a, 0x0c, 0x63, 0x68, 0x69, 0x6c, 0x64,
+     0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
+     0x0b, 0x32, 0x2e, 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, 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,
+     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,
+     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, 0x4c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
+     0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 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, 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,
+     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,
+     0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74,
      0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66,
      0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22,
-     0xad, 0x01, 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
-     0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x41,
-     0x0a, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
-     0x28, 0x0b, 0x32, 0x29, 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, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x06, 0x66,
-     0x72, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x54, 0x0a, 0x05, 0x46, 0x72, 0x61,
-     0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-     0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75,
-     0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-     0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 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,
-     0xdb, 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,
+     0xc0, 0x01, 0x0a, 0x12, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50,
+     0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x47,
+     0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 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, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67,
+     0x65, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x1a,
+     0x61, 0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x21,
+     0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61,
+     0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61,
+     0x63, 0x6b, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a,
+     0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03,
+     0x75, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69,
+     0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f,
+     0x64, 0x65, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x80, 0x02, 0x0a, 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, 0x12, 0x0f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 0x22, 0xad, 0x01, 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x79, 0x6d, 0x62,
+     0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73,
+     0x12, 0x41, 0x0a, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01,
+     0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 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, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52,
+     0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x54, 0x0a, 0x05, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75,
+     0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f,
+     0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c,
+     0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+     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, 0xcf, 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, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     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, 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,
+     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, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70,
+     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, 0x6c, 0x6d, 0x6b, 0x5f, 0x6d,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x69, 0x6f, 0x6e, 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,
+     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, 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, 0x41, 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, 0x5f, 0x73, 0x74, 0x61, 0x74,
-     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, 0x22, 0xd2,
-     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,
+     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, 0x22, 0xd2, 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, 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, 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, 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, 0xbf, 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,
+     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, 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,
-     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, 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,
+     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, 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, 0x65, 0x72, 0x66, 0x65,
+     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, 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,
+     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, 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, 0x68, 0x0a, 0x1b, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70,
-     0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73,
-     0x69, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0e, 0x20,
-     0x01, 0x28, 0x0b, 0x32, 0x29, 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, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x18,
+     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, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43,
-     0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 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,
+     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, 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, 0x42, 0x02, 0x48, 0x03}};
+     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}};
 
 }  // namespace perfetto
 
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index 5fbb119..8bc2fc9 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -2730,6 +2730,21 @@
     obj.object_id = object.id();
     obj.self_size = object.self_size();
     obj.type_id = object.type_id();
+    auto ref_field_ids_it = object.reference_field_id();
+    auto ref_object_ids_it = object.reference_object_id();
+    for (; ref_field_ids_it && ref_object_ids_it;
+         ++ref_field_ids_it, ++ref_object_ids_it) {
+      HeapGraphTracker::SourceObject::Reference ref;
+      ref.field_name_id = ref_field_ids_it->as_uint64();
+      ref.owned_object_id = ref_object_ids_it->as_uint64();
+      obj.references.emplace_back(std::move(ref));
+    }
+
+    if (ref_field_ids_it || ref_object_ids_it) {
+      context_->storage->IncrementIndexedStats(stats::heap_graph_missing_packet,
+                                               static_cast<int>(upid));
+      continue;
+    }
     context_->heap_graph_tracker->AddObject(upid, ts, std::move(obj));
   }
   for (auto it = heap_graph.type_names(); it; ++it) {
@@ -2740,6 +2755,14 @@
     context_->heap_graph_tracker->AddInternedTypeName(
         entry.iid(), context_->storage->InternString(str_view));
   }
+  for (auto it = heap_graph.field_names(); it; ++it) {
+    protos::pbzero::InternedString::Decoder entry(it->data(), it->size());
+    const char* str = reinterpret_cast<const char*>(entry.str().data);
+    auto str_view = base::StringView(str, entry.str().size);
+
+    context_->heap_graph_tracker->AddInternedFieldName(
+        entry.iid(), context_->storage->InternString(str_view));
+  }
   if (!heap_graph.continued()) {
     context_->heap_graph_tracker->FinalizeProfile();
   }
diff --git a/src/trace_processor/read_trace.cc b/src/trace_processor/read_trace.cc
new file mode 100644
index 0000000..8ba8f16
--- /dev/null
+++ b/src/trace_processor/read_trace.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/trace_processor/read_trace.h"
+
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/trace_processor/trace_processor.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+#define PERFETTO_HAS_AIO_H() 1
+#else
+#define PERFETTO_HAS_AIO_H() 0
+#endif
+
+#if PERFETTO_HAS_AIO_H()
+#include <aio.h>
+#endif
+
+namespace perfetto {
+namespace trace_processor {
+
+util::Status ReadTrace(
+    TraceProcessor* tp,
+    const char* filename,
+    const std::function<void(uint64_t parsed_size)>& progress_callback) {
+  base::ScopedFile fd(base::OpenFile(filename, O_RDONLY));
+  if (!fd)
+    return util::ErrStatus("Could not open trace file (path: %s)", filename);
+
+  // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
+  constexpr size_t kChunkSize = 1024 * 1024;
+  uint64_t file_size = 0;
+
+#if PERFETTO_HAS_AIO_H()
+  // Load the trace in chunks using async IO. We create a simple pipeline where,
+  // at each iteration, we parse the current chunk and asynchronously start
+  // reading the next chunk.
+  struct aiocb cb {};
+  cb.aio_nbytes = kChunkSize;
+  cb.aio_fildes = *fd;
+
+  std::unique_ptr<uint8_t[]> aio_buf(new uint8_t[kChunkSize]);
+#if defined(MEMORY_SANITIZER)
+  // Just initialize the memory to make the memory sanitizer happy as it
+  // cannot track aio calls below.
+  memset(aio_buf.get(), 0, kChunkSize);
+#endif  // defined(MEMORY_SANITIZER)
+  cb.aio_buf = aio_buf.get();
+
+  PERFETTO_CHECK(aio_read(&cb) == 0);
+  struct aiocb* aio_list[1] = {&cb};
+
+  for (int i = 0;; i++) {
+    if (progress_callback && i % 128 == 0)
+      progress_callback(file_size);
+
+    // Block waiting for the pending read to complete.
+    PERFETTO_CHECK(aio_suspend(aio_list, 1, nullptr) == 0);
+    auto rsize = aio_return(&cb);
+    if (rsize <= 0)
+      break;
+    file_size += static_cast<uint64_t>(rsize);
+
+    // Take ownership of the completed buffer and enqueue a new async read
+    // with a fresh buffer.
+    std::unique_ptr<uint8_t[]> buf(std::move(aio_buf));
+    aio_buf.reset(new uint8_t[kChunkSize]);
+#if defined(MEMORY_SANITIZER)
+    // Just initialize the memory to make the memory sanitizer happy as it
+    // cannot track aio calls below.
+    memset(aio_buf.get(), 0, kChunkSize);
+#endif  // defined(MEMORY_SANITIZER)
+    cb.aio_buf = aio_buf.get();
+    cb.aio_offset += rsize;
+    PERFETTO_CHECK(aio_read(&cb) == 0);
+
+    // Parse the completed buffer while the async read is in-flight.
+    util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
+    if (PERFETTO_UNLIKELY(!status.ok()))
+      return status;
+  }
+#else   // PERFETTO_HAS_AIO_H()
+  // Load the trace in chunks using ordinary read().
+  // This version is used on Windows, since there's no aio library.
+  for (int i = 0;; i++) {
+    if (progress_callback && i % 128 == 0)
+      progress_callback(file_size);
+
+    std::unique_ptr<uint8_t[]> buf(new uint8_t[kChunkSize]);
+    auto rsize = read(*fd, buf.get(), kChunkSize);
+    if (rsize <= 0)
+      break;
+    file_size += static_cast<uint64_t>(rsize);
+
+    util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
+    if (PERFETTO_UNLIKELY(!status.ok()))
+      return status;
+  }
+#endif  // PERFETTO_HAS_AIO_H()
+
+  tp->NotifyEndOfFile();
+
+  if (progress_callback)
+    progress_callback(file_size);
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/stats.h b/src/trace_processor/stats.h
index 8387f37..74eec50 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/stats.h
@@ -110,6 +110,7 @@
   F(json_tokenizer_failure,                   kSingle,  kError,    kTrace),    \
   F(heap_graph_invalid_string_id,             kIndexed, kError,    kTrace),    \
   F(heap_graph_non_finalized_graph,           kSingle,  kError,    kTrace),    \
+  F(heap_graph_malformed_packet,              kIndexed, kError,    kTrace),    \
   F(heap_graph_missing_packet,                kIndexed, kDataLoss, kTrace),    \
   F(heapprofd_buffer_corrupted,               kIndexed, kError,    kTrace),    \
   F(heapprofd_buffer_overran,                 kIndexed, kDataLoss, kTrace),    \
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index bf70637..16133d8 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -40,10 +40,21 @@
   C(int64_t, graph_sample_ts)                              \
   C(int64_t, object_id)                                    \
   C(int64_t, self_size)                                    \
+  C(int64_t, reference_set_id)                             \
   C(StringPool::Id, type_name)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_HEAP_GRAPH_OBJECT_DEF);
 
+#define PERFETTO_TP_HEAP_GRAPH_REFERENCE_DEF(NAME, PARENT, C) \
+  NAME(HeapGraphReferenceTable, "heap_graph_reference")       \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                           \
+  C(int64_t, reference_set_id)                                \
+  C(int64_t, owner_id)                                        \
+  C(int64_t, owned_id)                                        \
+  C(StringPool::Id, field_name)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_HEAP_GRAPH_REFERENCE_DEF);
+
 #define PERFETTO_TP_VULKAN_MEMORY_ALLOCATIONS_DEF(NAME, PARENT, C) \
   NAME(VulkanMemoryAllocationsTable, "vulkan_memory_allocations")  \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                                \
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 2086b74..1492cf7 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -376,6 +376,9 @@
   DbSqliteTable::RegisterTable(*db_, &storage->heap_graph_object_table(),
                                storage->heap_graph_object_table().table_name());
   DbSqliteTable::RegisterTable(
+      *db_, &storage->heap_graph_reference_table(),
+      storage->heap_graph_reference_table().table_name());
+  DbSqliteTable::RegisterTable(
       *db_, &storage->vulkan_memory_allocations_table(),
       storage->vulkan_memory_allocations_table().table_name());
 }
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index f37b5ab..e86f220 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -34,6 +34,7 @@
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/trace_processor/read_trace.h"
 #include "perfetto/trace_processor/trace_processor.h"
 #include "src/trace_processor/metrics/metrics.descriptor.h"
 #include "src/trace_processor/proto_to_json.h"
@@ -46,13 +47,6 @@
 #define PERFETTO_HAS_SIGNAL_H() 0
 #endif
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
-#define PERFETTO_HAS_AIO_H() 1
-#else
-#define PERFETTO_HAS_AIO_H() 0
-#endif
-
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_LINENOISE)
 #include <linenoise.h>
 #include <pwd.h>
@@ -69,10 +63,6 @@
 #include <signal.h>
 #endif
 
-#if PERFETTO_HAS_AIO_H()
-#include <aio.h>
-#endif
-
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #define ftruncate _chsize
 #else
@@ -643,94 +633,6 @@
   bool wide = false;
 };
 
-#if PERFETTO_HAS_AIO_H()
-uint64_t ReadTrace(TraceProcessor* tp, int file_descriptor) {
-  // Load the trace in chunks using async IO. We create a simple pipeline where,
-  // at each iteration, we parse the current chunk and asynchronously start
-  // reading the next chunk.
-
-  // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
-  constexpr size_t kChunkSize = 1024 * 1024;
-  uint64_t file_size = 0;
-
-  struct aiocb cb {};
-  cb.aio_nbytes = kChunkSize;
-  cb.aio_fildes = file_descriptor;
-
-  std::unique_ptr<uint8_t[]> aio_buf(new uint8_t[kChunkSize]);
-#if defined(MEMORY_SANITIZER)
-  // Just initialize the memory to make the memory sanitizer happy as it
-  // cannot track aio calls below.
-  memset(aio_buf.get(), 0, kChunkSize);
-#endif  // defined(MEMORY_SANITIZER)
-  cb.aio_buf = aio_buf.get();
-
-  PERFETTO_CHECK(aio_read(&cb) == 0);
-  struct aiocb* aio_list[1] = {&cb};
-
-  for (int i = 0;; i++) {
-    if (i % 128 == 0)
-      fprintf(stderr, "\rLoading trace: %.2f MB\r", file_size / 1E6);
-
-    // Block waiting for the pending read to complete.
-    PERFETTO_CHECK(aio_suspend(aio_list, 1, nullptr) == 0);
-    auto rsize = aio_return(&cb);
-    if (rsize <= 0)
-      break;
-    file_size += static_cast<uint64_t>(rsize);
-
-    // Take ownership of the completed buffer and enqueue a new async read
-    // with a fresh buffer.
-    std::unique_ptr<uint8_t[]> buf(std::move(aio_buf));
-    aio_buf.reset(new uint8_t[kChunkSize]);
-#if defined(MEMORY_SANITIZER)
-    // Just initialize the memory to make the memory sanitizer happy as it
-    // cannot track aio calls below.
-    memset(aio_buf.get(), 0, kChunkSize);
-#endif  // defined(MEMORY_SANITIZER)
-    cb.aio_buf = aio_buf.get();
-    cb.aio_offset += rsize;
-    PERFETTO_CHECK(aio_read(&cb) == 0);
-
-    // Parse the completed buffer while the async read is in-flight.
-    util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
-    if (PERFETTO_UNLIKELY(!status.ok())) {
-      PERFETTO_ELOG("Fatal error while parsing trace: %s", status.c_message());
-      exit(1);
-    }
-  }
-  tp->NotifyEndOfFile();
-  return file_size;
-}
-#else   // PERFETTO_HAS_AIO_H()
-uint64_t ReadTrace(TraceProcessor* tp, int file_descriptor) {
-  // Load the trace in chunks using ordinary read().
-  // This version is used on Windows, since there's no aio library.
-
-  constexpr size_t kChunkSize = 1024 * 1024;
-  uint64_t file_size = 0;
-
-  for (int i = 0;; i++) {
-    if (i % 128 == 0)
-      fprintf(stderr, "\rLoading trace: %.2f MB\r", file_size / 1E6);
-
-    std::unique_ptr<uint8_t[]> buf(new uint8_t[kChunkSize]);
-    auto rsize = read(file_descriptor, buf.get(), kChunkSize);
-    if (rsize <= 0)
-      break;
-    file_size += static_cast<uint64_t>(rsize);
-
-    util::Status status = tp->Parse(std::move(buf), static_cast<size_t>(rsize));
-    if (PERFETTO_UNLIKELY(!status.ok())) {
-      PERFETTO_ELOG("Fatal error while parsing trace: %s", status.c_message());
-      exit(1);
-    }
-  }
-  tp->NotifyEndOfFile();
-  return file_size;
-}
-#endif  // PERFETTO_HAS_AIO_H()
-
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 void PrintUsage(char** argv) {
   PERFETTO_ELOG(
@@ -955,18 +857,22 @@
   // Load the trace file into the trace processor.
   Config config;
   std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
-  base::ScopedFile fd(base::OpenFile(options.trace_file_path, O_RDONLY));
-  if (!fd) {
-    PERFETTO_ELOG("Could not open trace file (path: %s)",
-                  options.trace_file_path.c_str());
-    return 1;
-  }
 
   auto t_load_start = base::GetWallTimeNs();
-  uint64_t file_size = ReadTrace(tp.get(), *fd);
+  double size_mb = 0;
+  util::Status read_status =
+      ReadTrace(tp.get(), options.trace_file_path.c_str(),
+                [&size_mb](size_t parsed_size) {
+                  size_mb = parsed_size / 1E6;
+                  fprintf(stderr, "\rLoading trace: %.2f MB\r", size_mb);
+                });
+  if (!read_status.ok()) {
+    PERFETTO_ELOG("Could not read trace file (path: %s): %s",
+                  options.trace_file_path.c_str(), read_status.c_message());
+    return 1;
+  }
   auto t_load = base::GetWallTimeNs() - t_load_start;
   double t_load_s = t_load.count() / 1E9;
-  double size_mb = file_size / 1E6;
   PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
                 size_mb / t_load_s);
   g_tp = tp.get();
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 536fcd5..2f6f77e 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -1284,6 +1284,14 @@
     return &heap_graph_object_table_;
   }
 
+  const tables::HeapGraphReferenceTable& heap_graph_reference_table() const {
+    return heap_graph_reference_table_;
+  }
+
+  tables::HeapGraphReferenceTable* mutable_heap_graph_reference_table() {
+    return &heap_graph_reference_table_;
+  }
+
   const tables::GpuTrackTable& gpu_track_table() const {
     return gpu_track_table_;
   }
@@ -1405,6 +1413,8 @@
   // Symbol tables (mappings from frames to symbol names)
   tables::SymbolTable symbol_table_{&string_pool_, nullptr};
   tables::HeapGraphObjectTable heap_graph_object_table_{&string_pool_, nullptr};
+  tables::HeapGraphReferenceTable heap_graph_reference_table_{&string_pool_,
+                                                              nullptr};
 
   tables::VulkanMemoryAllocationsTable vulkan_memory_allocations_table_{
       &string_pool_, nullptr};
diff --git a/test/metrics/heap_profile.textproto b/test/metrics/heap_profile.textproto
index 5e91061..2d83244 100644
--- a/test/metrics/heap_profile.textproto
+++ b/test/metrics/heap_profile.textproto
@@ -54,12 +54,23 @@
       mapping_id: 1
       rel_pc: 0x3000
     }
+    frames {
+      iid: 4
+      function_name_id: 2
+      mapping_id: 1
+      rel_pc: 0x4000
+    }
     callstacks {
       iid: 1
       frame_ids: 1
       frame_ids: 2
       frame_ids: 3
     }
+    callstacks {
+      iid: 2
+      frame_ids: 1
+      frame_ids: 4
+    }
     mappings {
       iid: 1
       path_string_ids: 4
@@ -74,6 +85,13 @@
         alloc_count: 2
         free_count: 1
       }
+      samples {
+        callstack_id: 2
+        self_allocated: 100
+        self_freed: 10
+        alloc_count: 10
+        free_count: 1
+      }
     }
   }
 }
diff --git a/test/metrics/heap_profile_callsite_stats.out b/test/metrics/heap_profile_callsite_stats.out
deleted file mode 100644
index d923353..0000000
--- a/test/metrics/heap_profile_callsite_stats.out
+++ /dev/null
@@ -1,26 +0,0 @@
-heap_profile_callsite_stats {
-  instance_stats {
-    process_name: "system_server"
-    callsite_stats {
-      callsite {
-        frame {
-          name: "symbolized f3"
-          mapping_name: "/liblib.so"
-        }
-        frame {
-          name: "f2"
-          mapping_name: "/liblib.so"
-        }
-        frame {
-          name: "f1"
-          mapping_name: "/liblib.so"
-        }
-      }
-      total_count: 2
-      total_bytes: 2000
-      delta_count: 1
-      delta_bytes: 1000
-    }
-    pid: 2
-  }
-}
diff --git a/test/metrics/heap_profile_callsites.out b/test/metrics/heap_profile_callsites.out
new file mode 100644
index 0000000..3ca8b70
--- /dev/null
+++ b/test/metrics/heap_profile_callsites.out
@@ -0,0 +1,60 @@
+heap_profile_callsites {
+  instance_stats {
+    pid: 2
+    process_name: "system_server"
+    callsites {
+      hash: -3761178652842017503
+      parent_hash: 5412623030728177384
+      frame {
+        name: "symbolized f3"
+        mapping_name: "/liblib.so"
+      }
+      self_allocs {
+        total_count: 2
+        total_bytes: 2000
+        delta_count: 1
+        delta_bytes: 1000
+      }
+      child_allocs {
+        total_count: 2
+        total_bytes: 2000
+        delta_count: 1
+        delta_bytes: 1000
+      }
+    }
+    callsites {
+      hash: 5412623030728177384
+      parent_hash: 6947621464292123521
+      frame {
+        name: "f2"
+        mapping_name: "/liblib.so"
+      }
+      self_allocs {
+        total_count: 10
+        total_bytes: 100
+        delta_count: 9
+        delta_bytes: 90
+      }
+      child_allocs {
+        total_count: 12
+        total_bytes: 2100
+        delta_count: 10
+        delta_bytes: 1090
+      }
+    }
+    callsites {
+      hash: 6947621464292123521
+      parent_hash: -1
+      frame {
+        name: "f1"
+        mapping_name: "/liblib.so"
+      }
+      child_allocs {
+        total_count: 12
+        total_bytes: 2100
+        delta_count: 10
+        delta_bytes: 1090
+      }
+    }
+  }
+}
diff --git a/test/metrics/index b/test/metrics/index
index 4e0c3de..a159f21 100644
--- a/test/metrics/index
+++ b/test/metrics/index
@@ -18,8 +18,8 @@
 
 android_package_list.py android_package_list android_package_list.out
 
-heap_profile.textproto heap_profile_callsite_stats heap_profile_callsite_stats.out
+heap_profile.textproto heap_profile_callsites heap_profile_callsites.out
 heap_profile_no_symbols.textproto unsymbolized_frames unsymbolized_frames.out
 
 # Json output
-../data/memory_counters.pb trace_metadata trace_metadata.json.out
\ No newline at end of file
+../data/memory_counters.pb trace_metadata trace_metadata.json.out
diff --git a/tools/gen_amalgamated b/tools/gen_amalgamated
index 9113a5e..e7892b5 100755
--- a/tools/gen_amalgamated
+++ b/tools/gen_amalgamated
@@ -548,10 +548,10 @@
   if args.check:
     output = os.path.join(tempfile.mkdtemp(), 'perfetto_amalgamated')
 
+  out = gn_utils.prepare_out_directory(args.gn_args, 'tmp.gen_amalgamated')
+  if not args.quiet:
+    print('Building project...')
   try:
-    if not args.quiet:
-      print('Building project...')
-    out = gn_utils.prepare_out_directory(args.gn_args, 'tmp.gen_amalgamated')
     desc = gn_utils.load_build_description(out)
 
     # We need to build everything first so that the necessary header
diff --git a/tools/gn_utils.py b/tools/gn_utils.py
index 1494279..450f8ee 100644
--- a/tools/gn_utils.py
+++ b/tools/gn_utils.py
@@ -39,10 +39,10 @@
     print(
         'Command "{}" failed in {}:'.format(' '.join(cmd), cwd),
         file=sys.stderr)
-    print(e.output, file=sys.stderr)
+    print(e.output.decode(), file=sys.stderr)
     sys.exit(1)
   else:
-    return output
+    return output.decode()
 
 
 def repo_root():
diff --git a/tools/test_gen_amalgamated.py b/tools/test_gen_amalgamated.py
index c4b7813..a5354c6 100755
--- a/tools/test_gen_amalgamated.py
+++ b/tools/test_gen_amalgamated.py
@@ -20,6 +20,15 @@
 
 from compat import quote
 
+GN_ARGS = ' '.join(
+    quote(s) for s in (
+        'is_debug=false',
+        'is_perfetto_build_generator=true',
+        'is_perfetto_embedder=true',
+        'use_custom_libcxx=false',
+        'enable_perfetto_ipc=true',
+    ))
+
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
 
 
@@ -28,7 +37,7 @@
   command = [path] + list(args)
   print('Running:', ' '.join(quote(c) for c in command))
   try:
-    return subprocess.check_output(command, cwd=ROOT_DIR)
+    return subprocess.check_output(command, cwd=ROOT_DIR).decode()
   except subprocess.CalledProcessError as e:
     assert False, 'Command: {} failed'.format(' '.join(command))
 
@@ -40,9 +49,9 @@
 def check_amalgamated_dependencies():
   os_deps = {}
   for os_name in ['android', 'linux', 'mac']:
-    os_deps[os_name] = call('gen_amalgamated', '--gn_args',
-                            'target_os="%s"' % os_name, '--dump-deps',
-                            '--quiet').split('\n')
+    gn_args = (' target_os="%s"' % os_name) + GN_ARGS
+    os_deps[os_name] = call('gen_amalgamated', '--gn_args', gn_args,
+                            '--dump-deps', '--quiet').split('\n')
   for os_name, deps in os_deps.items():
     for dep in deps:
       for other_os, other_deps in os_deps.items():
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index eef61b0..66651f8 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "tools/trace_to_text/symbolize_profile.h"
 #include "tools/trace_to_text/trace_to_json.h"
 #include "tools/trace_to_text/trace_to_profile.h"
@@ -38,16 +39,34 @@
 
 int Usage(const char* argv0) {
   printf(
-      "Usage: %s systrace|json|ctrace|text|profile [--truncate start|end] "
+      "Usage: %s systrace|json|ctrace|text|profile [--pid PID] "
+      "[--timestamps TIMESTAMP1,TIMESTAMP2,...] "
+      "[--truncate start|end] "
       "[trace.pb] "
-      "[trace.txt]\n",
+      "[trace.txt]\n"
+      "\nProfile mode only:\n"
+      "\t--timestamps TIMESTAMP1,TIMESTAMP2,... generate profiles "
+      "only for these timestamps\n"
+      "\t--pid PID generate profiles only for this process id\n",
       argv0);
   return 1;
 }
 
+uint64_t StringToUint64OrDie(const char* str) {
+  char* end;
+  uint64_t number = static_cast<uint64_t>(strtoll(str, &end, 10));
+  if (*end != '\0') {
+    PERFETTO_ELOG("Invalid %s. Expected decimal integer.", str);
+    exit(1);
+  }
+  return number;
+}
+
 int Main(int argc, char** argv) {
   std::vector<const char*> positional_args;
   Keep truncate_keep = Keep::kAll;
+  uint64_t pid = 0;
+  std::vector<uint64_t> timestamps;
   for (int i = 1; i < argc; i++) {
     if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
       printf("%s\n", PERFETTO_GET_GIT_REVISION());
@@ -65,6 +84,15 @@
             "start of the trace.");
         return Usage(argv[0]);
       }
+    } else if (i <= argc && strcmp(argv[i], "--pid") == 0) {
+      i++;
+      pid = StringToUint64OrDie(argv[i]);
+    } else if (i <= argc && strcmp(argv[i], "--timestamps") == 0) {
+      i++;
+      std::vector<std::string> ts_strings = base::SplitString(argv[i], ",");
+      for (const std::string& ts : ts_strings) {
+        timestamps.emplace_back(StringToUint64OrDie(ts.c_str()));
+      }
     } else {
       positional_args.push_back(argv[i]);
     }
@@ -105,6 +133,12 @@
 
   std::string format(positional_args[0]);
 
+  if (format != "profile" && (pid != 0 || !timestamps.empty())) {
+    PERFETTO_ELOG(
+        "--pid and --timestamps are supported only for profile format.");
+    return 1;
+  }
+
   if (format == "json")
     return TraceToJson(input_stream, output_stream, /*compress=*/false,
                        truncate_keep);
@@ -127,7 +161,7 @@
     return TraceToText(input_stream, output_stream);
 
   if (format == "profile")
-    return TraceToProfile(input_stream, output_stream);
+    return TraceToProfile(input_stream, output_stream, pid, timestamps);
 
   if (format == "symbolize")
     return SymbolizeProfile(input_stream, output_stream);
diff --git a/ui/PRESUBMIT.py b/ui/PRESUBMIT.py
index eecdfeb..a4d6df0 100644
--- a/ui/PRESUBMIT.py
+++ b/ui/PRESUBMIT.py
@@ -53,6 +53,9 @@
     return [
         output_api.PresubmitError("""\
 There were tslint errors. You may be able to fix some of them using
-$ {} {} --project {} --fix""".format(relpath(node), relpath(tslint), ui_path))
+$ {} {} --project {} --fix
+
+If this is unexpected: did you remember to do a UI build before running the
+presubmit?""".format(relpath(node), relpath(tslint), ui_path))
     ]
   return []
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index 64758b6..ab590ef 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -43,7 +43,7 @@
   if (down && '?' === key) {
     toggleHelp();
   }
-  if (down && 'Enter' === key) {
+  if (down && 'enter' === key) {
     e.preventDefault();
     executeSearch(e.shiftKey);
   }
diff --git a/ui/src/frontend/pan_and_zoom_handler.ts b/ui/src/frontend/pan_and_zoom_handler.ts
index 7401eb6..e771abb 100644
--- a/ui/src/frontend/pan_and_zoom_handler.ts
+++ b/ui/src/frontend/pan_and_zoom_handler.ts
@@ -43,6 +43,10 @@
 const HORIZONTAL_WHEEL_PAN_SPEED = 1;
 const WHEEL_ZOOM_SPEED = -0.02;
 
+const EDITING_RANGE_CURSOR = 'ew-resize';
+const SHIFT_CURSOR = 'text';
+const DEFAULT_CURSOR = 'default';
+
 enum Pan {
   None = 0,
   Left = -1,
@@ -91,20 +95,28 @@
   private contentOffsetX: number;
   private onPanned: (movedPx: number) => void;
   private onZoomed: (zoomPositionPx: number, zoomRatio: number) => void;
-  private onDragSelect: (selectStartPx: number, selectEndPx: number) => void;
+  private shouldDrag: (currentPx: number) => boolean;
+  private onDrag:
+      (dragStartPx: number, prevPx: number, currentPx: number,
+       editing: boolean) => void;
 
-  constructor({element, contentOffsetX, onPanned, onZoomed, onDragSelect}: {
-    element: HTMLElement,
-    contentOffsetX: number,
-    onPanned: (movedPx: number) => void,
-    onZoomed: (zoomPositionPx: number, zoomRatio: number) => void,
-    onDragSelect: (selectStartPx: number, selectEndPx: number) => void
-  }) {
+  constructor(
+      {element, contentOffsetX, onPanned, onZoomed, shouldDrag, onDrag}: {
+        element: HTMLElement,
+        contentOffsetX: number,
+        onPanned: (movedPx: number) => void,
+        onZoomed: (zoomPositionPx: number, zoomRatio: number) => void,
+        shouldDrag: (currentPx: number) => boolean,
+        onDrag:
+            (dragStartPx: number, prevPx: number, currentPx: number,
+             editing: boolean) => void,
+      }) {
     this.element = element;
     this.contentOffsetX = contentOffsetX;
     this.onPanned = onPanned;
     this.onZoomed = onZoomed;
-    this.onDragSelect = onDragSelect;
+    this.shouldDrag = shouldDrag;
+    this.onDrag = onDrag;
 
     document.body.addEventListener('keydown', this.boundOnKeyDown);
     document.body.addEventListener('keyup', this.boundOnKeyUp);
@@ -112,23 +124,40 @@
     this.element.addEventListener('wheel', this.boundOnWheel, {passive: true});
 
     let lastX = -1;
-    new DragGestureHandler(this.element, x => {
-      if (this.shiftDown && this.dragStartPx !== -1) {
-        this.onDragSelect(this.dragStartPx, x);
-      } else {
-        this.onPanned(lastX - x);
-      }
-      lastX = x;
-    }, x => {
-      lastX = x;
-      if (this.shiftDown) {
-        this.dragStartPx = x;
-      }
-    }, () => {
-      this.dragStartPx = -1;
-    });
+    let drag = false;
+    new DragGestureHandler(
+        this.element,
+        x => {
+          // If we started our drag on a time range boundary or shift is down
+          // then we are drag selecting rather than panning.
+          if (drag || this.shiftDown) {
+            this.onDrag(this.dragStartPx, lastX, x, !this.shiftDown);
+          } else {
+            this.onPanned(lastX - x);
+          }
+          lastX = x;
+        },
+        x => {
+          lastX = x;
+          this.dragStartPx = x;
+          drag = this.shouldDrag(x);
+          // Set the cursor style based on where the cursor is when the drag
+          // starts.
+          if (drag) {
+            this.element.style.cursor = EDITING_RANGE_CURSOR;
+          } else if (this.shiftDown) {
+            this.element.style.cursor = SHIFT_CURSOR;
+          }
+        },
+        () => {
+          // Reset the cursor now the drag has ended.
+          this.element.style.cursor =
+              this.shiftDown ? SHIFT_CURSOR : DEFAULT_CURSOR;
+          this.dragStartPx = -1;
+        });
   }
 
+
   shutdown() {
     document.body.removeEventListener('keydown', this.boundOnKeyDown);
     document.body.removeEventListener('keyup', this.boundOnKeyUp);
@@ -176,10 +205,21 @@
         globals.frontendLocalState.sidebarVisible ? this.contentOffsetX : 0;
     // We can't use layerX here because there are many layers in this element.
     this.mousePositionX = e.clientX - pageOffset;
+    // Only change the cursor when hovering, the DragGestureHandler handles
+    // changing the cursor during drag events. This avoids the problem of
+    // the cursor flickering between styles if you drag fast and get too
+    // far from the current time range.
+    if (e.buttons === 0) {
+      if (!this.shouldDrag(this.mousePositionX)) {
+        this.element.style.cursor =
+            this.shiftDown ? SHIFT_CURSOR : DEFAULT_CURSOR;
+      } else {
+        this.element.style.cursor = EDITING_RANGE_CURSOR;
+      }
+    }
     if (this.shiftDown) {
       const pos = this.mousePositionX - TRACK_SHELL_WIDTH;
-      const ts =
-        globals.frontendLocalState.timeScale.pxToTime(pos);
+      const ts = globals.frontendLocalState.timeScale.pxToTime(pos);
       globals.frontendLocalState.setHoveredTimestamp(ts);
     }
   }
@@ -235,19 +275,20 @@
     handleKey(e, false);
   }
 
+  // TODO(taylori): Move this shift handling into the viewer page.
   private updateShift(down: boolean) {
     if (down === this.shiftDown) return;
     this.shiftDown = down;
     if (this.shiftDown) {
       if (this.mousePositionX) {
-        this.element.style.cursor = 'text';
+        this.element.style.cursor = SHIFT_CURSOR;
         const pos = this.mousePositionX - TRACK_SHELL_WIDTH;
         const ts = globals.frontendLocalState.timeScale.pxToTime(pos);
         globals.frontendLocalState.setHoveredTimestamp(ts);
       }
     } else {
       globals.frontendLocalState.setHoveredTimestamp(-1);
-      this.element.style.cursor = 'default';
+      this.element.style.cursor = DEFAULT_CURSOR;
     }
 
     globals.frontendLocalState.setShowTimeSelectPreview(this.shiftDown);
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index a1beb3b..9bea6254 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -179,6 +179,26 @@
   }
 }
 
+// Checks if the mousePos is within 3px of the start or end of the
+// current selected time range.
+function onTimeRangeBoundary(mousePos: number): 'START'|'END'|null {
+  const startSec = globals.frontendLocalState.selectedTimeRange.startSec;
+  const endSec = globals.frontendLocalState.selectedTimeRange.endSec;
+  if (startSec !== undefined && endSec !== undefined) {
+    const start = globals.frontendLocalState.timeScale.timeToPx(startSec);
+    const end = globals.frontendLocalState.timeScale.timeToPx(endSec);
+    const startDrag = mousePos - TRACK_SHELL_WIDTH;
+    const startDistance = Math.abs(start - startDrag);
+    const endDistance = Math.abs(end - startDrag);
+    const range = 3 * window.devicePixelRatio;
+    // We might be within 3px of both boundaries but we should choose
+    // the closest one.
+    if (startDistance < range && startDistance <= endDistance) return 'START';
+    if (endDistance < range && endDistance <= startDistance) return 'END';
+  }
+  return null;
+}
+
 /**
  * Top-most level component for the viewer page. Holds tracks, brush timeline,
  * panels, and everything else that's part of the main trace viewer page.
@@ -245,19 +265,39 @@
         frontendLocalState.updateVisibleTime(newSpan);
         globals.rafScheduler.scheduleRedraw();
       },
-      onDragSelect: (selectStartPx: number|null, selectEndPx: number) => {
-        if (!selectStartPx) return;
-        this.keepCurrentSelection = true;
-        globals.frontendLocalState.setShowTimeSelectPreview(false);
+      shouldDrag: (currentPx: number) => {
+        return onTimeRangeBoundary(currentPx) !== null;
+      },
+      onDrag: (
+          dragStartPx: number,
+          prevPx: number,
+          currentPx: number,
+          editing: boolean) => {
         const traceTime = globals.state.traceTime;
         const scale = frontendLocalState.timeScale;
-        const startPx = Math.min(selectStartPx, selectEndPx);
-        const endPx = Math.max(selectStartPx, selectEndPx);
-        const startTs = Math.max(traceTime.startSec,
-                               scale.pxToTime(startPx - TRACK_SHELL_WIDTH));
-        const endTs = Math.min(traceTime.endSec,
-                               scale.pxToTime(endPx - TRACK_SHELL_WIDTH));
-        globals.frontendLocalState.selectTimeRange(startTs, endTs);
+        this.keepCurrentSelection = true;
+        if (editing) {
+          const startSec = frontendLocalState.selectedTimeRange.startSec;
+          const endSec = frontendLocalState.selectedTimeRange.endSec;
+          if (startSec !== undefined && endSec !== undefined) {
+            const newTime = scale.pxToTime(currentPx - TRACK_SHELL_WIDTH);
+            // Have to check again for when one boundary crosses over the other.
+            const curBoundary = onTimeRangeBoundary(prevPx);
+            if (curBoundary == null) return;
+            const keepTime = curBoundary === 'START' ? endSec : startSec;
+            frontendLocalState.selectTimeRange(
+                Math.max(Math.min(keepTime, newTime), traceTime.startSec),
+                Math.min(Math.max(keepTime, newTime), traceTime.endSec));
+          }
+        } else {
+          frontendLocalState.setShowTimeSelectPreview(false);
+          const dragStartTime = scale.pxToTime(dragStartPx - TRACK_SHELL_WIDTH);
+          const dragEndTime = scale.pxToTime(currentPx - TRACK_SHELL_WIDTH);
+          frontendLocalState.selectTimeRange(
+              Math.max(
+                  Math.min(dragStartTime, dragEndTime), traceTime.startSec),
+              Math.min(Math.max(dragStartTime, dragEndTime), traceTime.endSec));
+        }
         globals.rafScheduler.scheduleRedraw();
       }
     });
diff --git a/ui/src/tracks/cpu_slices/frontend.ts b/ui/src/tracks/cpu_slices/frontend.ts
index 40b91fa..e670291 100644
--- a/ui/src/tracks/cpu_slices/frontend.ts
+++ b/ui/src/tracks/cpu_slices/frontend.ts
@@ -306,7 +306,7 @@
     const time = timeScale.pxToTime(x);
     const index = search(data.starts, time);
     const id = index === -1 ? undefined : data.ids[index];
-    if (!id) return false;
+    if (!id || this.utidHoveredInThisTrack === -1) return false;
     globals.makeSelection(Actions.selectSlice({id}));
     return true;
   }