Merge "ui/extension: Record using Chrome's browser DevTools target"
diff --git a/Android.bp b/Android.bp
index c29530e..5ed362b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,6 +40,7 @@
     "src/trace_processor/metrics/android/process_oom_score.sql",
     "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
     "src/trace_processor/metrics/android/span_view_stats.sql",
+    "src/trace_processor/metrics/android/unmapped_java_symbols.sql",
     "src/trace_processor/metrics/android/unsymbolized_frames.sql",
     "src/trace_processor/metrics/android/upid_span_view.sql",
     "src/trace_processor/metrics/trace_metadata.sql",
@@ -2589,6 +2590,7 @@
     "protos/perfetto/metrics/android/powrails_metric.proto",
     "protos/perfetto/metrics/android/process_metadata.proto",
     "protos/perfetto/metrics/android/startup_metric.proto",
+    "protos/perfetto/metrics/android/unmapped_java_symbols.proto",
     "protos/perfetto/metrics/android/unsymbolized_frames.proto",
   ],
   tools: [
@@ -2610,6 +2612,7 @@
     "external/perfetto/protos/perfetto/metrics/android/powrails_metric.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/process_metadata.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/startup_metric.pbzero.cc",
+    "external/perfetto/protos/perfetto/metrics/android/unmapped_java_symbols.pbzero.cc",
     "external/perfetto/protos/perfetto/metrics/android/unsymbolized_frames.pbzero.cc",
   ],
 }
@@ -2631,6 +2634,7 @@
     "protos/perfetto/metrics/android/powrails_metric.proto",
     "protos/perfetto/metrics/android/process_metadata.proto",
     "protos/perfetto/metrics/android/startup_metric.proto",
+    "protos/perfetto/metrics/android/unmapped_java_symbols.proto",
     "protos/perfetto/metrics/android/unsymbolized_frames.proto",
   ],
   tools: [
@@ -2652,6 +2656,7 @@
     "external/perfetto/protos/perfetto/metrics/android/powrails_metric.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/process_metadata.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/startup_metric.pbzero.h",
+    "external/perfetto/protos/perfetto/metrics/android/unmapped_java_symbols.pbzero.h",
     "external/perfetto/protos/perfetto/metrics/android/unsymbolized_frames.pbzero.h",
   ],
   export_include_dirs: [
@@ -5724,7 +5729,6 @@
     "src/trace_processor/args_table.cc",
     "src/trace_processor/filtered_row_index.cc",
     "src/trace_processor/gfp_flags.cc",
-    "src/trace_processor/metadata_table.cc",
     "src/trace_processor/process_table.cc",
     "src/trace_processor/raw_table.cc",
     "src/trace_processor/read_trace.cc",
@@ -5841,6 +5845,7 @@
     "src/trace_processor/importers/proto/track_event_module.cc",
     "src/trace_processor/importers/proto/track_event_parser.cc",
     "src/trace_processor/importers/proto/track_event_tokenizer.cc",
+    "src/trace_processor/metadata_tracker.cc",
     "src/trace_processor/process_tracker.cc",
     "src/trace_processor/slice_tracker.cc",
     "src/trace_processor/stack_profile_tracker.cc",
@@ -5883,7 +5888,6 @@
     "src/trace_processor/importers/proto/heap_graph_walker_unittest.cc",
     "src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
     "src/trace_processor/importers/systrace/systrace_parser_unittest.cc",
-    "src/trace_processor/metadata_table_unittest.cc",
     "src/trace_processor/process_table_unittest.cc",
     "src/trace_processor/process_tracker_unittest.cc",
     "src/trace_processor/protozero_to_text_unittests.cc",
diff --git a/BUILD b/BUILD
index 5699b8b..5303bde 100644
--- a/BUILD
+++ b/BUILD
@@ -689,6 +689,7 @@
         "src/trace_processor/metrics/android/process_oom_score.sql",
         "src/trace_processor/metrics/android/process_unagg_mem_view.sql",
         "src/trace_processor/metrics/android/span_view_stats.sql",
+        "src/trace_processor/metrics/android/unmapped_java_symbols.sql",
         "src/trace_processor/metrics/android/unsymbolized_frames.sql",
         "src/trace_processor/metrics/android/upid_span_view.sql",
         "src/trace_processor/metrics/trace_metadata.sql",
@@ -755,6 +756,7 @@
         "src/trace_processor/tables/counter_tables.h",
         "src/trace_processor/tables/macros.h",
         "src/trace_processor/tables/macros_internal.h",
+        "src/trace_processor/tables/metadata_tables.h",
         "src/trace_processor/tables/profiler_tables.h",
         "src/trace_processor/tables/slice_tables.h",
         "src/trace_processor/tables/track_tables.h",
@@ -789,8 +791,6 @@
         "src/trace_processor/filtered_row_index.h",
         "src/trace_processor/gfp_flags.cc",
         "src/trace_processor/gfp_flags.h",
-        "src/trace_processor/metadata_table.cc",
-        "src/trace_processor/metadata_table.h",
         "src/trace_processor/process_table.cc",
         "src/trace_processor/process_table.h",
         "src/trace_processor/raw_table.cc",
@@ -930,6 +930,8 @@
         "src/trace_processor/importers/proto/track_event_tokenizer.cc",
         "src/trace_processor/importers/proto/track_event_tokenizer.h",
         "src/trace_processor/metadata.h",
+        "src/trace_processor/metadata_tracker.cc",
+        "src/trace_processor/metadata_tracker.h",
         "src/trace_processor/process_tracker.cc",
         "src/trace_processor/process_tracker.h",
         "src/trace_processor/slice_tracker.cc",
@@ -1775,6 +1777,7 @@
         "protos/perfetto/metrics/android/powrails_metric.proto",
         "protos/perfetto/metrics/android/process_metadata.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
+        "protos/perfetto/metrics/android/unmapped_java_symbols.proto",
         "protos/perfetto/metrics/android/unsymbolized_frames.proto",
     ],
     visibility = [
diff --git a/include/README.md b/include/README.md
index 83a92f7..6edb3ca 100644
--- a/include/README.md
+++ b/include/README.md
@@ -87,8 +87,6 @@
     void OnStop(const StopArgs&) override {}
   };
   ...
-  PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(MyDataSource);
-  ...
   perfetto::DataSourceDescriptor dsd;
   dsd.set_name("my_data_source");
   MyDataSource::Register(dsd);
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index 923e176..e283737 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -369,8 +369,6 @@
   // Setup/Start/Stop notifications and makes the Trace() method work when
   // tracing is enabled and the data source is selected.
   // This must be called after Tracing::Initialize().
-  // The caller must also use the DEFINE_DATA_SOURCE_STATIC_MEMBERS() macro
-  // documented below.
   // Can return false to signal failure if attemping to register more than
   // kMaxDataSources (32) data sources types.
   static bool Register(const DataSourceDescriptor& descriptor) {
@@ -459,15 +457,8 @@
 // TODO(skyostil): Remove this macro.
 #define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(...)
 
-// The API client must use this in a translation unit. This is because it needs
-// to instantiate the static storage for the datasource to allow the fastpath
-// enabled check.
-#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...)        \
-  template <>                                                  \
-  perfetto::internal::DataSourceStaticState                    \
-      perfetto::DataSource<__VA_ARGS__>::static_state_{};      \
-  template <>                                                  \
-  thread_local perfetto::internal::DataSourceThreadLocalState* \
-      perfetto::DataSource<__VA_ARGS__>::tls_state_ = nullptr
+// Not needed -- only here for backwards compatibility.
+// TODO(skyostil): Remove this macro.
+#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...)
 
 #endif  // INCLUDE_PERFETTO_TRACING_DATA_SOURCE_H_
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 05f5b80..5f7e2b4 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -29,6 +29,7 @@
     "powrails_metric.proto",
     "process_metadata.proto",
     "startup_metric.proto",
+    "unmapped_java_symbols.proto",
     "unsymbolized_frames.proto",
   ]
 }
diff --git a/protos/perfetto/metrics/android/unmapped_java_symbols.proto b/protos/perfetto/metrics/android/unmapped_java_symbols.proto
new file mode 100644
index 0000000..53266ca
--- /dev/null
+++ b/protos/perfetto/metrics/android/unmapped_java_symbols.proto
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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;
+
+import "protos/perfetto/metrics/android/process_metadata.proto";
+
+message UnmappedJavaSymbols {
+  message ProcessSymbols {
+    optional AndroidProcessMetadata process_metadata = 1;
+    repeated string type_name = 2;
+    repeated string field_name = 3;
+  }
+
+  repeated ProcessSymbols process_symbols = 1;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index bcb5e93..c17be13 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -30,6 +30,7 @@
 import "protos/perfetto/metrics/android/startup_metric.proto";
 import "protos/perfetto/metrics/android/heap_profile_callsites.proto";
 import "protos/perfetto/metrics/android/package_list.proto";
+import "protos/perfetto/metrics/android/unmapped_java_symbols.proto";
 import "protos/perfetto/metrics/android/unsymbolized_frames.proto";
 import "protos/perfetto/metrics/android/java_heap_stats.proto";
 
@@ -50,7 +51,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 19
+// Next id: 20
 message TraceMetrics {
   reserved 4, 10, 13, 14;
 
@@ -99,6 +100,9 @@
   // Metrics used to find potential culprits of low-memory kills.
   optional AndroidLmkReasonMetric android_lmk_reason = 18;
 
+  // Java type names that have no deobfuscation mappings.
+  optional UnmappedJavaSymbols unmapped_java_symbols = 19;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index fc45d52..3221b5c 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -420,6 +420,20 @@
 
 // End of protos/perfetto/metrics/android/package_list.proto
 
+// Begin of protos/perfetto/metrics/android/unmapped_java_symbols.proto
+
+message UnmappedJavaSymbols {
+  message ProcessSymbols {
+    optional AndroidProcessMetadata process_metadata = 1;
+    repeated string type_name = 2;
+    repeated string field_name = 3;
+  }
+
+  repeated ProcessSymbols process_symbols = 1;
+}
+
+// End of protos/perfetto/metrics/android/unmapped_java_symbols.proto
+
 // Begin of protos/perfetto/metrics/android/unsymbolized_frames.proto
 
 message UnsymbolizedFrames {
@@ -475,7 +489,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 19
+// Next id: 20
 message TraceMetrics {
   reserved 4, 10, 13, 14;
 
@@ -524,6 +538,9 @@
   // Metrics used to find potential culprits of low-memory kills.
   optional AndroidLmkReasonMetric android_lmk_reason = 18;
 
+  // Java type names that have no deobfuscation mappings.
+  optional UnmappedJavaSymbols unmapped_java_symbols = 19;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index b794b92..c999174 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -4072,6 +4072,75 @@
 
 // End of protos/perfetto/trace/track_event/chrome_legacy_ipc.proto
 
+// Begin of protos/perfetto/trace/track_event/chrome_process_descriptor.proto
+
+// Describes the attributes for a Chrome process. Must be paired with a
+// ProcessDescriptor in the same TrackDescriptor.
+//
+// Next id: 4.
+message ChromeProcessDescriptor {
+  // See chromium's content::ProcessType.
+  enum ChromeProcessType {
+    PROCESS_UNSPECIFIED = 0;
+    PROCESS_BROWSER = 1;
+    PROCESS_RENDERER = 2;
+    PROCESS_UTILITY = 3;
+    PROCESS_ZYGOTE = 4;
+    PROCESS_SANDBOX_HELPER = 5;
+    PROCESS_GPU = 6;
+    PROCESS_PPAPI_PLUGIN = 7;
+    PROCESS_PPAPI_BROKER = 8;
+  }
+  optional ChromeProcessType process_type = 1;
+  optional int32 process_priority = 2;
+
+  // To support old UI. New UI should determine default sorting by process_type.
+  optional int32 legacy_sort_index = 3;
+}
+
+// End of protos/perfetto/trace/track_event/chrome_process_descriptor.proto
+
+// Begin of protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
+
+// Describes a Chrome thread's attributes. Emitted as part of a TrackDescriptor,
+// usually by the thread's trace writer. Must be paired with a ThreadDescriptor
+// in the same TrackDescriptor.
+//
+// Next id: 3.
+message ChromeThreadDescriptor {
+  enum ThreadType {
+    THREAD_UNSPECIFIED = 0;
+
+    THREAD_MAIN = 1;
+    THREAD_IO = 2;
+
+    // Scheduler:
+    THREAD_POOL_BG_WORKER = 3;
+    THREAD_POOL_FG_WORKER = 4;
+    THREAD_POOL_FB_BLOCKING = 5;
+    THREAD_POOL_BG_BLOCKING = 6;
+    THREAD_POOL_SERVICE = 7;
+
+    // Compositor:
+    THREAD_COMPOSITOR = 8;
+    THREAD_VIZ_COMPOSITOR = 9;
+    THREAD_COMPOSITOR_WORKER = 10;
+
+    // Renderer:
+    THREAD_SERVICE_WORKER = 11;
+
+    // Tracing related threads:
+    THREAD_MEMORY_INFRA = 50;
+    THREAD_SAMPLING_PROFILER = 51;
+  };
+  optional ThreadType thread_type = 1;
+
+  // To support old UI. New UI should determine default sorting by thread_type.
+  optional int32 legacy_sort_index = 2;
+}
+
+// End of protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
+
 // Begin of protos/perfetto/trace/track_event/chrome_user_event.proto
 
 // Details about a UI interaction initiated by the user, such as opening or
@@ -4165,11 +4234,17 @@
 // Describes a process's attributes. Emitted as part of a TrackDescriptor,
 // usually by the process's main thread.
 //
-// Next id: 5.
+// Next id: 6.
 message ProcessDescriptor {
   optional int32 pid = 1;
   repeated string cmdline = 2;
 
+  optional int32 process_priority = 5;
+
+  // ---------------------------------------------------------------------------
+  // Deprecated / legacy fields, which will be removed in the future:
+  // ---------------------------------------------------------------------------
+
   // See chromium's content::ProcessType.
   enum ChromeProcessType {
     PROCESS_UNSPECIFIED = 0;
@@ -4183,7 +4258,6 @@
     PROCESS_PPAPI_BROKER = 8;
   }
   optional ChromeProcessType chrome_process_type = 4;
-  optional int32 process_priority = 5;
 
   // To support old UI. New UI should determine default sorting by process_type.
   optional int32 legacy_sort_index = 3;
@@ -4229,6 +4303,10 @@
 
   optional string thread_name = 5;
 
+  // ---------------------------------------------------------------------------
+  // Deprecated / legacy fields, which will be removed in the future:
+  // ---------------------------------------------------------------------------
+
   enum ChromeThreadType {
     CHROME_THREAD_UNSPECIFIED = 0;
 
@@ -4256,10 +4334,6 @@
   };
   optional ChromeThreadType chrome_thread_type = 4;
 
-  // ---------------------------------------------------------------------------
-  // Deprecated / legacy fields, which will be removed in the future:
-  // ---------------------------------------------------------------------------
-
   // Deprecated. Use ClockSnapshot in combination with TracePacket's timestamp
   // and timestamp_clock_id fields instead.
   optional int64 reference_timestamp_us = 6;
@@ -4280,7 +4354,9 @@
 // Begin of protos/perfetto/trace/track_event/track_descriptor.proto
 
 // Defines a track for TrackEvents. Slices and instant events on the same track
-// will be nested based on their timestamps, see TrackEvent::Type.
+// will be nested based on their timestamps, see TrackEvent::Type. Additionally
+// events on tracks which represent the same thread (i.e., matching pid and tid
+// in ThreadDescriptor) will be merged onto one sequential timeline.
 //
 // A TrackDescriptor only needs to be emitted by one trace writer / producer and
 // is valid for the entirety of the trace. To ensure the descriptor isn't lost
@@ -4292,7 +4368,7 @@
 // |TrackEvent::track_uuid|. It is possible but not necessary to emit a
 // TrackDescriptor for this implicit track.
 //
-// Next id: 1.
+// Next id: 8.
 message TrackDescriptor {
   // Unique ID that identifies this track. This ID is global to the whole trace.
   // Producers should ensure that it is unlikely to clash with IDs emitted by
@@ -4302,16 +4378,20 @@
   // event id + id_scope, pid, and/or tid to compute this ID.
   optional uint64 uuid = 1;
 
-  // TODO(eseckler): Support track hierarchies.
-  // uint64 parent_uuid = X;
+  // A parent track reference can be used to describe relationships between
+  // tracks. For example, to define an asynchronous track which is scoped to a
+  // specific process, specify the uuid for that process's process track here.
+  optional uint64 parent_uuid = 5;
 
   // Name of the track.
   optional string name = 2;
 
-  // Associate the track with a process or thread - the UI will draw this track
-  // in the context of the process/thread.
+  // Associate the track with a process or thread - the UI will merge all tracks
+  // for the same process / thread into a single timeline view.
   optional ProcessDescriptor process = 3;
+  optional ChromeProcessDescriptor chrome_process = 6;
   optional ThreadDescriptor thread = 4;
+  optional ChromeThreadDescriptor chrome_thread = 7;
 }
 
 // End of protos/perfetto/trace/track_event/track_descriptor.proto
diff --git a/src/protozero/test/example_proto/test_messages.descriptor.h b/src/protozero/test/example_proto/test_messages.descriptor.h
index f26c058..9c2bcfa 100644
--- a/src/protozero/test/example_proto/test_messages.descriptor.h
+++ b/src/protozero/test/example_proto/test_messages.descriptor.h
@@ -224,19 +224,19 @@
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x42,
      0x61, 0x7a, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, 0x61,
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x6f, 0x6f, 0x4d,
-     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x4d, 0x6f,
+     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 0x6f,
      0x6f, 0x4d, 0x6f, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x52, 0x4c, 0x45,
      0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x0a, 0x55, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+     0x52, 0x0a, 0x75, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
      0x12, 0x12, 0x0a, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
+     0x28, 0x08, 0x52, 0x04, 0x78, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
      0x55, 0x72, 0x4c, 0x45, 0x5f, 0x6e, 0x63, 0x6f, 0x5f, 0x5f, 0x64, 0x65,
-     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x72, 0x4c,
+     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x72, 0x4c,
      0x45, 0x4e, 0x63, 0x6f, 0x44, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x09, 0x5f,
      0x5f, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x07, 0x42, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
+     0x28, 0x08, 0x52, 0x07, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
      0x0e, 0x0a, 0x02, 0x55, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x02, 0x55, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
+     0x02, 0x75, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
      0x69, 0x67, 0x5f, 0x5f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
      0x62, 0x61, 0x6e, 0x67, 0x42, 0x69, 0x67, 0x22, 0x8f, 0x01, 0x0a, 0x14,
      0x50, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74,
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6a05dfa..a6fd002 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -112,6 +112,8 @@
     "importers/proto/track_event_tokenizer.cc",
     "importers/proto/track_event_tokenizer.h",
     "metadata.h",
+    "metadata_tracker.cc",
+    "metadata_tracker.h",
     "process_tracker.cc",
     "process_tracker.h",
     "slice_tracker.cc",
@@ -286,8 +288,6 @@
     "filtered_row_index.h",
     "gfp_flags.cc",
     "gfp_flags.h",
-    "metadata_table.cc",
-    "metadata_table.h",
     "process_table.cc",
     "process_table.h",
     "raw_table.cc",
@@ -379,7 +379,6 @@
     "importers/proto/heap_graph_walker_unittest.cc",
     "importers/proto/proto_trace_parser_unittest.cc",
     "importers/systrace/systrace_parser_unittest.cc",
-    "metadata_table_unittest.cc",
     "process_table_unittest.cc",
     "process_tracker_unittest.cc",
     "protozero_to_text_unittests.cc",
diff --git a/src/trace_processor/args_tracker.cc b/src/trace_processor/args_tracker.cc
index cfd9f83..4f143c5 100644
--- a/src/trace_processor/args_tracker.cc
+++ b/src/trace_processor/args_tracker.cc
@@ -87,8 +87,8 @@
         break;
       // Special case: overwrites the metadata table row.
       case TableId::kMetadataTable:
-        storage->mutable_metadata()->OverwriteMetadata(
-            row, Variadic::Integer(set_id));
+        storage->mutable_metadata_table()->mutable_int_value()->Set(row,
+                                                                    set_id);
         break;
       case TableId::kTrack:
         storage->mutable_track_table()->mutable_source_arg_set_id()->Set(
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index 93e9d01..ee12912 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -309,6 +309,8 @@
     PERFETTO_FATAL("Invalid type");
   }
 
+  const StringPool& string_pool() const { return *string_pool_; }
+
  private:
   enum class ColumnType {
     // Standard primitive types.
diff --git a/src/trace_processor/db/typed_column.h b/src/trace_processor/db/typed_column.h
index 99355d7..a7183f6 100644
--- a/src/trace_processor/db/typed_column.h
+++ b/src/trace_processor/db/typed_column.h
@@ -62,6 +62,11 @@
   // Inserts the value at the end of the column.
   void Append(T v) { mutable_sparse_vector<T>()->Append(v); }
 
+  // Returns the row containing the given value in the Column.
+  base::Optional<uint32_t> IndexOf(T v) const {
+    return Column::IndexOf(NumericToSqlValue(v));
+  }
+
   // Implements equality between two items of type |T|.
   static bool Equals(T a, T b) {
     // We need to use equal_to here as it could be T == double and because we
@@ -147,6 +152,16 @@
     mutable_sparse_vector<StringPool::Id>()->Append(v);
   }
 
+  // Returns the row containing the given value in the Column.
+  base::Optional<uint32_t> IndexOf(StringPool::Id v) const {
+    return Column::IndexOf(SqlValue::String(string_pool().Get(v).c_str()));
+  }
+
+  // Returns the row containing the given value in the Column.
+  base::Optional<uint32_t> IndexOf(NullTermStringView v) const {
+    return Column::IndexOf(SqlValue::String(v.c_str()));
+  }
+
   // Implements equality between two items of type |T|.
   static bool Equals(StringPool::Id a, StringPool::Id b) { return a == b; }
 
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index e10da4e..4aa62b9 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -952,64 +952,69 @@
 
 util::Status ExportMetadata(const TraceStorage* storage,
                             TraceFormatWriter* writer) {
-  const auto& trace_metadata = storage->metadata();
-  const auto& keys = trace_metadata.keys();
-  const auto& values = trace_metadata.values();
-  for (size_t pos = 0; pos < keys.size(); pos++) {
+  const auto& trace_metadata = storage->metadata_table();
+  const auto& keys = trace_metadata.name();
+  const auto& int_values = trace_metadata.int_value();
+  const auto& str_values = trace_metadata.str_value();
+
+  // Create a mapping from key string ids to keys.
+  std::unordered_map<StringId, metadata::KeyIDs> key_map;
+  for (uint32_t i = 0; i < metadata::kNumKeys; ++i) {
+    auto id = *storage->string_pool().GetId(metadata::kNames[i]);
+    key_map[id] = static_cast<metadata::KeyIDs>(i);
+  }
+
+  for (uint32_t pos = 0; pos < trace_metadata.row_count(); pos++) {
     // Cast away from enum type, as otherwise -Wswitch-enum will demand an
     // exhaustive list of cases, even if there's a default case.
-    switch (static_cast<size_t>(keys[pos])) {
+    metadata::KeyIDs key = key_map[keys[pos]];
+    switch (static_cast<size_t>(key)) {
       case metadata::benchmark_description:
         writer->AppendTelemetryMetadataString(
             "benchmarkDescriptions",
-            GetNonNullString(storage, values[pos].string_value));
+            GetNonNullString(storage, str_values[pos]));
         break;
 
       case metadata::benchmark_name:
         writer->AppendTelemetryMetadataString(
-            "benchmarks", GetNonNullString(storage, values[pos].string_value));
+            "benchmarks", GetNonNullString(storage, str_values[pos]));
         break;
 
       case metadata::benchmark_start_time_us:
 
         writer->SetTelemetryMetadataTimestamp("benchmarkStart",
-                                              values[pos].int_value);
+                                              *int_values[pos]);
         break;
 
       case metadata::benchmark_had_failures:
-        if (pos < values.size())
-          writer->AppendTelemetryMetadataBool("hadFailures",
-                                              values[pos].int_value);
+        writer->AppendTelemetryMetadataBool("hadFailures", *int_values[pos]);
         break;
 
       case metadata::benchmark_label:
         writer->AppendTelemetryMetadataString(
-            "labels", GetNonNullString(storage, values[pos].string_value));
+            "labels", GetNonNullString(storage, str_values[pos]));
         break;
 
       case metadata::benchmark_story_name:
         writer->AppendTelemetryMetadataString(
-            "stories", GetNonNullString(storage, values[pos].string_value));
+            "stories", GetNonNullString(storage, str_values[pos]));
         break;
 
       case metadata::benchmark_story_run_index:
-        writer->AppendTelemetryMetadataInt("storysetRepeats",
-                                           values[pos].int_value);
+        writer->AppendTelemetryMetadataInt("storysetRepeats", *int_values[pos]);
         break;
 
       case metadata::benchmark_story_run_time_us:
-        writer->SetTelemetryMetadataTimestamp("traceStart",
-                                              values[pos].int_value);
+        writer->SetTelemetryMetadataTimestamp("traceStart", *int_values[pos]);
         break;
 
       case metadata::benchmark_story_tags:  // repeated
         writer->AppendTelemetryMetadataString(
-            "storyTags", GetNonNullString(storage, values[pos].string_value));
+            "storyTags", GetNonNullString(storage, str_values[pos]));
         break;
 
       default:
-        PERFETTO_DLOG("Ignoring metadata key %zu",
-                      static_cast<size_t>(keys[pos]));
+        PERFETTO_DLOG("Ignoring metadata key %zu", static_cast<size_t>(key));
         break;
     }
   }
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index cfcb6f0..7cac7cd 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -26,6 +26,7 @@
 
 #include "perfetto/ext/base/temp_file.h"
 #include "src/trace_processor/args_tracker.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/track_tracker.h"
 
@@ -67,6 +68,7 @@
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.storage.reset(new TraceStorage());
     context_.track_tracker.reset(new TrackTracker(&context_));
+    context_.metadata_tracker.reset(new MetadataTracker(&context_));
   }
 
   std::string ToJson(ArgumentFilterPredicate argument_filter = nullptr,
@@ -254,17 +256,20 @@
   StringId desc_id =
       context_.storage->InternString(base::StringView(kDescription));
   Variadic description = Variadic::String(desc_id);
-  context_.storage->SetMetadata(metadata::benchmark_description, description);
+  context_.metadata_tracker->SetMetadata(metadata::benchmark_description,
+                                         description);
 
   StringId benchmark_name_id =
       context_.storage->InternString(base::StringView(kBenchmarkName));
   Variadic benchmark_name = Variadic::String(benchmark_name_id);
-  context_.storage->SetMetadata(metadata::benchmark_name, benchmark_name);
+  context_.metadata_tracker->SetMetadata(metadata::benchmark_name,
+                                         benchmark_name);
 
   StringId story_name_id =
       context_.storage->InternString(base::StringView(kStoryName));
   Variadic story_name = Variadic::String(story_name_id);
-  context_.storage->SetMetadata(metadata::benchmark_story_name, story_name);
+  context_.metadata_tracker->SetMetadata(metadata::benchmark_story_name,
+                                         story_name);
 
   StringId tag1_id =
       context_.storage->InternString(base::StringView(kStoryTag1));
@@ -272,19 +277,22 @@
       context_.storage->InternString(base::StringView(kStoryTag2));
   Variadic tag1 = Variadic::String(tag1_id);
   Variadic tag2 = Variadic::String(tag2_id);
-  context_.storage->AppendMetadata(metadata::benchmark_story_tags, tag1);
-  context_.storage->AppendMetadata(metadata::benchmark_story_tags, tag2);
+  context_.metadata_tracker->AppendMetadata(metadata::benchmark_story_tags,
+                                            tag1);
+  context_.metadata_tracker->AppendMetadata(metadata::benchmark_story_tags,
+                                            tag2);
 
   Variadic benchmark_start = Variadic::Integer(kBenchmarkStart);
-  context_.storage->SetMetadata(metadata::benchmark_start_time_us,
-                                benchmark_start);
+  context_.metadata_tracker->SetMetadata(metadata::benchmark_start_time_us,
+                                         benchmark_start);
 
   Variadic story_start = Variadic::Integer(kStoryStart);
-  context_.storage->SetMetadata(metadata::benchmark_story_run_time_us,
-                                story_start);
+  context_.metadata_tracker->SetMetadata(metadata::benchmark_story_run_time_us,
+                                         story_start);
 
   Variadic had_failures = Variadic::Integer(kHadFailures);
-  context_.storage->SetMetadata(metadata::benchmark_had_failures, had_failures);
+  context_.metadata_tracker->SetMetadata(metadata::benchmark_had_failures,
+                                         had_failures);
 
   base::TempFile temp_file = base::TempFile::Create();
   FILE* output = fopen(temp_file.path().c_str(), "w+");
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 5370e57..178be15 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -21,6 +21,7 @@
 #include "src/trace_processor/args_tracker.h"
 #include "src/trace_processor/clock_tracker.h"
 #include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/syscall_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
@@ -207,7 +208,7 @@
   protos::pbzero::TraceConfig::StatsdMetadata::Decoder metadata(blob.data,
                                                                 blob.size);
   if (metadata.has_triggering_subscription_id()) {
-    context_->storage->SetMetadata(
+    context_->metadata_tracker->SetMetadata(
         metadata::statsd_triggering_subscription_id,
         Variadic::Integer(metadata.triggering_subscription_id()));
   }
@@ -226,9 +227,9 @@
   for (auto it = pkg_list.packages(); it; ++it) {
     // Insert a placeholder metadata entry, which will be overwritten by the
     // arg_set_id when the arg tracker is flushed.
-    uint32_t row = context_->storage->AppendMetadata(
+    auto id = context_->metadata_tracker->AppendMetadata(
         metadata::android_packages_list, Variadic::Integer(0));
-
+    uint32_t row = *context_->storage->metadata_table().id().IndexOf(id);
     auto add_arg = [this, row](base::StringView name, Variadic value) {
       StringId key_id = context_->storage->InternString(name);
       context_->args_tracker->AddArg(TableId::kMetadataTable, row, key_id,
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.cc b/src/trace_processor/importers/proto/heap_graph_tracker.cc
index 0f9ae9a..01dbf02 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.cc
@@ -197,38 +197,45 @@
   uint32_t mapping_idx = *mapping_table->id().IndexOf(mapping_id);
 
   auto paths = sequence_state.walker.FindPathsFromRoot();
-  for (const auto& p : paths.children)
-    WriteFlamegraph(sequence_state, p.second, -1, 0, mapping_idx);
+  WriteFlamegraph(sequence_state, paths, mapping_idx);
 
   sequence_state_.erase(seq_id);
 }
 
 void HeapGraphTracker::WriteFlamegraph(
     const SequenceState& sequence_state,
-    const HeapGraphWalker::PathFromRoot& path,
-    int32_t parent_id,
-    uint32_t depth,
+    const HeapGraphWalker::PathFromRoot& init_path,
     uint32_t mapping_row) {
-  tables::StackProfileFrameTable::Row row{};
-  row.name = StringId(static_cast<uint32_t>(path.class_name));
-  row.mapping = mapping_row;
+  std::vector<int32_t> node_to_row_id(init_path.nodes.size());
 
-  auto id = context_->storage->mutable_stack_profile_frame_table()->Insert(row);
-  int32_t frame_id = static_cast<int32_t>(id.value);
+  node_to_row_id[0] = -1;  // We use parent_id -1 for roots.
+  for (size_t i = 1; i < init_path.nodes.size(); ++i) {
+    const HeapGraphWalker::PathFromRoot::Node& node = init_path.nodes[i];
+    PERFETTO_CHECK(node.parent_id < i);
+    const int32_t parent_row_id = node_to_row_id[node.parent_id];
+    const uint32_t depth = node.depth - 1;  // -1 because we do not have the
+                                            // artificial root in the database.
 
-  auto* callsites = context_->storage->mutable_stack_profile_callsite_table();
-  auto callsite_id = callsites->Insert({depth, parent_id, frame_id});
-  parent_id = static_cast<int32_t>(callsite_id.value);
-  depth++;
+    tables::StackProfileFrameTable::Row row{};
+    PERFETTO_CHECK(node.class_name > 0);
+    row.name = StringId(static_cast<uint32_t>(node.class_name));
+    row.mapping = mapping_row;
 
-  tables::HeapProfileAllocationTable::Row alloc_row{
-      sequence_state.current_ts, sequence_state.current_upid, parent_id,
-      static_cast<int64_t>(path.count), static_cast<int64_t>(path.size)};
-  // TODO(fmayer): Maybe add a separate table for heap graph flamegraphs.
-  context_->storage->mutable_heap_profile_allocation_table()->Insert(alloc_row);
-  for (const auto& p : path.children) {
-    const HeapGraphWalker::PathFromRoot& child = p.second;
-    WriteFlamegraph(sequence_state, child, parent_id, depth, mapping_row);
+    auto id =
+        context_->storage->mutable_stack_profile_frame_table()->Insert(row);
+    int32_t frame_id = static_cast<int32_t>(id.value);
+
+    auto* callsites = context_->storage->mutable_stack_profile_callsite_table();
+    auto callsite_id = callsites->Insert({depth, parent_row_id, frame_id});
+    int32_t row_id = static_cast<int32_t>(callsite_id.value);
+    node_to_row_id[i] = row_id;
+
+    tables::HeapProfileAllocationTable::Row alloc_row{
+        sequence_state.current_ts, sequence_state.current_upid, row_id,
+        static_cast<int64_t>(node.count), static_cast<int64_t>(node.size)};
+    // TODO(fmayer): Maybe add a separate table for heap graph flamegraphs.
+    context_->storage->mutable_heap_profile_allocation_table()->Insert(
+        alloc_row);
   }
 }
 
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.h b/src/trace_processor/importers/proto/heap_graph_tracker.h
index 56c9e7f..71bbb8f 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.h
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.h
@@ -106,8 +106,6 @@
 
   void WriteFlamegraph(const SequenceState& sequence_state,
                        const HeapGraphWalker::PathFromRoot& path,
-                       int32_t parent_id,
-                       uint32_t depth,
                        uint32_t mapping_row);
 
   TraceProcessorContext* const context_;
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.cc b/src/trace_processor/importers/proto/heap_graph_walker.cc
index 90fd398..c7ba842 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker.cc
@@ -324,27 +324,70 @@
 }
 
 HeapGraphWalker::PathFromRoot HeapGraphWalker::FindPathsFromRoot() {
+  PathFromRoot path;
   for (Node* root : roots_)
-    FindPathFromRoot(root, &path_);
+    FindPathFromRoot(root, &path);
   for (Node& node : nodes_)
     node.find_paths_from_root_visited = false;
-  return std::move(path_);
+  return path;
 }
 
 // TODO(fmayer): Teach this to handle field names.
-void HeapGraphWalker::FindPathFromRoot(Node* n, PathFromRoot* parent) {
-  PathFromRoot& cur = parent->children[n->class_name];
-  cur.size += n->self_size;
-  cur.count++;
-  cur.parent = parent;
-  cur.class_name = n->class_name;
-  for (Node* child : n->children) {
-    if (child->distance_to_root == n->distance_to_root + 1 &&
-        !child->find_paths_from_root_visited) {
-      // Mark as visited in case there is another path with the same distance
-      // from a root.
-      child->find_paths_from_root_visited = true;
-      FindPathFromRoot(child, &cur);
+void HeapGraphWalker::FindPathFromRoot(Node* first_node, PathFromRoot* path) {
+  // We have long retention chains (e.g. from LinkedList). If we use the stack
+  // here, we risk running out of stack space. This is why we use a vector to
+  // simulate the stack.
+  struct StackElem {
+    Node* node;        // Node in the original graph.
+    size_t parent_id;  // id of parent node in the result tree.
+    size_t i;          // Index of the next child of this node to handle.
+    uint32_t depth;    // Depth in the resulting tree
+                       // (including artifical root).
+  };
+
+  std::vector<StackElem> stack{{first_node, PathFromRoot::kRoot, 0, 0}};
+
+  while (!stack.empty()) {
+    Node* n = stack.back().node;
+    size_t parent_id = stack.back().parent_id;
+    uint32_t depth = stack.back().depth;
+    size_t& i = stack.back().i;
+
+    auto it = path->nodes[parent_id].children.find(n->class_name);
+    if (it == path->nodes[parent_id].children.end()) {
+      size_t id = path->nodes.size();
+      path->nodes.emplace_back(PathFromRoot::Node{});
+      std::tie(it, std::ignore) =
+          path->nodes[parent_id].children.emplace(n->class_name, id);
+      path->nodes.back().class_name = n->class_name;
+      path->nodes.back().depth = depth;
+      path->nodes.back().parent_id = parent_id;
+    }
+    size_t id = it->second;
+    PathFromRoot::Node* output_tree_node = &path->nodes[id];
+
+    if (i == 0) {
+      // This is the first time we are looking at this node, so add its
+      // size to the relevant node in the resulting tree.
+      output_tree_node->size += n->self_size;
+      output_tree_node->count++;
+    }
+    // Otherwise we have already handled this node and just need to get its
+    // i-th child.
+    if (!n->children.empty()) {
+      Node* child = n->children[i];
+      if (++i == n->children.size())
+        stack.pop_back();
+
+      if (child->distance_to_root == n->distance_to_root + 1 &&
+          !child->find_paths_from_root_visited) {
+        // Mark as visited in case there is another path with the same distance
+        // from a root.
+        child->find_paths_from_root_visited = true;
+        stack.emplace_back(StackElem{child, id, 0, depth + 1});
+      }
+    } else {
+      stack.pop_back();
     }
   }
 }
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.h b/src/trace_processor/importers/proto/heap_graph_walker.h
index 4ec3dc8..052320b 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.h
+++ b/src/trace_processor/importers/proto/heap_graph_walker.h
@@ -97,12 +97,20 @@
 
 class HeapGraphWalker {
  public:
+  using ClassNameId = int32_t;
+
   struct PathFromRoot {
-    uint64_t size = 0;
-    uint64_t count = 0;
-    int32_t class_name = -1;
-    std::map<int32_t, PathFromRoot> children;
-    PathFromRoot* parent = nullptr;
+    static constexpr size_t kRoot = 0;
+    struct Node {
+      uint32_t depth = 0;
+      // Invariant: parent_id < id of this node.
+      size_t parent_id = 0;
+      uint64_t size = 0;
+      uint64_t count = 0;
+      ClassNameId class_name = -1;
+      std::map<ClassNameId, size_t> children;
+    };
+    std::vector<Node> nodes{Node{}};
   };
 
   class Delegate {
@@ -118,7 +126,7 @@
 
   void AddEdge(int64_t owner_row, int64_t owned_row);
   void AddNode(int64_t row, uint64_t size) { AddNode(row, size, -1); }
-  void AddNode(int64_t row, uint64_t size, int32_t class_name);
+  void AddNode(int64_t row, uint64_t size, ClassNameId class_name);
 
   // Mark a a node as root. This marks all the nodes reachable from it as
   // reachable.
@@ -170,14 +178,13 @@
   void FoundSCC(Node*);
   int64_t RetainedSize(const Component&);
 
-  void FindPathFromRoot(Node* n, PathFromRoot* parent);
+  void FindPathFromRoot(Node* n, PathFromRoot* path);
 
   std::vector<Component> components_;
   std::vector<Node*> node_stack_;
   uint64_t next_node_index_ = 1;
   std::vector<Node> nodes_;
 
-  PathFromRoot path_;
   std::vector<Node*> roots_;
 
   Delegate* delegate_;
diff --git a/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
index 4b1b586..9326133 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker_unittest.cc
@@ -584,14 +584,21 @@
 }
 
 bool HasPath(const HeapGraphWalker::PathFromRoot& path,
-             std::vector<int32_t> class_names) {
+             const HeapGraphWalker::PathFromRoot::Node& node,
+             std::vector<HeapGraphWalker::ClassNameId> class_names) {
   if (class_names.empty())
     return true;
-  auto it = path.children.find(class_names[0]);
-  if (it == path.children.end())
+  auto it = node.children.find(class_names[0]);
+  if (it == node.children.end())
     return false;
   class_names.erase(class_names.begin());
-  return HasPath(it->second, class_names);
+  return HasPath(path, path.nodes[it->second], std::move(class_names));
+}
+
+bool HasPath(const HeapGraphWalker::PathFromRoot& path,
+             std::vector<int32_t> class_names) {
+  return HasPath(path, path.nodes[HeapGraphWalker::PathFromRoot::kRoot],
+                 std::move(class_names));
 }
 
 //    1      |
@@ -601,7 +608,7 @@
 //  ^        |
 //  |        |
 //  4R       |
-TEST(HeapGraphWalkeTest, ShortestPath) {
+TEST(HeapGraphWalkerTest, ShortestPath) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
   walker.AddNode(1, 1, 1);
@@ -630,7 +637,7 @@
 //  ^        |
 //  |        |
 //  4R       |
-TEST(HeapGraphWalkeTest, ShortestPathMultipleRoots) {
+TEST(HeapGraphWalkerTest, ShortestPathMultipleRoots) {
   HeapGraphWalkerTestDelegate delegate;
   HeapGraphWalker walker(&delegate);
   walker.AddNode(1, 1, 1);
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 76dffe8..9fe74c8 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -36,6 +36,7 @@
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/metadata.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/stack_profile_tracker.h"
@@ -462,47 +463,50 @@
 
 void ProtoTraceParser::ParseChromeBenchmarkMetadata(ConstBytes blob) {
   TraceStorage* storage = context_->storage.get();
+  MetadataTracker* metadata = context_->metadata_tracker.get();
+
   protos::pbzero::ChromeBenchmarkMetadata::Decoder packet(blob.data, blob.size);
   if (packet.has_benchmark_name()) {
     auto benchmark_name_id = storage->InternString(packet.benchmark_name());
-    storage->SetMetadata(metadata::benchmark_name,
-                         Variadic::String(benchmark_name_id));
+    metadata->SetMetadata(metadata::benchmark_name,
+                          Variadic::String(benchmark_name_id));
   }
   if (packet.has_benchmark_description()) {
     auto benchmark_description_id =
         storage->InternString(packet.benchmark_description());
-    storage->SetMetadata(metadata::benchmark_description,
-                         Variadic::String(benchmark_description_id));
+    metadata->SetMetadata(metadata::benchmark_description,
+                          Variadic::String(benchmark_description_id));
   }
   if (packet.has_label()) {
     auto label_id = storage->InternString(packet.label());
-    storage->SetMetadata(metadata::benchmark_label, Variadic::String(label_id));
+    metadata->SetMetadata(metadata::benchmark_label,
+                          Variadic::String(label_id));
   }
   if (packet.has_story_name()) {
     auto story_name_id = storage->InternString(packet.story_name());
-    storage->SetMetadata(metadata::benchmark_story_name,
-                         Variadic::String(story_name_id));
+    metadata->SetMetadata(metadata::benchmark_story_name,
+                          Variadic::String(story_name_id));
   }
   for (auto it = packet.story_tags(); it; ++it) {
     auto story_tag_id = storage->InternString(*it);
-    storage->AppendMetadata(metadata::benchmark_story_tags,
-                            Variadic::String(story_tag_id));
+    metadata->AppendMetadata(metadata::benchmark_story_tags,
+                             Variadic::String(story_tag_id));
   }
   if (packet.has_benchmark_start_time_us()) {
-    storage->SetMetadata(metadata::benchmark_start_time_us,
-                         Variadic::Integer(packet.benchmark_start_time_us()));
+    metadata->SetMetadata(metadata::benchmark_start_time_us,
+                          Variadic::Integer(packet.benchmark_start_time_us()));
   }
   if (packet.has_story_run_time_us()) {
-    storage->SetMetadata(metadata::benchmark_story_run_time_us,
-                         Variadic::Integer(packet.story_run_time_us()));
+    metadata->SetMetadata(metadata::benchmark_story_run_time_us,
+                          Variadic::Integer(packet.story_run_time_us()));
   }
   if (packet.has_story_run_index()) {
-    storage->SetMetadata(metadata::benchmark_story_run_index,
-                         Variadic::Integer(packet.story_run_index()));
+    metadata->SetMetadata(metadata::benchmark_story_run_index,
+                          Variadic::Integer(packet.story_run_index()));
   }
   if (packet.has_had_failures()) {
-    storage->SetMetadata(metadata::benchmark_had_failures,
-                         Variadic::Integer(packet.had_failures()));
+    metadata->SetMetadata(metadata::benchmark_had_failures,
+                          Variadic::Integer(packet.had_failures()));
   }
 }
 
@@ -615,7 +619,8 @@
     base::Uuid uuid(uuid_lsb, uuid_msb);
     std::string str = uuid.ToPrettyString();
     StringId id = context_->storage->InternString(base::StringView(str));
-    context_->storage->SetMetadata(metadata::trace_uuid, Variadic::String(id));
+    context_->metadata_tracker->SetMetadata(metadata::trace_uuid,
+                                            Variadic::String(id));
   }
 }
 
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index 9e7b9cd..f90418d 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -28,6 +28,7 @@
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
 #include "src/trace_processor/importers/proto/track_event_module.h"
 #include "src/trace_processor/metadata.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/register_additional_modules.h"
 #include "src/trace_processor/slice_tracker.h"
@@ -218,6 +219,7 @@
     context_.storage.reset(storage_);
     context_.track_tracker.reset(new TrackTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
+    context_.metadata_tracker.reset(new MetadataTracker(&context_));
     event_ = new MockEventTracker(&context_);
     context_.event_tracker.reset(event_);
     sched_ = new MockSchedEventTracker(&context_);
@@ -2373,22 +2375,25 @@
   EXPECT_CALL(*storage_, InternString(base::StringView(kTag2)))
       .WillOnce(Return(3));
 
+  StringId benchmark_id = *storage_->string_pool().GetId(
+      metadata::kNames[metadata::benchmark_name]);
+  StringId tags_id = *storage_->string_pool().GetId(
+      metadata::kNames[metadata::benchmark_story_tags]);
+
   context_.sorter->ExtractEventsForced();
 
-  const auto& meta_keys = storage_->metadata().keys();
-  const auto& meta_values = storage_->metadata().values();
-  EXPECT_EQ(meta_keys.size(), 3u);
-  std::vector<std::pair<metadata::KeyIDs, Variadic>> meta_entries;
-  for (size_t i = 0; i < meta_keys.size(); i++) {
+  const auto& meta_keys = storage_->metadata_table().name();
+  const auto& meta_values = storage_->metadata_table().str_value();
+  EXPECT_EQ(storage_->metadata_table().row_count(), 3u);
+
+  std::vector<std::pair<StringId, StringId>> meta_entries;
+  for (uint32_t i = 0; i < storage_->metadata_table().row_count(); i++) {
     meta_entries.emplace_back(std::make_pair(meta_keys[i], meta_values[i]));
   }
-  EXPECT_THAT(
-      meta_entries,
-      UnorderedElementsAreArray(
-          {std::make_pair(metadata::benchmark_name, Variadic::String(1)),
-           std::make_pair(metadata::benchmark_story_tags, Variadic::String(2)),
-           std::make_pair(metadata::benchmark_story_tags,
-                          Variadic::String(3))}));
+  EXPECT_THAT(meta_entries,
+              UnorderedElementsAreArray({std::make_pair(benchmark_id, 1),
+                                         std::make_pair(tags_id, 2),
+                                         std::make_pair(tags_id, 3)}));
 }
 
 TEST_F(ProtoTraceParserTest, AndroidPackagesList) {
@@ -2426,25 +2431,15 @@
   // structure, make an assumption that metadata storage is filled in in the
   // FIFO order of seen packages.
   const auto& args = context_.storage->args();
-  const auto& metadata = context_.storage->metadata();
-  const auto& meta_keys = metadata.keys();
-  const auto& meta_values = metadata.values();
+  const auto& metadata = context_.storage->metadata_table();
 
-  ASSERT_TRUE(std::count(meta_keys.cbegin(), meta_keys.cend(),
-                         metadata::android_packages_list) == 2);
+  Table package_list = metadata.Filter(
+      {metadata.name().eq(metadata::kNames[metadata::android_packages_list])});
+  ASSERT_EQ(package_list.row_count(), 2u);
 
-  auto first_meta_idx = std::distance(
-      meta_keys.cbegin(), std::find(meta_keys.cbegin(), meta_keys.cend(),
-                                    metadata::android_packages_list));
-  auto second_meta_idx = std::distance(
-      meta_keys.cbegin(),
-      std::find(meta_keys.cbegin() + first_meta_idx + 1, meta_keys.cend(),
-                metadata::android_packages_list));
-
-  uint32_t first_set_id = static_cast<uint32_t>(
-      meta_values[static_cast<size_t>(first_meta_idx)].int_value);
-  uint32_t second_set_id = static_cast<uint32_t>(
-      meta_values[static_cast<size_t>(second_meta_idx)].int_value);
+  const Column* int_value = package_list.GetColumnByName("int_value");
+  uint32_t first_set_id = static_cast<uint32_t>(int_value->Get(0).long_value);
+  uint32_t second_set_id = static_cast<uint32_t>(int_value->Get(1).long_value);
 
   // helper to look up arg values
   auto find_arg = [&args, this](ArgSetId set_id, const char* arg_name) {
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 5beed38..dd143dd 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -21,6 +21,7 @@
 #include "perfetto/protozero/proto_decoder.h"
 #include "src/trace_processor/event_tracker.h"
 #include "src/trace_processor/metadata.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/syscall_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
@@ -308,18 +309,18 @@
     StringPool::Id machine_id =
         context_->storage->InternString(utsname.machine());
 
-    context_->storage->SetMetadata(metadata::system_name,
-                                   Variadic::String(sysname_id));
-    context_->storage->SetMetadata(metadata::system_version,
-                                   Variadic::String(version_id));
-    context_->storage->SetMetadata(metadata::system_release,
-                                   Variadic::String(release_id));
-    context_->storage->SetMetadata(metadata::system_machine,
-                                   Variadic::String(machine_id));
+    MetadataTracker* metadata = context_->metadata_tracker.get();
+    metadata->SetMetadata(metadata::system_name, Variadic::String(sysname_id));
+    metadata->SetMetadata(metadata::system_version,
+                          Variadic::String(version_id));
+    metadata->SetMetadata(metadata::system_release,
+                          Variadic::String(release_id));
+    metadata->SetMetadata(metadata::system_machine,
+                          Variadic::String(machine_id));
   }
 
   if (packet.has_android_build_fingerprint()) {
-    context_->storage->SetMetadata(
+    context_->metadata_tracker->SetMetadata(
         metadata::android_build_fingerprint,
         Variadic::String(context_->storage->InternString(
             packet.android_build_fingerprint())));
diff --git a/src/trace_processor/metadata.h b/src/trace_processor/metadata.h
index aad028f..e7edcd8 100644
--- a/src/trace_processor/metadata.h
+++ b/src/trace_processor/metadata.h
@@ -28,41 +28,58 @@
 
 // Compile time list of metadata items.
 // clang-format off
-#define PERFETTO_TP_METADATA(F)                                        \
-  F(benchmark_description,               kSingle,  Variadic::kString), \
-  F(benchmark_name,                      kSingle,  Variadic::kString), \
-  F(benchmark_start_time_us,             kSingle,  Variadic::kInt),    \
-  F(benchmark_had_failures,              kSingle,  Variadic::kInt),    \
-  F(benchmark_label,                     kSingle,  Variadic::kString), \
-  F(benchmark_story_name,                kSingle,  Variadic::kString), \
-  F(benchmark_story_run_index,           kSingle,  Variadic::kInt),    \
-  F(benchmark_story_run_time_us,         kSingle,  Variadic::kInt),    \
-  F(benchmark_story_tags,                kMulti,   Variadic::kString), \
-  F(android_packages_list,               kMulti,   Variadic::kInt),    \
-  F(statsd_triggering_subscription_id,   kSingle,  Variadic::kInt),    \
-  F(trace_uuid,                          kSingle,  Variadic::kString), \
-  F(system_name,                         kSingle,  Variadic::kString), \
-  F(system_version,                      kSingle,  Variadic::kString), \
-  F(system_release,                      kSingle,  Variadic::kString), \
-  F(system_machine,                      kSingle,  Variadic::kString), \
-  F(android_build_fingerprint,           kSingle,  Variadic::kString)
+#define PERFETTO_TP_METADATA(F)                                               \
+  F(benchmark_description,             KeyType::kSingle,  Variadic::kString), \
+  F(benchmark_name,                    KeyType::kSingle,  Variadic::kString), \
+  F(benchmark_start_time_us,           KeyType::kSingle,  Variadic::kInt),    \
+  F(benchmark_had_failures,            KeyType::kSingle,  Variadic::kInt),    \
+  F(benchmark_label,                   KeyType::kSingle,  Variadic::kString), \
+  F(benchmark_story_name,              KeyType::kSingle,  Variadic::kString), \
+  F(benchmark_story_run_index,         KeyType::kSingle,  Variadic::kInt),    \
+  F(benchmark_story_run_time_us,       KeyType::kSingle,  Variadic::kInt),    \
+  F(benchmark_story_tags,              KeyType::kMulti,   Variadic::kString), \
+  F(android_packages_list,             KeyType::kMulti,   Variadic::kInt),    \
+  F(statsd_triggering_subscription_id, KeyType::kSingle,  Variadic::kInt),    \
+  F(trace_uuid,                        KeyType::kSingle,  Variadic::kString), \
+  F(system_name,                       KeyType::kSingle,  Variadic::kString), \
+  F(system_version,                    KeyType::kSingle,  Variadic::kString), \
+  F(system_release,                    KeyType::kSingle,  Variadic::kString), \
+  F(system_machine,                    KeyType::kSingle,  Variadic::kString), \
+  F(android_build_fingerprint,         KeyType::kSingle,  Variadic::kString)
 // clang-format on
 
-enum KeyType {
-  kSingle,  // One value per key.
-  kMulti    // Multiple values per key.
+// Compile time list of metadata items.
+// clang-format off
+#define PERFETTO_TP_METADATA_KEY_TYPES(F) \
+  F(kSingle, "single"),                   \
+  F(kMulti,  "multi")
+// clang-format
+
+#define PERFETTO_TP_META_TYPE_ENUM(varname, ...) varname
+enum class KeyType : size_t {
+  PERFETTO_TP_METADATA_KEY_TYPES(PERFETTO_TP_META_TYPE_ENUM),
+  kNumKeyTypes,
+};
+
+#define PERFETTO_TP_META_TYPE_NAME(_, name, ...) name
+constexpr char const* kKeyTypeNames[] = {
+  PERFETTO_TP_METADATA_KEY_TYPES(PERFETTO_TP_META_TYPE_NAME)
 };
 
 // Declares an enum of literals (one for each item). The enum values of each
 // literal corresponds to the string index in the arrays below.
 #define PERFETTO_TP_META_ENUM(name, ...) name
-enum KeyIDs : size_t { PERFETTO_TP_METADATA(PERFETTO_TP_META_ENUM), kNumKeys };
+enum KeyIDs : size_t {
+  PERFETTO_TP_METADATA(PERFETTO_TP_META_ENUM),
+  kNumKeys
+};
 
 // The code below declares an array for each property:
 // name, key type, value type.
 
 #define PERFETTO_TP_META_NAME(name, ...) #name
-constexpr char const* kNames[] = {PERFETTO_TP_METADATA(PERFETTO_TP_META_NAME)};
+constexpr char const* kNames[] = {
+  PERFETTO_TP_METADATA(PERFETTO_TP_META_NAME)};
 
 #define PERFETTO_TP_META_KEYTYPE(_, type, ...) type
 constexpr KeyType kKeyTypes[] = {
diff --git a/src/trace_processor/metadata_table.cc b/src/trace_processor/metadata_table.cc
deleted file mode 100644
index a31a7c0..0000000
--- a/src/trace_processor/metadata_table.cc
+++ /dev/null
@@ -1,158 +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.
- */
-
-#include "src/trace_processor/metadata_table.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/storage_columns.h"
-#include "src/trace_processor/storage_schema.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-MetadataTable::MetadataTable(sqlite3*, const TraceStorage* storage)
-    : storage_(storage) {}
-
-void MetadataTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<MetadataTable>(db, storage, "metadata");
-}
-
-StorageSchema MetadataTable::CreateStorageSchema() {
-  return StorageSchema::Builder()
-      .AddColumn<StringColumn<MetadataKeyNameAccessor>>(
-          "name", &storage_->metadata().keys())
-      .AddColumn<StringColumn<MetadataKeyTypeAccessor>>(
-          "key_type", &storage_->metadata().keys())
-      .AddColumn<ValueColumn>("int_value", Variadic::Type::kInt, storage_)
-      .AddColumn<ValueColumn>("str_value", Variadic::Type::kString, storage_)
-      .Build({"name"});
-}
-
-uint32_t MetadataTable::RowCount() {
-  return static_cast<uint32_t>(storage_->metadata().keys().size());
-}
-
-int MetadataTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
-  return SQLITE_OK;
-}
-
-MetadataTable::MetadataKeyNameAccessor::MetadataKeyNameAccessor(
-    const std::deque<metadata::KeyIDs>* keys)
-    : keys_(keys) {}
-
-MetadataTable::MetadataKeyNameAccessor::~MetadataKeyNameAccessor() = default;
-
-MetadataTable::MetadataKeyTypeAccessor::MetadataKeyTypeAccessor(
-    const std::deque<metadata::KeyIDs>* keys)
-    : keys_(keys) {}
-
-MetadataTable::MetadataKeyTypeAccessor::~MetadataKeyTypeAccessor() = default;
-
-MetadataTable::ValueColumn::ValueColumn(std::string col_name,
-                                        Variadic::Type type,
-                                        const TraceStorage* storage)
-    : StorageColumn(col_name, false /* hidden */),
-      type_(type),
-      storage_(storage) {
-  PERFETTO_CHECK(type == Variadic::Type::kInt ||
-                 type == Variadic::Type::kString);
-}
-
-void MetadataTable::ValueColumn::ReportResult(sqlite3_context* ctx,
-                                              uint32_t row) const {
-  const auto& metadata = storage_->metadata();
-  auto value_type = metadata::kValueTypes[metadata.keys()[row]];
-  if (value_type != type_) {
-    sqlite3_result_null(ctx);
-    return;
-  }
-
-  if (value_type == Variadic::Type::kInt) {
-    sqlite_utils::ReportSqliteResult(ctx, metadata.values()[row].int_value);
-    return;
-  }
-  if (value_type == Variadic::Type::kString) {
-    const char* str =
-        storage_->GetString(metadata.values()[row].string_value).c_str();
-    sqlite3_result_text(ctx, str, -1, sqlite_utils::kSqliteStatic);
-    return;
-  }
-  PERFETTO_FATAL("Unimplemented metadata value type.");
-}
-
-MetadataTable::ValueColumn::Bounds MetadataTable::ValueColumn::BoundFilter(
-    int,
-    sqlite3_value*) const {
-  return Bounds{};
-}
-
-void MetadataTable::ValueColumn::Filter(int op,
-                                        sqlite3_value* value,
-                                        FilteredRowIndex* index) const {
-  if (type_ == Variadic::Type::kInt) {
-    bool op_is_null = sqlite_utils::IsOpIsNull(op);
-    auto predicate = sqlite_utils::CreateNumericPredicate<int64_t>(op, value);
-    index->FilterRows(
-        [this, predicate, op_is_null](uint32_t row) PERFETTO_ALWAYS_INLINE {
-          const auto& arg = storage_->metadata().values()[row];
-          return arg.type == type_ ? predicate(arg.int_value) : op_is_null;
-        });
-    return;
-  }
-  if (type_ == Variadic::Type::kString) {
-    auto predicate = sqlite_utils::CreateStringPredicate(op, value);
-    index->FilterRows([this, &predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
-      const auto& arg = storage_->metadata().values()[row];
-      return arg.type == type_
-                 ? predicate(storage_->GetString(arg.string_value).c_str())
-                 : predicate(nullptr);
-    });
-    return;
-  }
-  PERFETTO_FATAL("Unimplemented metadata value type.");
-}
-
-MetadataTable::ValueColumn::Comparator MetadataTable::ValueColumn::Sort(
-    const QueryConstraints::OrderBy& ob) const {
-  if (ob.desc) {
-    return [this](uint32_t f, uint32_t s) { return -CompareRefsAsc(f, s); };
-  }
-  return [this](uint32_t f, uint32_t s) { return CompareRefsAsc(f, s); };
-}
-
-int MetadataTable::ValueColumn::CompareRefsAsc(uint32_t f, uint32_t s) const {
-  const auto& arg_f = storage_->metadata().values()[f];
-  const auto& arg_s = storage_->metadata().values()[s];
-
-  if (arg_f.type == type_ && arg_s.type == type_) {
-    if (type_ == Variadic::Type::kInt) {
-      return sqlite_utils::CompareValuesAsc(arg_f.int_value, arg_s.int_value);
-    }
-    if (type_ == Variadic::Type::kString) {
-      const auto& f_str = storage_->GetString(arg_f.string_value);
-      const auto& s_str = storage_->GetString(arg_s.string_value);
-      return sqlite_utils::CompareValuesAsc(f_str, s_str);
-    }
-    PERFETTO_FATAL("Unimplemented metadata value type.");
-  } else if (arg_s.type == type_) {
-    return -1;
-  } else if (arg_f.type == type_) {
-    return 1;
-  }
-  return 0;
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/metadata_table.h b/src/trace_processor/metadata_table.h
deleted file mode 100644
index c7b0dfe..0000000
--- a/src/trace_processor/metadata_table.h
+++ /dev/null
@@ -1,122 +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.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_METADATA_TABLE_H_
-#define SRC_TRACE_PROCESSOR_METADATA_TABLE_H_
-
-#include "src/trace_processor/metadata.h"
-#include "src/trace_processor/storage_table.h"
-#include "src/trace_processor/trace_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class MetadataTable : public StorageTable {
- public:
-  MetadataTable(sqlite3*, const TraceStorage*);
-
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  // StorageTable implementation.
-  StorageSchema CreateStorageSchema() override;
-  uint32_t RowCount() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-
- private:
-  // Returns the stringified key enum name from metadata::kNames.
-  class MetadataKeyNameAccessor : public Accessor<NullTermStringView> {
-   public:
-    MetadataKeyNameAccessor(const std::deque<metadata::KeyIDs>* keys);
-    ~MetadataKeyNameAccessor() override;
-
-    uint32_t Size() const override {
-      return static_cast<uint32_t>(keys_->size());
-    }
-
-    NullTermStringView Get(uint32_t idx) const override {
-      return NullTermStringView(metadata::kNames[(*keys_)[idx]]);
-    }
-
-   private:
-    const std::deque<metadata::KeyIDs>* keys_;
-  };
-
-  // Returns the stringified metadata type, "single" for scalar, "multi" for
-  // repeated.
-  class MetadataKeyTypeAccessor : public Accessor<NullTermStringView> {
-   public:
-    MetadataKeyTypeAccessor(const std::deque<metadata::KeyIDs>* keys);
-    ~MetadataKeyTypeAccessor() override;
-
-    uint32_t Size() const override {
-      return static_cast<uint32_t>(keys_->size());
-    }
-
-    NullTermStringView Get(uint32_t idx) const override {
-      switch (metadata::kKeyTypes[(*keys_)[idx]]) {
-        case metadata::KeyType::kSingle:
-          return NullTermStringView("single");
-          break;
-        case metadata::KeyType::kMulti:
-          return NullTermStringView("multi");
-          break;
-      }
-      PERFETTO_FATAL("unsupported metadata type");  // for gcc
-    }
-
-   private:
-    const std::deque<metadata::KeyIDs>* keys_;
-  };
-
-  // Returns values from Variadic storage. Only supports columns of
-  // type Variadic::Type::kInt or Variadic::Type::kString.
-  //
-  // Based on ArgsTable::ValueColumn.
-  class ValueColumn final : public StorageColumn {
-   public:
-    ValueColumn(std::string col_name,
-                Variadic::Type type,
-                const TraceStorage* storage);
-
-    void ReportResult(sqlite3_context* ctx, uint32_t row) const override;
-    Bounds BoundFilter(int op, sqlite3_value* sqlite_val) const override;
-    void Filter(int op, sqlite3_value* value, FilteredRowIndex*) const override;
-    Comparator Sort(const QueryConstraints::OrderBy& ob) const override;
-
-    bool HasOrdering() const override { return false; }
-
-    SqlValue::Type GetType() const override {
-      if (type_ == Variadic::Type::kInt)
-        return SqlValue::Type::kLong;
-      if (type_ == Variadic::Type::kString)
-        return SqlValue::Type::kString;
-      PERFETTO_FATAL("Unimplemented metadata value type.");
-    }
-
-   private:
-    int CompareRefsAsc(uint32_t f, uint32_t s) const;
-
-    Variadic::Type type_;
-    const TraceStorage* storage_ = nullptr;
-  };
-
-  const TraceStorage* const storage_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_METADATA_TABLE_H_
diff --git a/src/trace_processor/metadata_table_unittest.cc b/src/trace_processor/metadata_table_unittest.cc
deleted file mode 100644
index 9f8309c..0000000
--- a/src/trace_processor/metadata_table_unittest.cc
+++ /dev/null
@@ -1,119 +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.
- */
-
-#include "src/trace_processor/metadata_table.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-class MetadataTableUnittest : public ::testing::Test {
- public:
-  MetadataTableUnittest() {
-    sqlite3* db = nullptr;
-    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
-    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
-    db_.reset(db);
-
-    context_.storage.reset(new TraceStorage());
-    MetadataTable::RegisterTable(db_.get(), context_.storage.get());
-  }
-
-  void PrepareValidStatement(const std::string& sql) {
-    int size = static_cast<int>(sql.size());
-    sqlite3_stmt* stmt;
-    ASSERT_EQ(sqlite3_prepare_v2(*db_, sql.c_str(), size, &stmt, nullptr),
-              SQLITE_OK);
-    stmt_.reset(stmt);
-  }
-
-  const char* GetColumnAsText(int colId) {
-    return reinterpret_cast<const char*>(sqlite3_column_text(*stmt_, colId));
-  }
-
- protected:
-  TraceProcessorContext context_;
-  ScopedDb db_;
-  ScopedStmt stmt_;
-};
-
-TEST_F(MetadataTableUnittest, NoEntries) {
-  PrepareValidStatement("SELECT * FROM metadata");
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(MetadataTableUnittest, SingleStringValue) {
-  static const char kName[] = "benchmark";
-  Variadic value = Variadic::String(context_.storage->InternString(kName));
-  context_.storage->SetMetadata(metadata::benchmark_name, value);
-
-  PrepareValidStatement("SELECT * FROM metadata");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_STREQ(GetColumnAsText(0), "benchmark_name");      // name
-  ASSERT_STREQ(GetColumnAsText(1), "single");              // key_type
-  ASSERT_EQ(sqlite3_column_type(*stmt_, 2), SQLITE_NULL);  // int_value
-  ASSERT_STREQ(GetColumnAsText(3), kName);                 // str_value
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(MetadataTableUnittest, SingleIntegerValue) {
-  static const int64_t kTimestamp = 1234567890;
-  Variadic value = Variadic::Integer(kTimestamp);
-  context_.storage->SetMetadata(metadata::benchmark_story_run_time_us, value);
-
-  PrepareValidStatement("SELECT * FROM metadata");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_STREQ(GetColumnAsText(0), "benchmark_story_run_time_us");  // name
-  ASSERT_STREQ(GetColumnAsText(1), "single");                       // key_type
-  ASSERT_EQ(sqlite3_column_int64(*stmt_, 2), kTimestamp);           // int_value
-  ASSERT_EQ(sqlite3_column_type(*stmt_, 3), SQLITE_NULL);           // str_value
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(MetadataTableUnittest, MultipleStringValues) {
-  static const char kTag1[] = "foo";
-  static const char kTag2[] = "bar";
-  Variadic tag1 = Variadic::String(context_.storage->InternString(kTag1));
-  Variadic tag2 = Variadic::String(context_.storage->InternString(kTag2));
-  context_.storage->AppendMetadata(metadata::benchmark_story_tags, tag1);
-  context_.storage->AppendMetadata(metadata::benchmark_story_tags, tag2);
-
-  PrepareValidStatement("SELECT * FROM metadata");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_STREQ(GetColumnAsText(0), "benchmark_story_tags");  // name
-  ASSERT_STREQ(GetColumnAsText(1), "multi");                 // key_type
-  ASSERT_EQ(sqlite3_column_type(*stmt_, 2), SQLITE_NULL);    // int_value
-  ASSERT_STREQ(GetColumnAsText(3), kTag1);                   // str_value
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  ASSERT_STREQ(GetColumnAsText(0), "benchmark_story_tags");  // name
-  ASSERT_STREQ(GetColumnAsText(1), "multi");                 // key_type
-  ASSERT_EQ(sqlite3_column_type(*stmt_, 2), SQLITE_NULL);    // int_value
-  ASSERT_STREQ(GetColumnAsText(3), kTag2);                   // str_value
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/metadata_tracker.cc b/src/trace_processor/metadata_tracker.cc
new file mode 100644
index 0000000..e105d01
--- /dev/null
+++ b/src/trace_processor/metadata_tracker.cc
@@ -0,0 +1,94 @@
+/*
+ * 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 "src/trace_processor/metadata_tracker.h"
+
+#include "src/trace_processor/process_tracker.h"
+#include "src/trace_processor/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+MetadataTracker::MetadataTracker(TraceProcessorContext* context)
+    : context_(context) {
+  for (uint32_t i = 0; i < kNumKeys; ++i) {
+    key_ids_[i] = context->storage->InternString(metadata::kNames[i]);
+  }
+  for (uint32_t i = 0; i < kNumKeyTypes; ++i) {
+    key_type_ids_[i] =
+        context->storage->InternString(metadata::kKeyTypeNames[i]);
+  }
+}
+
+MetadataId MetadataTracker::SetMetadata(metadata::KeyIDs key, Variadic value) {
+  PERFETTO_DCHECK(metadata::kKeyTypes[key] == metadata::KeyType::kSingle);
+  PERFETTO_DCHECK(value.type == metadata::kValueTypes[key]);
+
+  auto* metadata_table = context_->storage->mutable_metadata_table();
+  uint32_t key_idx = static_cast<uint32_t>(key);
+  base::Optional<uint32_t> opt_row =
+      metadata_table->name().IndexOf(metadata::kNames[key_idx]);
+  if (opt_row) {
+    WriteValue(*opt_row, value);
+    return metadata_table->id()[*opt_row];
+  }
+
+  tables::MetadataTable::Row row;
+  row.name = key_ids_[key_idx];
+  row.key_type = key_type_ids_[static_cast<size_t>(metadata::KeyType::kSingle)];
+
+  MetadataId id = metadata_table->Insert(row);
+  WriteValue(*metadata_table->id().IndexOf(id), value);
+  return id;
+}
+
+MetadataId MetadataTracker::AppendMetadata(metadata::KeyIDs key,
+                                           Variadic value) {
+  PERFETTO_DCHECK(key < metadata::kNumKeys);
+  PERFETTO_DCHECK(metadata::kKeyTypes[key] == metadata::KeyType::kMulti);
+  PERFETTO_DCHECK(value.type == metadata::kValueTypes[key]);
+
+  uint32_t key_idx = static_cast<uint32_t>(key);
+  tables::MetadataTable::Row row;
+  row.name = key_ids_[key_idx];
+  row.key_type = key_type_ids_[static_cast<size_t>(metadata::KeyType::kMulti)];
+
+  auto* metadata_table = context_->storage->mutable_metadata_table();
+  MetadataId id = metadata_table->Insert(row);
+  WriteValue(*metadata_table->id().IndexOf(id), value);
+  return id;
+}
+
+void MetadataTracker::WriteValue(uint32_t row, Variadic value) {
+  auto* metadata_table = context_->storage->mutable_metadata_table();
+  switch (value.type) {
+    case Variadic::Type::kInt:
+      metadata_table->mutable_int_value()->Set(row, value.int_value);
+      break;
+    case Variadic::Type::kString:
+      metadata_table->mutable_str_value()->Set(row, value.string_value);
+      break;
+    case Variadic::Type::kJson:
+    case Variadic::Type::kBool:
+    case Variadic::Type::kPointer:
+    case Variadic::Type::kUint:
+    case Variadic::Type::kReal:
+      PERFETTO_FATAL("Unsupported value type");
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/metadata_tracker.h b/src/trace_processor/metadata_tracker.h
new file mode 100644
index 0000000..38685ac
--- /dev/null
+++ b/src/trace_processor/metadata_tracker.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_METADATA_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_METADATA_TRACKER_H_
+
+#include "src/trace_processor/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+// Tracks information in the metadata table.
+class MetadataTracker {
+ public:
+  MetadataTracker(TraceProcessorContext* context);
+
+  // Example usage:
+  // SetMetadata(metadata::benchmark_name,
+  //             Variadic::String(storage->InternString("foo"));
+  // Returns the id of the new entry.
+  MetadataId SetMetadata(metadata::KeyIDs key, Variadic value);
+
+  // Example usage:
+  // AppendMetadata(metadata::benchmark_story_tags,
+  //                Variadic::String(storage->InternString("bar"));
+  // Returns the id of the new entry.
+  MetadataId AppendMetadata(metadata::KeyIDs key, Variadic value);
+
+ private:
+  static constexpr size_t kNumKeys =
+      static_cast<size_t>(metadata::KeyIDs::kNumKeys);
+  static constexpr size_t kNumKeyTypes =
+      static_cast<size_t>(metadata::KeyType::kNumKeyTypes);
+
+  void WriteValue(uint32_t row, Variadic value);
+
+  std::array<StringId, kNumKeys> key_ids_;
+  std::array<StringId, kNumKeyTypes> key_type_ids_;
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_METADATA_TRACKER_H_
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 71b2623..537f55e 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -40,6 +40,7 @@
   "android/mem_stats_priority_breakdown.sql",
   "android/span_view_stats.sql",
   "android/upid_span_view.sql",
+  "android/unmapped_java_symbols.sql",
   "android/unsymbolized_frames.sql",
 ]
 
diff --git a/src/trace_processor/metrics/android/process_metadata.sql b/src/trace_processor/metrics/android/process_metadata.sql
index 92b7bfe..2dbc0b4 100644
--- a/src/trace_processor/metrics/android/process_metadata.sql
+++ b/src/trace_processor/metrics/android/process_metadata.sql
@@ -56,7 +56,7 @@
 
 DROP VIEW IF EXISTS process_metadata;
 
-CREATE VIEW process_metadata AS
+CREATE VIEW IF NOT EXISTS process_metadata AS
 WITH upid_packages AS (
   SELECT
   upid,
diff --git a/src/trace_processor/metrics/android/unmapped_java_symbols.sql b/src/trace_processor/metrics/android/unmapped_java_symbols.sql
new file mode 100644
index 0000000..c2e7785
--- /dev/null
+++ b/src/trace_processor/metrics/android/unmapped_java_symbols.sql
@@ -0,0 +1,50 @@
+--
+-- Copyright 2020 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.
+--
+
+SELECT RUN_METRIC('android/process_metadata.sql');
+
+CREATE TABLE IF NOT EXISTS types_per_upid AS
+WITH distinct_unmapped_type_names AS (
+  SELECT DISTINCT upid, type_name
+  FROM heap_graph_object WHERE deobfuscated_type_name IS NULL
+)
+SELECT upid, RepeatedField(type_name) AS types
+FROM distinct_unmapped_type_names GROUP BY 1;
+
+CREATE TABLE IF NOT EXISTS fields_per_upid AS
+WITH distinct_unmapped_field_names AS (
+  SELECT DISTINCT upid, field_name
+  FROM heap_graph_object JOIN heap_graph_reference USING (reference_set_id)
+  WHERE deobfuscated_type_name IS NULL
+)
+SELECT upid, RepeatedField(field_name) AS fields
+FROM distinct_unmapped_field_names GROUP BY 1;
+
+CREATE VIEW IF NOT EXISTS java_symbols_per_process AS
+SELECT UnmappedJavaSymbols_ProcessSymbols(
+  'process_metadata', metadata,
+  'type_name', types,
+  'field_name', fields
+) types
+FROM types_per_upid
+JOIN process_metadata USING (upid)
+LEFT JOIN fields_per_upid USING (upid);
+
+CREATE VIEW unmapped_java_symbols_output AS
+SELECT UnmappedJavaSymbols(
+  'process_symbols',
+  (SELECT RepeatedField(types) FROM java_symbols_per_process)
+);
diff --git a/src/trace_processor/metrics/metrics.descriptor.h b/src/trace_processor/metrics/metrics.descriptor.h
index 7b37058..6e19075 100644
--- a/src/trace_processor/metrics/metrics.descriptor.h
+++ b/src/trace_processor/metrics/metrics.descriptor.h
@@ -27,14 +27,14 @@
 // SHA1(tools/gen_binary_descriptors)
 // f242f1ac484bbe7ba4c45e77b56ab588f8015196
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// e1fea13f5853cbb276ae800c5fee91c46704565d
+// 768c8fa5ed7f48eb644a1a45bb7295e32f8c771c
 
 // 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, 11445> kMetricsDescriptor{
+constexpr std::array<uint8_t, 12018> 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,
@@ -733,262 +733,310 @@
      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,
+     0x03, 0x0a, 0xa3, 0x03, 0x0a, 0x3b, 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, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6a,
+     0x61, 0x76, 0x61, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 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, 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, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x96, 0x02, 0x0a, 0x13, 0x55,
+     0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a, 0x61, 0x76, 0x61, 0x53,
+     0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x0f, 0x70, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+     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, 0x55, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a, 0x61,
+     0x76, 0x61, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x2e, 0x50, 0x72,
+     0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
+     0x52, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x79, 0x6d,
+     0x62, 0x6f, 0x6c, 0x73, 0x1a, 0xa0, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f,
+     0x63, 0x65, 0x73, 0x73, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12,
+     0x52, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d,
+     0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
+     0x0b, 0x32, 0x27, 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, 0x4d, 0x65,
+     0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63,
+     0x65, 0x73, 0x73, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
+     0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+     0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65,
+     0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c,
+     0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
+     0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 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, 0xfc,
+     0x03, 0x0a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6a,
+     0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61,
+     0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65,
+     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+     0x73, 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, 0x70,
+     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
+     0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x02,
+     0x0a, 0x0d, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74,
+     0x61, 0x74, 0x73, 0x12, 0x53, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61,
+     0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20,
+     0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61,
+     0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,
+     0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74,
+     0x73, 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53,
+     0x74, 0x61, 0x74, 0x73, 0x1a, 0x65, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70,
+     0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01,
+     0x28, 0x03, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x65,
+     0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+     0x03, 0x52, 0x08, 0x68, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12,
+     0x2e, 0x0a, 0x13, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65,
+     0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03,
+     0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61,
+     0x62, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x1a,
+     0xa7, 0x01, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
+     0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x70, 0x69,
+     0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x75, 0x70, 0x69,
+     0x64, 0x12, 0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+     0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
+     0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x3f, 0x0a, 0x07,
+     0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
+     0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61,
+     0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x53, 0x61,
+     0x6d, 0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+     0x73, 0x42, 0x02, 0x48, 0x03, 0x0a, 0x8e, 0x13, 0x0a, 0x25, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+     0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72,
+     0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+     0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
+     0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x2f, 0x62, 0x61, 0x74, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     0x36, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
+     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d,
+     0x5f, 0x75, 0x6e, 0x61, 0x67, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f,
+     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
+     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
+     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f, 0x6d,
+     0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+     0x37, 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, 0x72, 0x65, 0x61, 0x73, 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, 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, 0x3b, 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, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6a, 0x61,
+     0x76, 0x61, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 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, 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, 0xfc, 0x03, 0x0a,
-     0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-     0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6a, 0x61, 0x76,
-     0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 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, 0x70, 0x72, 0x6f,
-     0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
-     0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x02, 0x0a, 0x0d,
-     0x4a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74,
-     0x73, 0x12, 0x53, 0x0a, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
-     0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-     0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61,
-     0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x49, 0x6e,
-     0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52,
-     0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61,
-     0x74, 0x73, 0x1a, 0x65, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
-     0x12, 0x0e, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
-     0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x65, 0x61, 0x70,
-     0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
-     0x08, 0x68, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2e, 0x0a,
-     0x13, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x68,
-     0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x11, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c,
-     0x65, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0xa7, 0x01,
-     0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74,
-     0x61, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x70, 0x69, 0x64, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x75, 0x70, 0x69, 0x64, 0x12,
-     0x41, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02,
-     0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x07, 0x70,
-     0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x3f, 0x0a, 0x07, 0x73, 0x61,
-     0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
-     0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65,
-     0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x53, 0x61, 0x6d, 0x70,
-     0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x42,
-     0x02, 0x48, 0x03, 0x0a, 0xf7, 0x11, 0x0a, 0x25, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f,
-     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x1a, 0x31, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72,
-     0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
-     0x62, 0x61, 0x74, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x2f, 0x63, 0x70, 0x75, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x36, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6d, 0x65, 0x6d, 0x5f, 0x75,
-     0x6e, 0x61, 0x67, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x2f, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6c, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74,
-     0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x37, 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, 0x72,
-     0x65, 0x61, 0x73, 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, 0x65, 0x74, 0x74, 0x6f, 0x2f,
-     0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x2f, 0x70, 0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73,
-     0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x1a, 0x34, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
-     0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x3c, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x2f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70, 0x72,
-     0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69,
-     0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x61,
-     0x67, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x1a, 0x39, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x75,
-     0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f,
-     0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x1a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-     0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x6a, 0x61,
-     0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74,
-     0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf8, 0x02, 0x0a, 0x0d,
-     0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
-     0x61, 0x12, 0x50, 0x0a, 0x11, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73,
-     0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01,
-     0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54,
-     0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-     0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65, 0x72, 0x72, 0x6f,
-     0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
-     0x2a, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x64, 0x75, 0x72,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01,
-     0x28, 0x03, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
-     0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20,
-     0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63, 0x65, 0x55, 0x75,
-     0x69, 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x69, 0x6e, 0x67,
-     0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
-     0x09, 0x52, 0x17, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x42, 0x75,
-     0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69,
-     0x6e, 0x74, 0x12, 0x49, 0x0a, 0x21, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64,
-     0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f,
-     0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-     0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1e, 0x73,
-     0x74, 0x61, 0x74, 0x73, 0x64, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72,
-     0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-     0x69, 0x6f, 0x6e, 0x49, 0x64, 0x1a, 0x43, 0x0a, 0x05, 0x45, 0x6e, 0x74,
-     0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10,
-     0x0a, 0x03, 0x69, 0x64, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
-     0x03, 0x69, 0x64, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
-     0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c,
-     0x75, 0x65, 0x22, 0x80, 0x09, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65,
-     0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x48, 0x0a, 0x0c, 0x61,
-     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x74, 0x18,
-     0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x42, 0x61, 0x74, 0x74, 0x65,
-     0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0b, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x42, 0x61, 0x74, 0x74, 0x12, 0x42, 0x0a,
-     0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x63, 0x70, 0x75,
-     0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x43, 0x70, 0x75, 0x4d,
-     0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-     0x69, 0x64, 0x43, 0x70, 0x75, 0x12, 0x45, 0x0a, 0x0b, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4d, 0x65,
-     0x74, 0x72, 0x69, 0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x4d, 0x65, 0x6d, 0x12, 0x5c, 0x0a, 0x11, 0x61, 0x6e, 0x64, 0x72,
-     0x6f, 0x69, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x75, 0x6e, 0x61, 0x67,
-     0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x70, 0x65,
+     0x6f, 0x74, 0x6f, 0x1a, 0x35, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
+     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x74,
+     0x72, 0x69, 0x63, 0x73, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x2f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
+     0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf8,
+     0x02, 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, 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,
+     0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64,
+     0x61, 0x74, 0x61, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65,
+     0x72, 0x72, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74,
+     0x72, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f,
+     0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18,
+     0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x63, 0x65,
+     0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x12, 0x1d,
+     0x0a, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64,
+     0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x61, 0x63,
+     0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x3a, 0x0a, 0x19, 0x61, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66,
+     0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x04,
+     0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+     0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72,
+     0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x49, 0x0a, 0x21, 0x73, 0x74, 0x61,
+     0x74, 0x73, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69,
+     0x6e, 0x67, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+     0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
+     0x52, 0x1e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x54, 0x72, 0x69, 0x67,
+     0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
+     0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x1a, 0x43, 0x0a, 0x05,
+     0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+     0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+     0x65, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x78, 0x18, 0x02, 0x20, 0x01,
+     0x28, 0x0d, 0x52, 0x03, 0x69, 0x64, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x76,
+     0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
+     0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xda, 0x09, 0x0a, 0x0c, 0x54, 0x72,
+     0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x48,
+     0x0a, 0x0c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x62, 0x61,
+     0x74, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
      0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 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, 0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x5f, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69,
-     0x63, 0x52, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x6f,
-     0x6e, 0x12, 0x42, 0x0a, 0x0b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-     0x5f, 0x6c, 0x6d, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21,
+     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,
-     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,
+     0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x6e, 0x61, 0x67, 0x67, 0x72,
+     0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+     0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4d, 0x65, 0x6d,
+     0x55, 0x6e, 0x61, 0x67, 0x67, 0x12, 0x55, 0x0a, 0x14, 0x61, 0x6e, 0x64,
+     0x72, 0x6f, 0x69, 0x64, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
+     0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
      0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-     0x64, 0x50, 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,
+     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, 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, 0x53, 0x74, 0x61, 0x72, 0x74,
-     0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x0e, 0x61, 0x6e,
-     0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70,
-     0x12, 0x5b, 0x0a, 0x16, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f,
-     0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
-     0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69,
-     0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x52,
-     0x14, 0x68, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
-     0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x45, 0x0a,
-     0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
-     0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74,
-     0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65,
-     0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x13,
-     0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64,
-     0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28,
-     0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x6e, 0x73, 0x79,
-     0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d,
-     0x65, 0x73, 0x52, 0x12, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
-     0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x46,
-     0x0a, 0x0f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f,
-     0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32,
-     0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76, 0x61, 0x48, 0x65,
-     0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0d, 0x6a, 0x61, 0x76,
-     0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x55,
-     0x0a, 0x12, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x6c, 0x6d,
-     0x6b, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01,
-     0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     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, 0x4c, 0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f,
-     0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10, 0x61, 0x6e, 0x64,
-     0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f,
-     0x6e, 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, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a,
-     0x04, 0x08, 0x0e, 0x10, 0x0f, 0x42, 0x02, 0x48, 0x03}};
+     0x72, 0x6f, 0x69, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x61, 0x69,
+     0x6c, 0x73, 0x52, 0x0f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x50,
+     0x6f, 0x77, 0x72, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4e, 0x0a, 0x0f, 0x61,
+     0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
+     0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
+     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+     0x6f, 0x73, 0x2e, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74,
+     0x61, 0x72, 0x74, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52,
+     0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x53, 0x74, 0x61, 0x72,
+     0x74, 0x75, 0x70, 0x12, 0x5b, 0x0a, 0x16, 0x68, 0x65, 0x61, 0x70, 0x5f,
+     0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c,
+     0x73, 0x69, 0x74, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x25, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x70, 0x50, 0x72,
+     0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74,
+     0x65, 0x73, 0x52, 0x14, 0x68, 0x65, 0x61, 0x70, 0x50, 0x72, 0x6f, 0x66,
+     0x69, 0x6c, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x74, 0x65, 0x73,
+     0x12, 0x45, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x65,
+     0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
+     0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
+     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
+     0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x74, 0x72,
+     0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
+     0x54, 0x0a, 0x13, 0x75, 0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69,
+     0x7a, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0f,
+     0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
+     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55,
+     0x6e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46,
+     0x72, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x12, 0x75, 0x6e, 0x73, 0x79, 0x6d,
+     0x62, 0x6f, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x46, 0x72, 0x61, 0x6d, 0x65,
+     0x73, 0x12, 0x46, 0x0a, 0x0f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x68, 0x65,
+     0x61, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01,
+     0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
+     0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4a, 0x61, 0x76,
+     0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0d,
+     0x6a, 0x61, 0x76, 0x61, 0x48, 0x65, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74,
+     0x73, 0x12, 0x55, 0x0a, 0x12, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+     0x5f, 0x6c, 0x6d, 0x6b, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18,
+     0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x52, 0x65,
+     0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x10,
+     0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x4c, 0x6d, 0x6b, 0x52, 0x65,
+     0x61, 0x73, 0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x15, 0x75, 0x6e, 0x6d, 0x61,
+     0x70, 0x70, 0x65, 0x64, 0x5f, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x73, 0x79,
+     0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32,
+     0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70,
+     0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x70, 0x70,
+     0x65, 0x64, 0x4a, 0x61, 0x76, 0x61, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+     0x73, 0x52, 0x13, 0x75, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4a,
+     0x61, 0x76, 0x61, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 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, 0x0a,
+     0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e,
+     0x10, 0x0f, 0x42, 0x02, 0x48, 0x03}};
 
 }  // namespace perfetto
 
diff --git a/src/trace_processor/raw_table.cc b/src/trace_processor/raw_table.cc
index f657650..ac8127f 100644
--- a/src/trace_processor/raw_table.cc
+++ b/src/trace_processor/raw_table.cc
@@ -96,20 +96,22 @@
 }
 
 bool RawTable::ParseGfpFlags(Variadic value, base::StringWriter* writer) {
-  if (!storage_->metadata().MetadataExists(metadata::KeyIDs::system_name) ||
-      !storage_->metadata().MetadataExists(metadata::KeyIDs::system_release)) {
-    return false;
-  }
+  const auto& metadata_table = storage_->metadata_table();
 
-  const Variadic& name =
-      storage_->metadata().GetScalarMetadata(metadata::KeyIDs::system_name);
-  base::StringView system_name = storage_->GetString(name.string_value);
+  auto opt_name_idx = metadata_table.name().IndexOf(
+      metadata::kNames[metadata::KeyIDs::system_name]);
+  auto opt_release_idx = metadata_table.name().IndexOf(
+      metadata::kNames[metadata::KeyIDs::system_release]);
+  if (!opt_name_idx || !opt_release_idx)
+    return false;
+
+  StringId name = metadata_table.str_value()[*opt_name_idx];
+  base::StringView system_name = storage_->GetString(name);
   if (system_name != "Linux")
     return false;
 
-  const Variadic& release =
-      storage_->metadata().GetScalarMetadata(metadata::KeyIDs::system_release);
-  base::StringView system_release = storage_->GetString(release.string_value);
+  StringId release = metadata_table.str_value()[*opt_release_idx];
+  base::StringView system_release = storage_->GetString(release);
   auto version = ParseKernelReleaseVersion(system_release);
 
   WriteGfpFlag(value.uint_value, version, writer);
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index aebd76c..268e419 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -20,6 +20,7 @@
     "counter_tables.h",
     "macros.h",
     "macros_internal.h",
+    "metadata_tables.h",
     "profiler_tables.h",
     "slice_tables.h",
     "track_tables.h",
diff --git a/src/trace_processor/tables/metadata_tables.h b/src/trace_processor/tables/metadata_tables.h
new file mode 100644
index 0000000..9a2095d
--- /dev/null
+++ b/src/trace_processor/tables/metadata_tables.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_TABLES_METADATA_TABLES_H_
+#define SRC_TRACE_PROCESSOR_TABLES_METADATA_TABLES_H_
+
+#include "src/trace_processor/tables/macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace tables {
+
+#define PERFETTO_TP_METADATA_TABLE_DEF(NAME, PARENT, C) \
+  NAME(MetadataTable, "metadata")                       \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                     \
+  C(StringPool::Id, name)                               \
+  C(StringPool::Id, key_type)                           \
+  C(base::Optional<int64_t>, int_value)                 \
+  C(base::Optional<StringPool::Id>, str_value)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_METADATA_TABLE_DEF);
+
+}  // namespace tables
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_TABLES_METADATA_TABLES_H_
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index dbefd62..52ec6de 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -25,6 +25,7 @@
 #include "src/trace_processor/importers/json/json_trace_parser.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
 #include "src/trace_processor/importers/proto/track_event_module.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/stack_profile_tracker.h"
diff --git a/src/trace_processor/trace_processor_context.h b/src/trace_processor/trace_processor_context.h
index 2fd3c15..10e6eb4 100644
--- a/src/trace_processor/trace_processor_context.h
+++ b/src/trace_processor/trace_processor_context.h
@@ -35,6 +35,7 @@
 class FtraceModule;
 class HeapGraphTracker;
 class HeapProfileTracker;
+class MetadataTracker;
 class ProcessTracker;
 class SliceTracker;
 class TraceParser;
@@ -60,6 +61,7 @@
   std::unique_ptr<TraceSorter> sorter;
   std::unique_ptr<ChunkedTraceReader> chunk_reader;
   std::unique_ptr<HeapProfileTracker> heap_profile_tracker;
+  std::unique_ptr<MetadataTracker> metadata_tracker;
 
   // These fields are stored as pointers to Destructible objects rather than
   // their actual type (a subclass of Destructible), as the concrete subclass
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 191780b..c843df7 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -25,7 +25,6 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/args_table.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/metadata_table.h"
 #include "src/trace_processor/process_table.h"
 #include "src/trace_processor/raw_table.h"
 #include "src/trace_processor/register_additional_modules.h"
@@ -379,7 +378,6 @@
   WindowOperatorTable::RegisterTable(*db_, context_.storage.get());
   StatsTable::RegisterTable(*db_, context_.storage.get());
   RawTable::RegisterTable(*db_, context_.storage.get());
-  MetadataTable::RegisterTable(*db_, context_.storage.get());
 
   // New style db-backed tables.
   const TraceStorage* storage = context_.storage.get();
@@ -451,6 +449,9 @@
   DbSqliteTable::RegisterTable(
       *db_, &storage->vulkan_memory_allocations_table(),
       storage->vulkan_memory_allocations_table().table_name());
+
+  DbSqliteTable::RegisterTable(*db_, &storage->metadata_table(),
+                               storage->metadata_table().table_name());
 }
 
 TraceProcessorImpl::~TraceProcessorImpl() {
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 9e0726d..0ad11ee 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -26,6 +26,7 @@
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
 #include "src/trace_processor/importers/proto/track_event_module.h"
+#include "src/trace_processor/metadata_tracker.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/stack_profile_tracker.h"
@@ -46,6 +47,7 @@
   context_.process_tracker.reset(new ProcessTracker(&context_));
   context_.clock_tracker.reset(new ClockTracker(&context_));
   context_.heap_profile_tracker.reset(new HeapProfileTracker(&context_));
+  context_.metadata_tracker.reset(new MetadataTracker(&context_));
 
   context_.modules.emplace_back(new FtraceModule());
   // Ftrace module is special, because it has one extra method for parsing
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 2faa5d9..83a609b 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -38,6 +38,7 @@
 #include "src/trace_processor/stats.h"
 #include "src/trace_processor/tables/android_tables.h"
 #include "src/trace_processor/tables/counter_tables.h"
+#include "src/trace_processor/tables/metadata_tables.h"
 #include "src/trace_processor/tables/profiler_tables.h"
 #include "src/trace_processor/tables/slice_tables.h"
 #include "src/trace_processor/tables/track_tables.h"
@@ -85,6 +86,8 @@
 
 using MappingId = tables::StackProfileMappingTable::Id;
 
+using MetadataId = tables::MetadataTable::Id;
+
 // TODO(lalitm): this is a temporary hack while migrating the counters table and
 // will be removed when the migration is complete.
 static const TrackId kInvalidTrackId =
@@ -479,64 +482,6 @@
   };
   using StatsMap = std::array<Stats, stats::kNumKeys>;
 
-  class Metadata {
-   public:
-    const std::deque<metadata::KeyIDs>& keys() const { return keys_; }
-    const std::deque<Variadic>& values() const { return values_; }
-
-    uint32_t SetScalarMetadata(metadata::KeyIDs key, Variadic value) {
-      PERFETTO_DCHECK(key < metadata::kNumKeys);
-      PERFETTO_DCHECK(metadata::kKeyTypes[key] == metadata::kSingle);
-      PERFETTO_DCHECK(value.type == metadata::kValueTypes[key]);
-
-      // Already set - on release builds, overwrite the previous value.
-      auto it = scalar_indices.find(key);
-      if (it != scalar_indices.end()) {
-        PERFETTO_DFATAL("Setting a scalar metadata entry more than once.");
-        uint32_t index = static_cast<uint32_t>(it->second);
-        values_[index] = value;
-        return index;
-      }
-      // First time setting this key.
-      keys_.push_back(key);
-      values_.push_back(value);
-      uint32_t index = static_cast<uint32_t>(keys_.size() - 1);
-      scalar_indices[key] = index;
-      return index;
-    }
-
-    uint32_t AppendMetadata(metadata::KeyIDs key, Variadic value) {
-      PERFETTO_DCHECK(key < metadata::kNumKeys);
-      PERFETTO_DCHECK(metadata::kKeyTypes[key] == metadata::kMulti);
-      PERFETTO_DCHECK(value.type == metadata::kValueTypes[key]);
-
-      keys_.push_back(key);
-      values_.push_back(value);
-      return static_cast<uint32_t>(keys_.size() - 1);
-    }
-
-    const Variadic& GetScalarMetadata(metadata::KeyIDs key) const {
-      PERFETTO_DCHECK(scalar_indices.count(key) == 1);
-      return values_.at(scalar_indices.at(key));
-    }
-
-    bool MetadataExists(metadata::KeyIDs key) const {
-      return scalar_indices.count(key) >= 1;
-    }
-
-    void OverwriteMetadata(uint32_t index, Variadic value) {
-      PERFETTO_DCHECK(index < values_.size());
-      values_[index] = value;
-    }
-
-   private:
-    std::deque<metadata::KeyIDs> keys_;
-    std::deque<Variadic> values_;
-    // Extraneous state to track locations of entries that should have at most
-    // one row. Used only to maintain uniqueness during insertions.
-    std::map<metadata::KeyIDs, uint32_t> scalar_indices;
-  };
-
   UniqueTid AddEmptyThread(uint32_t tid) {
     unique_threads_.emplace_back(tid);
     return static_cast<UniqueTid>(unique_threads_.size() - 1);
@@ -592,24 +537,6 @@
     stats_[key].indexed_values[index] = value;
   }
 
-  // Example usage:
-  // SetMetadata(metadata::benchmark_name,
-  //             Variadic::String(storage->InternString("foo"));
-  // Returns the row of the new entry.
-  // Virtual for testing.
-  virtual uint32_t SetMetadata(metadata::KeyIDs key, Variadic value) {
-    return metadata_.SetScalarMetadata(key, value);
-  }
-
-  // Example usage:
-  // AppendMetadata(metadata::benchmark_story_tags,
-  //                Variadic::String(storage->InternString("bar"));
-  // Returns the row of the new entry.
-  // Virtual for testing.
-  virtual uint32_t AppendMetadata(metadata::KeyIDs key, Variadic value) {
-    return metadata_.AppendMetadata(key, value);
-  }
-
   class ScopedStatsTracer {
    public:
     ScopedStatsTracer(TraceStorage* storage, size_t key)
@@ -772,8 +699,10 @@
 
   const StatsMap& stats() const { return stats_; }
 
-  const Metadata& metadata() const { return metadata_; }
-  Metadata* mutable_metadata() { return &metadata_; }
+  const tables::MetadataTable& metadata_table() const {
+    return metadata_table_;
+  }
+  tables::MetadataTable* mutable_metadata_table() { return &metadata_table_; }
 
   const Args& args() const { return args_; }
   Args* mutable_args() { return &args_; }
@@ -925,7 +854,7 @@
   // Extra data extracted from the trace. Includes:
   // * metadata from chrome and benchmarking infrastructure
   // * descriptions of android packages
-  Metadata metadata_{};
+  tables::MetadataTable metadata_table_{&string_pool_, nullptr};
 
   // Metadata for tracks.
   tables::TrackTable track_table_{&string_pool_, nullptr};
diff --git a/test/metrics/index b/test/metrics/index
index 0175754..52edade 100644
--- a/test/metrics/index
+++ b/test/metrics/index
@@ -22,6 +22,7 @@
 heap_profile_no_symbols.textproto unsymbolized_frames unsymbolized_frames.out
 
 ../trace_processor/heap_graph.textproto java_heap_stats java_heap_stats.out
+obfuscated_heap_graph.textproto unmapped_java_symbols unmapped_java_symbols.out
 
 # Json output
 ../data/memory_counters.pb trace_metadata trace_metadata.json.out
diff --git a/test/metrics/obfuscated_heap_graph.textproto b/test/metrics/obfuscated_heap_graph.textproto
new file mode 100644
index 0000000..4391067
--- /dev/null
+++ b/test/metrics/obfuscated_heap_graph.textproto
@@ -0,0 +1,61 @@
+packet {
+  process_tree {
+    processes {
+      pid: 1
+      ppid: 0
+      cmdline: "init"
+      uid: 0
+    }
+    processes {
+      pid: 2
+      ppid: 1
+      cmdline: "com.google.android.gm"
+      uid: 10001
+    }
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  timestamp: 10
+  heap_graph {
+    pid: 2
+    roots {
+      root_type: ROOT_JAVA_FRAME
+      object_ids: 0x01
+    }
+    objects {
+      id: 0x01
+      type_id: 1
+      self_size: 64
+      reference_field_id: 1
+      reference_object_id: 0x02
+    }
+    objects {
+      id: 0x02
+      type_id: 2
+      self_size: 32
+    }
+    continued: true
+    index: 1
+  }
+}
+packet {
+  trusted_packet_sequence_id: 999
+  heap_graph {
+    pid: 2
+    type_names {
+      iid: 1
+      str: "FactoryProducerDelegateImplActor"
+    }
+    type_names {
+      iid: 2
+      str: "a"
+    }
+    field_names {
+      iid: 1
+      str: "FactoryProducerDelegateImplActor.a"
+    }
+    continued: false
+    index: 2
+  }
+}
diff --git a/test/metrics/unmapped_java_symbols.out b/test/metrics/unmapped_java_symbols.out
new file mode 100644
index 0000000..b095d42
--- /dev/null
+++ b/test/metrics/unmapped_java_symbols.out
@@ -0,0 +1,11 @@
+unmapped_java_symbols {
+  process_symbols {
+    process_metadata {
+      name: "com.google.android.gm"
+      uid: 10001
+    }
+    type_name: "FactoryProducerDelegateImplActor"
+    type_name: "a"
+    field_name: "FactoryProducerDelegateImplActor.a"
+  }
+}
diff --git a/tools/dev_server b/tools/dev_server
index 7188f38..bd74df7 100755
--- a/tools/dev_server
+++ b/tools/dev_server
@@ -20,6 +20,7 @@
 import argparse
 import socket
 import subprocess
+import threading
 
 try:
   import socketserver
@@ -43,6 +44,7 @@
     self.port = port
     self.directory = directory
     self.rebuilder = rebuilder
+    self.lock = threading.Lock()
 
   def serve(self):
     this = self
@@ -60,7 +62,8 @@
 
       def do_GET(self):
         try:
-          this.rebuilder.rebuild_if_needed()
+          with this.lock:
+            this.rebuilder.rebuild_if_needed()
         except RebuildFailed as e:
           self.send_response(200)
           self.send_header("Content-type", "text/html")
@@ -90,6 +93,7 @@
     self.command = command
     self.ignored_paths = ignored_paths
     self.last_disk_state = None
+    self.abs_ignored_paths = [os.path.abspath(p) for p in self.ignored_paths]
 
   def rebuild_if_needed(self):
     if self.last_disk_state == self.last_modified_time():
@@ -103,7 +107,8 @@
   def last_modified_time(self):
     start_time = time.time()
     max_mtime = 0
-    for dirpath, dirnames, filenames in os.walk('.', topdown=True):
+    for dirpath, dirnames, filenames in os.walk(
+        os.path.abspath('.'), topdown=True):
       dirnames[:] = [
           n for n in dirnames
           if not self.should_ignore_path(os.path.join(dirpath, n))
@@ -119,8 +124,7 @@
     return max_mtime
 
   def should_ignore_path(self, path):
-    return os.path.abspath(path) in (
-        os.path.abspath(p) for p in self.ignored_paths)
+    return path in self.abs_ignored_paths
 
   def rebuild(self):
     print('Running command: {}'.format(self.command))
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index d8a8f7f..5cfece7 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -99,6 +99,8 @@
     'protos/perfetto/trace/track_event/chrome_histogram_sample.proto',
     'protos/perfetto/trace/track_event/chrome_keyed_service.proto',
     'protos/perfetto/trace/track_event/chrome_legacy_ipc.proto',
+    'protos/perfetto/trace/track_event/chrome_process_descriptor.proto',
+    'protos/perfetto/trace/track_event/chrome_thread_descriptor.proto',
     'protos/perfetto/trace/track_event/chrome_user_event.proto',
     'protos/perfetto/trace/track_event/debug_annotation.proto',
     'protos/perfetto/trace/track_event/log_message.proto',
diff --git a/ui/run-dev-server b/ui/run-dev-server
index 11a602e..9faf567 100755
--- a/ui/run-dev-server
+++ b/ui/run-dev-server
@@ -39,6 +39,7 @@
   -i $ROOT_DIR/src/traced \
   -i $ROOT_DIR/buildtools \
   -i $ROOT_DIR/out \
+  -i $ROOT_DIR/infra \
   -i $ROOT_DIR/ui/node_modules \
   -s $UI_OUT_DIR \
   "$ROOT_DIR/tools/ninja -C $OUT_DIR ui"
diff --git a/ui/src/controller/heap_profile_controller.ts b/ui/src/controller/heap_profile_controller.ts
index a556b92..973a92f 100644
--- a/ui/src/controller/heap_profile_controller.ts
+++ b/ui/src/controller/heap_profile_controller.ts
@@ -256,7 +256,7 @@
          from stack_profile_callsite cs
          join stack_profile_frame fr on cs.frame_id = fr.id
          join stack_profile_mapping map on fr.mapping = map.id
-         inner join (
+         left join (
               select symbol_set_id, FIRST_VALUE(name) OVER(PARTITION BY
                 symbol_set_id) as name
               from stack_profile_symbol GROUP BY symbol_set_id