Add memory snapshot processing to Perfetto TP

The trace processor parses the proto-encoded memory dump, converts it into the GlobalNodeGraph using GraphProcessor and emits rows into the tables. See crbug.com/1116359 for more details.

Bug: 1116359
Change-Id: I95e6af67265f68022ba5b244ca098ae594333b7b
diff --git a/Android.bp b/Android.bp
index 7f117c7..4a52aad 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7366,6 +7366,8 @@
     "src/trace_processor/importers/proto/args_table_utils.cc",
     "src/trace_processor/importers/proto/async_track_set_tracker.cc",
     "src/trace_processor/importers/proto/heap_profile_tracker.cc",
+    "src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
+    "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
     "src/trace_processor/importers/proto/metadata_tracker.cc",
     "src/trace_processor/importers/proto/packet_sequence_state.cc",
     "src/trace_processor/importers/proto/profile_module.cc",
@@ -8572,6 +8574,7 @@
     ":perfetto_include_perfetto_base_base",
     ":perfetto_include_perfetto_ext_base_base",
     ":perfetto_include_perfetto_ext_trace_processor_export_json",
+    ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
     ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
     ":perfetto_include_perfetto_protozero_protozero",
     ":perfetto_include_perfetto_trace_processor_basic_types",
@@ -8615,6 +8618,7 @@
     ":perfetto_src_trace_processor_export_json",
     ":perfetto_src_trace_processor_ftrace_descriptors",
     ":perfetto_src_trace_processor_importers_common",
+    ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
     ":perfetto_src_trace_processor_lib",
     ":perfetto_src_trace_processor_metatrace",
     ":perfetto_src_trace_processor_metrics_lib",
@@ -8704,6 +8708,7 @@
     ":perfetto_include_perfetto_base_base",
     ":perfetto_include_perfetto_ext_base_base",
     ":perfetto_include_perfetto_ext_trace_processor_export_json",
+    ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
     ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
     ":perfetto_include_perfetto_profiling_deobfuscator",
     ":perfetto_include_perfetto_profiling_pprof_builder",
@@ -8751,6 +8756,7 @@
     ":perfetto_src_trace_processor_export_json",
     ":perfetto_src_trace_processor_ftrace_descriptors",
     ":perfetto_src_trace_processor_importers_common",
+    ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
     ":perfetto_src_trace_processor_lib",
     ":perfetto_src_trace_processor_metatrace",
     ":perfetto_src_trace_processor_metrics_lib",
diff --git a/BUILD b/BUILD
index da6b0ae..5dd6713 100644
--- a/BUILD
+++ b/BUILD
@@ -339,6 +339,19 @@
     ],
 )
 
+# GN target: //include/perfetto/ext/trace_processor/importers/memory_tracker:memory_tracker
+filegroup(
+    name = "include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
+    srcs = [
+        "include/perfetto/ext/trace_processor/importers/memory_tracker/graph.h",
+        "include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h",
+        "include/perfetto/ext/trace_processor/importers/memory_tracker/memory_allocator_node_id.h",
+        "include/perfetto/ext/trace_processor/importers/memory_tracker/memory_graph_edge.h",
+        "include/perfetto/ext/trace_processor/importers/memory_tracker/raw_memory_graph_node.h",
+        "include/perfetto/ext/trace_processor/importers/memory_tracker/raw_process_memory_node.h",
+    ],
+)
+
 # GN target: //include/perfetto/ext/trace_processor:export_json
 filegroup(
     name = "include_perfetto_ext_trace_processor_export_json",
@@ -754,6 +767,18 @@
     ],
 )
 
+# GN target: //src/trace_processor/importers/memory_tracker:graph_processor
+filegroup(
+    name = "src_trace_processor_importers_memory_tracker_graph_processor",
+    srcs = [
+        "src/trace_processor/importers/memory_tracker/graph.cc",
+        "src/trace_processor/importers/memory_tracker/graph_processor.cc",
+        "src/trace_processor/importers/memory_tracker/memory_allocator_node_id.cc",
+        "src/trace_processor/importers/memory_tracker/raw_memory_graph_node.cc",
+        "src/trace_processor/importers/memory_tracker/raw_process_memory_node.cc",
+    ],
+)
+
 # GN target: //src/trace_processor/importers:common
 filegroup(
     name = "src_trace_processor_importers_common",
@@ -1166,6 +1191,10 @@
         "src/trace_processor/importers/proto/async_track_set_tracker.h",
         "src/trace_processor/importers/proto/heap_profile_tracker.cc",
         "src/trace_processor/importers/proto/heap_profile_tracker.h",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h",
         "src/trace_processor/importers/proto/metadata_tracker.cc",
         "src/trace_processor/importers/proto/metadata_tracker.h",
         "src/trace_processor/importers/proto/packet_sequence_state.cc",
@@ -3061,6 +3090,7 @@
         ":src_trace_processor_export_json",
         ":src_trace_processor_ftrace_descriptors",
         ":src_trace_processor_importers_common",
+        ":src_trace_processor_importers_memory_tracker_graph_processor",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_lib",
@@ -3079,6 +3109,7 @@
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
         ":include_perfetto_ext_trace_processor_export_json",
+        ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
         ":include_perfetto_protozero_protozero",
         ":include_perfetto_trace_processor_basic_types",
@@ -3141,6 +3172,7 @@
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
         ":include_perfetto_ext_trace_processor_export_json",
+        ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
         ":include_perfetto_protozero_protozero",
         ":include_perfetto_trace_processor_basic_types",
@@ -3154,6 +3186,7 @@
         ":src_trace_processor_export_json",
         ":src_trace_processor_ftrace_descriptors",
         ":src_trace_processor_importers_common",
+        ":src_trace_processor_importers_memory_tracker_graph_processor",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_lib",
@@ -3310,6 +3343,7 @@
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
         ":include_perfetto_ext_trace_processor_export_json",
+        ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
         ":include_perfetto_profiling_deobfuscator",
         ":include_perfetto_profiling_pprof_builder",
@@ -3326,6 +3360,7 @@
         ":src_trace_processor_export_json",
         ":src_trace_processor_ftrace_descriptors",
         ":src_trace_processor_importers_common",
+        ":src_trace_processor_importers_memory_tracker_graph_processor",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_lib",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 448e482..b6641ab 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -92,6 +92,10 @@
     "importers/proto/async_track_set_tracker.h",
     "importers/proto/heap_profile_tracker.cc",
     "importers/proto/heap_profile_tracker.h",
+    "importers/proto/memory_tracker_snapshot_module.cc",
+    "importers/proto/memory_tracker_snapshot_module.h",
+    "importers/proto/memory_tracker_snapshot_parser.cc",
+    "importers/proto/memory_tracker_snapshot_parser.h",
     "importers/proto/metadata_tracker.cc",
     "importers/proto/metadata_tracker.h",
     "importers/proto/packet_sequence_state.cc",
@@ -139,6 +143,7 @@
     "../protozero",
     "containers",
     "importers:common",
+    "importers/memory_tracker:graph_processor",
     "storage",
     "tables",
     "types",
diff --git a/src/trace_processor/importers/common/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h
index 7b051a2..4b17787 100644
--- a/src/trace_processor/importers/common/args_tracker.h
+++ b/src/trace_processor/importers/common/args_tracker.h
@@ -112,7 +112,7 @@
     return AddArgsTo(context_->storage->mutable_flow_table(), id);
   }
 
-  BoundInserter AddArgsTo(SnapshotNodeId id) {
+  BoundInserter AddArgsTo(tables::MemorySnapshotNodeTable::Id id) {
     return AddArgsTo(context_->storage->mutable_memory_snapshot_node_table(),
                      id);
   }
diff --git a/src/trace_processor/importers/default_modules.cc b/src/trace_processor/importers/default_modules.cc
index 2704478..88fa696 100644
--- a/src/trace_processor/importers/default_modules.cc
+++ b/src/trace_processor/importers/default_modules.cc
@@ -16,6 +16,7 @@
 
 #include "src/trace_processor/importers/default_modules.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h"
 #include "src/trace_processor/importers/proto/profile_module.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/importers/proto/track_event_module.h"
@@ -30,6 +31,7 @@
   context->ftrace_module =
       static_cast<FtraceModule*>(context->modules.back().get());
 
+  context->modules.emplace_back(new MemoryTrackerSnapshotModule(context));
   context->modules.emplace_back(new TrackEventModule(context));
   context->modules.emplace_back(new ProfileModule(context));
 }
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc
new file mode 100644
index 0000000..cd4dde2
--- /dev/null
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using perfetto::protos::pbzero::TracePacket;
+
+MemoryTrackerSnapshotModule::MemoryTrackerSnapshotModule(
+    TraceProcessorContext* context)
+    : parser_(context) {
+  RegisterForField(TracePacket::kMemoryTrackerSnapshotFieldNumber, context);
+}
+
+MemoryTrackerSnapshotModule::~MemoryTrackerSnapshotModule() = default;
+
+void MemoryTrackerSnapshotModule::ParsePacket(
+    const TracePacket::Decoder& decoder,
+    const TimestampedTracePiece& ttp,
+    uint32_t field_id) {
+  switch (field_id) {
+    case TracePacket::kMemoryTrackerSnapshotFieldNumber:
+      parser_.ParseMemoryTrackerSnapshot(ttp.timestamp,
+                                         decoder.memory_tracker_snapshot());
+      return;
+  }
+}
+
+void MemoryTrackerSnapshotModule::NotifyEndOfFile() {
+  parser_.NotifyEndOfFile();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_module.h b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.h
new file mode 100644
index 0000000..730b7e3
--- /dev/null
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.h
@@ -0,0 +1,48 @@
+/*
+ * 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_IMPORTERS_PROTO_MEMORY_TRACKER_SNAPSHOT_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_MEMORY_TRACKER_SNAPSHOT_MODULE_H_
+
+#include "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
+
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class MemoryTrackerSnapshotModule : public ProtoImporterModule {
+ public:
+  explicit MemoryTrackerSnapshotModule(TraceProcessorContext* context);
+
+  ~MemoryTrackerSnapshotModule() override;
+
+  void ParsePacket(const protos::pbzero::TracePacket::Decoder&,
+                   const TimestampedTracePiece&,
+                   uint32_t field_id) override;
+
+  void NotifyEndOfFile() override;
+
+ private:
+  MemoryTrackerSnapshotParser parser_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_MEMORY_TRACKER_SNAPSHOT_MODULE_H_
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc
new file mode 100644
index 0000000..6c735f9
--- /dev/null
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+#include "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h"
+
+#include "perfetto/ext/base/string_view.h"
+#include "protos/perfetto/trace/memory_graph.pbzero.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/tables/memory_tables.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+MemoryTrackerSnapshotParser::MemoryTrackerSnapshotParser(
+    TraceProcessorContext* context)
+    : context_(context),
+      level_of_detail_ids_{{context_->storage->InternString("detailed"),
+                            context_->storage->InternString("light"),
+                            context_->storage->InternString("background")}},
+      unit_ids_{{context_->storage->InternString("objects"),
+                 context_->storage->InternString("bytes")}},
+      aggregate_raw_nodes_(),
+      last_snapshot_timestamp_(-1),
+      last_snapshot_level_of_detail_(LevelOfDetail::kFirst) {}
+
+void MemoryTrackerSnapshotParser::ParseMemoryTrackerSnapshot(int64_t ts,
+                                                             ConstBytes blob) {
+  PERFETTO_DCHECK(last_snapshot_timestamp_ <= ts);
+  if (!aggregate_raw_nodes_.empty() && ts != last_snapshot_timestamp_) {
+    GenerateGraphFromRawNodesAndEmitRows();
+  }
+  ReadProtoSnapshot(blob, aggregate_raw_nodes_, last_snapshot_level_of_detail_);
+  last_snapshot_timestamp_ = ts;
+}
+
+void MemoryTrackerSnapshotParser::NotifyEndOfFile() {
+  if (!aggregate_raw_nodes_.empty()) {
+    GenerateGraphFromRawNodesAndEmitRows();
+  }
+}
+
+void MemoryTrackerSnapshotParser::ReadProtoSnapshot(
+    ConstBytes blob,
+    RawMemoryNodeMap& raw_nodes,
+    LevelOfDetail& level_of_detail) {
+  protos::pbzero::MemoryTrackerSnapshot::Decoder snapshot(blob.data, blob.size);
+  level_of_detail = LevelOfDetail::kDetailed;
+
+  switch (snapshot.level_of_detail()) {
+    case 0:  // FULL
+      level_of_detail = LevelOfDetail::kDetailed;
+      break;
+    case 1:  // LIGHT
+      level_of_detail = LevelOfDetail::kLight;
+      break;
+    case 2:  // BACKGROUND
+      level_of_detail = LevelOfDetail::kBackground;
+      break;
+  }
+
+  for (auto process_it = snapshot.process_memory_dumps(); process_it;
+       ++process_it) {
+    protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::Decoder
+        process_memory_dump(*process_it);
+
+    base::PlatformProcessId pid =
+        static_cast<base::PlatformProcessId>(process_memory_dump.pid());
+
+    RawProcessMemoryNode::MemoryNodesMap nodes_map;
+    RawProcessMemoryNode::AllocatorNodeEdgesMap edges_map;
+
+    for (auto node_it = process_memory_dump.allocator_dumps(); node_it;
+         ++node_it) {
+      protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::MemoryNode::
+          Decoder node(*node_it);
+
+      MemoryAllocatorNodeId node_id(node.id());
+      const std::string absolute_name = node.absolute_name().ToStdString();
+      int flags;
+      if (node.weak()) {
+        flags = RawMemoryGraphNode::kWeak;
+      } else {
+        flags = RawMemoryGraphNode::kDefault;
+      }
+
+      std::vector<RawMemoryGraphNode::MemoryNodeEntry> entries;
+
+      if (node.has_size_bytes()) {
+        entries.emplace_back("size", RawMemoryGraphNode::kUnitsBytes,
+                             node.size_bytes());
+      }
+
+      for (auto entry_it = node.entries(); entry_it; ++entry_it) {
+        protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::MemoryNode::
+            MemoryNodeEntry::Decoder entry(*entry_it);
+
+        std::string unit;
+
+        switch (entry.units()) {
+          case 1:  // BYTES
+            unit = RawMemoryGraphNode::kUnitsBytes;
+            break;
+          case 2:  // COUNT
+            unit = RawMemoryGraphNode::kUnitsObjects;
+            break;
+        }
+        if (entry.has_value_uint64()) {
+          entries.emplace_back(entry.name().ToStdString(), unit,
+                               entry.value_uint64());
+        } else if (entry.has_value_string()) {
+          entries.emplace_back(entry.name().ToStdString(), unit,
+                               entry.value_string().ToStdString());
+        } else {
+          context_->storage->IncrementStats(
+              stats::memory_snapshot_parser_failure);
+        }
+      }
+      std::unique_ptr<RawMemoryGraphNode> raw_graph_node(new RawMemoryGraphNode(
+          absolute_name, level_of_detail, node_id, std::move(entries)));
+      raw_graph_node->set_flags(flags);
+      nodes_map.emplace(
+          std::make_pair(absolute_name, std::move(raw_graph_node)));
+    }
+
+    for (auto edge_it = process_memory_dump.memory_edges(); edge_it;
+         ++edge_it) {
+      protos::pbzero::MemoryTrackerSnapshot::ProcessSnapshot::MemoryEdge::
+          Decoder edge(*edge_it);
+
+      std::unique_ptr<MemoryGraphEdge> graph_edge(new MemoryGraphEdge(
+          MemoryAllocatorNodeId(edge.source_id()),
+          MemoryAllocatorNodeId(edge.target_id()),
+          static_cast<int>(edge.importance()), edge.overridable()));
+
+      edges_map.emplace(std::make_pair(MemoryAllocatorNodeId(edge.source_id()),
+                                       std::move(graph_edge)));
+    }
+    raw_nodes.emplace(
+        pid, new RawProcessMemoryNode(level_of_detail, std::move(edges_map),
+                                      std::move(nodes_map)));
+  }
+}
+
+std::unique_ptr<GlobalNodeGraph> MemoryTrackerSnapshotParser::GenerateGraph(
+    RawMemoryNodeMap& raw_nodes) {
+  auto graph = GraphProcessor::CreateMemoryGraph(raw_nodes);
+  GraphProcessor::CalculateSizesForGraph(graph.get());
+  return graph;
+}
+
+void MemoryTrackerSnapshotParser::EmitRows(int64_t ts,
+                                           GlobalNodeGraph& graph,
+                                           LevelOfDetail level_of_detail) {
+  IdNodeMap id_node_table;
+
+  // For now, we use the existing global instant event track for chrome events,
+  // since memory dumps are global.
+  TrackId track_id =
+      context_->track_tracker->GetOrCreateLegacyChromeGlobalInstantTrack();
+
+  tables::MemorySnapshotTable::Row snapshot_row(
+      ts, track_id, level_of_detail_ids_[static_cast<size_t>(level_of_detail)]);
+  tables::MemorySnapshotTable::Id snapshot_row_id =
+      context_->storage->mutable_memory_snapshot_table()
+          ->Insert(snapshot_row)
+          .id;
+
+  for (auto const& it_process : graph.process_node_graphs()) {
+    tables::ProcessMemorySnapshotTable::Row process_row;
+    process_row.upid = context_->process_tracker->GetOrCreateProcess(
+        static_cast<uint32_t>(it_process.first));
+    process_row.snapshot_id = snapshot_row_id;
+    tables::ProcessMemorySnapshotTable::Id proc_snapshot_row_id =
+        context_->storage->mutable_process_memory_snapshot_table()
+            ->Insert(process_row)
+            .id;
+    EmitMemorySnapshotNodeRows(*(it_process.second->root()),
+                               proc_snapshot_row_id, id_node_table);
+  }
+
+  // For each snapshot nodes from shared_memory_graph will be associated
+  // with a fabricated process_memory_snapshot entry whose pid == 0.
+  // TODO(mobica-google-contributors@mobica.com): Track the shared memory graph
+  // in a separate table.
+  tables::ProcessMemorySnapshotTable::Row fake_process_row;
+  fake_process_row.upid = context_->process_tracker->GetOrCreateProcess(0u);
+  fake_process_row.snapshot_id = snapshot_row_id;
+  tables::ProcessMemorySnapshotTable::Id fake_proc_snapshot_row_id =
+      context_->storage->mutable_process_memory_snapshot_table()
+          ->Insert(fake_process_row)
+          .id;
+  EmitMemorySnapshotNodeRows(*(graph.shared_memory_graph()->root()),
+                             fake_proc_snapshot_row_id, id_node_table);
+
+  for (const auto& it_edge : graph.edges()) {
+    tables::MemorySnapshotEdgeTable::Row edge_row;
+    edge_row.source_node_id = static_cast<tables::MemorySnapshotNodeTable::Id>(
+        id_node_table.find(it_edge.source()->id())->second);
+    edge_row.target_node_id = static_cast<tables::MemorySnapshotNodeTable::Id>(
+        id_node_table.find(it_edge.target()->id())->second);
+    edge_row.importance = static_cast<uint32_t>(it_edge.priority());
+    context_->storage->mutable_memory_snapshot_edge_table()->Insert(edge_row);
+  }
+}
+
+void MemoryTrackerSnapshotParser::EmitMemorySnapshotNodeRows(
+    GlobalNodeGraph::Node& root_node_graph,
+    ProcessMemorySnapshotId& proc_snapshot_row_id,
+    IdNodeMap& id_node_map) {
+  EmitMemorySnapshotNodeRowsRecursively(root_node_graph, std::string(),
+                                        base::nullopt, proc_snapshot_row_id,
+                                        id_node_map);
+}
+
+void MemoryTrackerSnapshotParser::EmitMemorySnapshotNodeRowsRecursively(
+    GlobalNodeGraph::Node& node,
+    const std::string& path,
+    base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+    ProcessMemorySnapshotId& proc_snapshot_row_id,
+    IdNodeMap& id_node_map) {
+  base::Optional<tables::MemorySnapshotNodeTable::Id> node_id;
+  // Skip emitting the root node into the tables - it is not a real node.
+  if (!path.empty()) {
+    node_id = EmitNode(node, path, parent_node_row_id, proc_snapshot_row_id,
+                       id_node_map);
+  }
+
+  for (const auto& name_and_child : *node.children()) {
+    std::string child_path = path;
+    if (!child_path.empty())
+      child_path += "/";
+    child_path += name_and_child.first;
+
+    EmitMemorySnapshotNodeRowsRecursively(*(name_and_child.second), child_path,
+                                          /*parent_node_id=*/node_id,
+                                          proc_snapshot_row_id, id_node_map);
+  }
+}
+
+base::Optional<tables::MemorySnapshotNodeTable::Id>
+MemoryTrackerSnapshotParser::EmitNode(
+    const GlobalNodeGraph::Node& node,
+    const std::string& path,
+    base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+    ProcessMemorySnapshotId& proc_snapshot_row_id,
+    IdNodeMap& id_node_map) {
+  tables::MemorySnapshotNodeTable::Row node_row;
+  node_row.process_snapshot_id = proc_snapshot_row_id;
+  node_row.parent_node_id = parent_node_row_id;
+  node_row.path = context_->storage->InternString(base::StringView(path));
+
+  tables::MemorySnapshotNodeTable::Id node_row_id =
+      context_->storage->mutable_memory_snapshot_node_table()
+          ->Insert(node_row)
+          .id;
+
+  auto* node_table = context_->storage->mutable_memory_snapshot_node_table();
+  uint32_t node_row_index =
+      static_cast<uint32_t>(*node_table->id().IndexOf(node_row_id));
+  ArgsTracker::BoundInserter args =
+      context_->args_tracker->AddArgsTo(node_row_id);
+
+  for (const auto& entry : node.const_entries()) {
+    switch (entry.second.type) {
+      case GlobalNodeGraph::Node::Entry::Type::kUInt64: {
+        int64_t value_int = static_cast<int64_t>(entry.second.value_uint64);
+
+        if (entry.first == "size") {
+          node_table->mutable_size()->Set(node_row_index, value_int);
+        } else if (entry.first == "effective_size") {
+          node_table->mutable_effective_size()->Set(node_row_index, value_int);
+        } else {
+          args.AddArg(context_->storage->InternString(
+                          base::StringView(entry.first + ".value")),
+                      Variadic::Integer(value_int));
+          if (entry.second.units < unit_ids_.size()) {
+            args.AddArg(context_->storage->InternString(
+                            base::StringView(entry.first + ".unit")),
+                        Variadic::String(unit_ids_[entry.second.units]));
+          }
+        }
+        break;
+      }
+      case GlobalNodeGraph::Node::Entry::Type::kString: {
+        args.AddArg(context_->storage->InternString(
+                        base::StringView(entry.first + ".value")),
+                    Variadic::String(context_->storage->InternString(
+                        base::StringView(entry.second.value_string))));
+        break;
+      }
+    }
+  }
+  id_node_map.emplace(std::make_pair(node.id(), node_row_id));
+  return node_row_id;
+}
+
+void MemoryTrackerSnapshotParser::GenerateGraphFromRawNodesAndEmitRows() {
+  std::unique_ptr<GlobalNodeGraph> graph = GenerateGraph(aggregate_raw_nodes_);
+  EmitRows(last_snapshot_timestamp_, *(graph.get()),
+           last_snapshot_level_of_detail_);
+  aggregate_raw_nodes_.clear();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h
new file mode 100644
index 0000000..32119c2
--- /dev/null
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h
@@ -0,0 +1,122 @@
+/*
+ * 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_IMPORTERS_PROTO_MEMORY_TRACKER_SNAPSHOT_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_MEMORY_TRACKER_SNAPSHOT_PARSER_H_
+
+#include "perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h"
+#include "protos/perfetto/trace/memory_graph.pbzero.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class MemoryTrackerSnapshotParser {
+ public:
+  explicit MemoryTrackerSnapshotParser(TraceProcessorContext* context);
+  void ParseMemoryTrackerSnapshot(int64_t ts, protozero::ConstBytes blob);
+
+  void NotifyEndOfFile();
+
+ private:
+  using RawMemoryNodeMap =
+      std::map<base::PlatformProcessId, std::unique_ptr<RawProcessMemoryNode>>;
+  using IdNodeMap =
+      std::map<MemoryAllocatorNodeId, tables::MemorySnapshotNodeTable::Id>;
+  using ConstBytes = protozero::ConstBytes;
+
+  class ChildNode {
+   public:
+    ChildNode() : table_index_(-1) {}
+    GlobalNodeGraph::Node* node_;
+    std::string path_;
+    uint64_t size_;
+    uint64_t effective_size_;
+    int32_t table_index_;
+  };
+
+  // Reads the proto-encoded memory snapshot of a process (message
+  // MemoryTrackerSnapshot) in given |blob| in order to get:
+  // - map of RawProcessMemoryNode containers |raw_nodes|. It
+  //   is need to generates GlobalNodeGraph via GraphProcessor.
+  // - level of detail of the memory graph |level_of_detail|.
+  void ReadProtoSnapshot(ConstBytes blob,
+                         RawMemoryNodeMap& raw_nodes,
+                         LevelOfDetail& level_of_detail);
+
+  // Generates GlobalNodeGraph via GraphProcessor for given map |raw_nodes|.
+  std::unique_ptr<GlobalNodeGraph> GenerateGraph(RawMemoryNodeMap& raw_nodes);
+
+  // Fills out MemorySnapshotTable, ProcessMemorySnapshotTable,
+  // MemorySnapshotNodeTable, MemorySnapshotEdgeTable with given
+  // timestamp |ts|, |graph|, |level_of_detail|.
+  void EmitRows(int64_t ts,
+                GlobalNodeGraph& graph,
+                LevelOfDetail level_of_detail);
+
+  // Fills out MemorySnapshotNodeTable for given root node
+  // |root_node_graph| and ProcessMemorySnapshotId |proc_snapshot_row_id|.
+  // Generates map of MemoryAllocatorNodeId and MemorySnapshotNodeTable::Id
+  // |id_node_map| which is used at time of filling out of
+  // MemorySnapshotEdgeTable.
+  void EmitMemorySnapshotNodeRows(GlobalNodeGraph::Node& root_node_graph,
+                                  ProcessMemorySnapshotId& proc_snapshot_row_id,
+                                  IdNodeMap& id_node_map);
+
+  // Recursively traverses through list of children of |node| to generate full
+  // |path| to every node in MemorySnapshotNodeTable for given
+  // ProcessMemorySnapshotId |proc_snapshot_row_id|.
+  // Generates map of MemoryAllocatorNodeId and MemorySnapshotNodeTable::Id
+  // |id_node_map| which is used at time of filling out of
+  // MemorySnapshotEdgeTable.
+  void EmitMemorySnapshotNodeRowsRecursively(
+      GlobalNodeGraph::Node& node,
+      const std::string&,
+      base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+      ProcessMemorySnapshotId& proc_snapshot_row_id,
+      IdNodeMap& id_node_map);
+
+  // Fills out MemorySnapshotNodeTable for given Node
+  // |node|, |path|, MemorySnapshotNodeTable::Id |parent_node_row_id| and
+  // ProcessMemorySnapshotId |proc_snapshot_row_id|. Generates map of
+  // MemoryAllocatorNodeId and MemorySnapshotNodeTable::Id |id_node_map| which
+  // is used at time of filling out of MemorySnapshotEdgeTable.
+  base::Optional<tables::MemorySnapshotNodeTable::Id> EmitNode(
+      const GlobalNodeGraph::Node& node,
+      const std::string& path,
+      base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+      ProcessMemorySnapshotId& proc_snapshot_row_id,
+      IdNodeMap& id_node_map);
+
+  void GenerateGraphFromRawNodesAndEmitRows();
+
+  TraceProcessorContext* context_;
+  std::array<StringId, 3> level_of_detail_ids_;
+  std::array<StringId, 2> unit_ids_;
+  RawMemoryNodeMap aggregate_raw_nodes_;
+  int64_t last_snapshot_timestamp_;
+  LevelOfDetail last_snapshot_level_of_detail_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_MEMORY_TRACKER_SNAPSHOT_PARSER_H_
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 3a08c4a..b93af99 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -798,7 +798,6 @@
       context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
       continue;
     }
-
   }
 }
 
@@ -833,7 +832,18 @@
         {upid, ts, context_->storage->InternString(e.path()),
          static_cast<int64_t>(e.size_kb()),
          static_cast<int64_t>(e.private_dirty_kb()),
-         static_cast<int64_t>(e.swap_kb())});
+         static_cast<int64_t>(e.swap_kb()),
+         context_->storage->InternString(e.file_name()),
+         static_cast<int64_t>(e.start_address()),
+         static_cast<int64_t>(e.module_timestamp()),
+         context_->storage->InternString(e.module_debugid()),
+         context_->storage->InternString(e.module_debug_path()),
+         static_cast<int32_t>(e.protection_flags()),
+         static_cast<int64_t>(e.private_clean_resident_kb()),
+         static_cast<int64_t>(e.shared_dirty_resident_kb()),
+         static_cast<int64_t>(e.shared_clean_resident_kb()),
+         static_cast<int64_t>(e.locked_kb()),
+         static_cast<int64_t>(e.proportional_resident_kb())});
   }
 }
 
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 96b14ec..d81e335 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -69,6 +69,12 @@
       cpu_times_softirq_ns_id_(
           context->storage->InternString("cpu.times.softirq_ns")),
       oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
+      is_peak_rss_resettable_id_(
+          context->storage->InternString("is_peak_rss_resettable")),
+      chrome_private_footprint_kb_(
+          context->storage->InternString("chrome.private_footprint_kb")),
+      chrome_peak_resident_set_kb_(
+          context->storage->InternString("chrome.peak_resident_set_kb")),
       thread_time_in_state_id_(context->storage->InternString("time_in_state")),
       thread_time_in_state_cpu_id_(
           context_->storage->InternString("time_in_state_cpu_id")),
@@ -99,6 +105,12 @@
       context->storage->InternString("mem.rss.watermark");
   proc_stats_process_names_[ProcessStats::Process::kOomScoreAdjFieldNumber] =
       oom_score_adj_id_;
+  proc_stats_process_names_
+      [ProcessStats::Process::kChromePrivateFootprintKbFieldNumber] =
+          chrome_private_footprint_kb_;
+  proc_stats_process_names_
+      [ProcessStats::Process::kChromePeakResidentSetKbFieldNumber] =
+          chrome_peak_resident_set_kb_;
 }
 
 void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) {
@@ -281,11 +293,19 @@
 
     protozero::ProtoDecoder proc(*it);
     uint32_t pid = 0;
+    bool is_peak_rss_resettable = false;
+    bool has_peak_rrs_resettable = false;
     for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) {
       if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) {
         pid = fld.as_uint32();
         continue;
       }
+      if (fld.id() == protos::pbzero::ProcessStats::Process::
+                          kIsPeakRssResettableFieldNumber) {
+        is_peak_rss_resettable = fld.as_bool();
+        has_peak_rrs_resettable = true;
+        continue;
+      }
       if (fld.id() ==
           protos::pbzero::ProcessStats::Process::kThreadsFieldNumber) {
         if (PERFETTO_UNLIKELY(ms_per_tick_ == 0 ||
@@ -311,18 +331,27 @@
       }
     }
 
+    if (has_peak_rrs_resettable) {
+      UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
+      context_->process_tracker->AddArgsTo(upid).AddArg(
+          is_peak_rss_resettable_id_,
+          Variadic::Boolean(is_peak_rss_resettable));
+    }
     // Skip field_id 0 (invalid) and 1 (pid).
     for (size_t field_id = 2; field_id < counter_values.size(); field_id++) {
-      if (!has_counter[field_id])
+      if (!has_counter[field_id] || field_id ==
+                                        protos::pbzero::ProcessStats::Process::
+                                            kIsPeakRssResettableFieldNumber) {
         continue;
+      }
 
       // Lookup the interned string id from the field name using the
       // pre-cached |proc_stats_process_names_| map.
       StringId name = proc_stats_process_names_[field_id];
-      int64_t value = counter_values[field_id];
       UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
       TrackId track =
           context_->track_tracker->InternProcessCounterTrack(name, upid);
+      int64_t value = counter_values[field_id];
       context_->event_tracker->PushCounter(ts, static_cast<double>(value),
                                            track);
     }
diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h
index 5485b36..b604b6c 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.h
+++ b/src/trace_processor/importers/proto/system_probes_parser.h
@@ -60,6 +60,9 @@
   const StringId cpu_times_irq_ns_id_;
   const StringId cpu_times_softirq_ns_id_;
   const StringId oom_score_adj_id_;
+  const StringId is_peak_rss_resettable_id_;
+  const StringId chrome_private_footprint_kb_;
+  const StringId chrome_peak_resident_set_kb_;
   const StringId thread_time_in_state_id_;
   const StringId thread_time_in_state_cpu_id_;
   const StringId cpu_freq_id_;
@@ -69,7 +72,7 @@
   // Maps a proto field number for memcounters in ProcessStats::Process to
   // their StringId. Keep kProcStatsProcessSize equal to 1 + max proto field
   // id of ProcessStats::Process.
-  static constexpr size_t kProcStatsProcessSize = 11;
+  static constexpr size_t kProcStatsProcessSize = 15;
   std::array<StringId, kProcStatsProcessSize> proc_stats_process_names_{};
 
   uint64_t ms_per_tick_ = 0;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 062d287..8514b42 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -155,6 +155,7 @@
   F(ninja_parse_errors,                 kSingle,  kError,    kTrace,    ""),   \
   F(perf_samples_skipped,               kSingle,  kInfo,     kTrace,    ""),   \
   F(perf_samples_skipped_dataloss,      kSingle,  kDataLoss, kTrace,    ""),   \
+  F(memory_snapshot_parser_failure,     kSingle,  kError,    kAnalysis, ""),   \
   F(thread_time_in_state_out_of_order,  kSingle,  kError,    kAnalysis, ""),   \
   F(thread_time_in_state_unknown_cpu_freq,                                     \
                                         kSingle,  kError,    kAnalysis, ""),   \
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 604b026..6c42178 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -91,6 +91,8 @@
 
 using VulkanAllocId = tables::VulkanMemoryAllocationsTable::Id;
 
+using ProcessMemorySnapshotId = tables::ProcessMemorySnapshotTable::Id;
+
 using SnapshotNodeId = tables::MemorySnapshotNodeTable::Id;
 
 // TODO(lalitm): this is a temporary hack while migrating the counters table and
diff --git a/src/trace_processor/tables/memory_tables.h b/src/trace_processor/tables/memory_tables.h
index 6678a73..510426f 100644
--- a/src/trace_processor/tables/memory_tables.h
+++ b/src/trace_processor/tables/memory_tables.h
@@ -44,14 +44,14 @@
 PERFETTO_TP_TABLE(PERFETTO_TP_PROCESS_MEMORY_SNAPSHOT_DEF);
 
 // @tablegroup
-#define PERFETTO_TP_MEMORY_SNAPSHOT_NODE_DEF(NAME, PARENT, C) \
-  NAME(MemorySnapshotNodeTable, "memory_snapshot_node")       \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                           \
-  C(ProcessMemorySnapshotTable::Id, process_snapshot_id)      \
-  C(MemorySnapshotNodeTable::Id, parent_node_id)              \
-  C(StringPool::Id, path)                                     \
-  C(int64_t, size)                                            \
-  C(int64_t, effective_size)                                  \
+#define PERFETTO_TP_MEMORY_SNAPSHOT_NODE_DEF(NAME, PARENT, C)    \
+  NAME(MemorySnapshotNodeTable, "memory_snapshot_node")          \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                              \
+  C(ProcessMemorySnapshotTable::Id, process_snapshot_id)         \
+  C(base::Optional<MemorySnapshotNodeTable::Id>, parent_node_id) \
+  C(StringPool::Id, path)                                        \
+  C(int64_t, size)                                               \
+  C(int64_t, effective_size)                                     \
   C(base::Optional<uint32_t>, arg_set_id)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_MEMORY_SNAPSHOT_NODE_DEF);
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index ae1f9cc..ff9cee6 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -97,6 +97,7 @@
   for (std::unique_ptr<ProtoImporterModule>& module : context_.modules) {
     module->NotifyEndOfFile();
   }
+  context_.args_tracker->Flush();
 }
 
 }  // namespace trace_processor
diff --git a/test/trace_processor/chrome/index b/test/trace_processor/chrome/index
index a1d9c54..1c821ac 100644
--- a/test/trace_processor/chrome/index
+++ b/test/trace_processor/chrome/index
@@ -12,3 +12,12 @@
 ../../data/chrome_scroll_without_vsync.pftrace scroll_jank_cause_queuing_delay_general_validation.sql scroll_jank_cause_queuing_delay_general_validation.out
 ../../data/chrome_scroll_without_vsync.pftrace chrome_thread_slice_with_cpu_time.sql chrome_thread_slice_with_cpu_time.out
 ../track_event/track_event_counters.textproto chrome_thread_slice_with_cpu_time_repeated.sql chrome_thread_slice_with_cpu_time_repeated.out
+
+# Chrome memory snapshots.
+../../data/chrome_memory_snapshot.pftrace memory_snapshot_general_validation.sql memory_snapshot_general_validation.out
+../../data/chrome_memory_snapshot.pftrace memory_snapshot_os_dump_events.sql memory_snapshot_os_dump_events.out
+../../data/chrome_memory_snapshot.pftrace memory_snapshot_chrome_dump_events.sql memory_snapshot_chrome_dump_events.out
+../../data/chrome_memory_snapshot.pftrace memory_snapshot_nodes.sql memory_snapshot_nodes.out
+../../data/chrome_memory_snapshot.pftrace memory_snapshot_edges.sql memory_snapshot_edges.out
+../../data/chrome_memory_snapshot.pftrace memory_snapshot_node_args.sql memory_snapshot_node_args.out
+../../data/chrome_memory_snapshot.pftrace memory_snapshot_smaps.sql memory_snapshot_smaps.out
diff --git a/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out b/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out
new file mode 100644
index 0000000..1d07a47
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out
@@ -0,0 +1,19 @@
+"process_snapshot_id","upid","snapshot_id","timestamp","detail_level"
+0,1,0,111027205123000,"background"
+1,2,0,111027205123000,"background"
+2,3,0,111027205123000,"background"
+3,5,0,111027205123000,"background"
+4,4,0,111027205123000,"background"
+5,8,0,111027205123000,"background"
+6,6,0,111027205123000,"background"
+7,7,0,111027205123000,"background"
+8,0,0,111027205123000,"background"
+9,1,1,111037187565000,"background"
+10,2,1,111037187565000,"background"
+11,3,1,111037187565000,"background"
+12,5,1,111037187565000,"background"
+13,4,1,111037187565000,"background"
+14,8,1,111037187565000,"background"
+15,6,1,111037187565000,"background"
+16,7,1,111037187565000,"background"
+17,0,1,111037187565000,"background"
diff --git a/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.sql b/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.sql
new file mode 100644
index 0000000..67fbf86
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.sql
@@ -0,0 +1,25 @@
+--
+-- 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
+  pms.id AS process_snapshot_id,
+  upid,
+  snapshot_id,
+  timestamp,
+  detail_level
+FROM memory_snapshot ms
+LEFT JOIN process_memory_snapshot pms
+  ON ms.id = pms.snapshot_id
diff --git a/test/trace_processor/chrome/memory_snapshot_edges.out b/test/trace_processor/chrome/memory_snapshot_edges.out
new file mode 100644
index 0000000..15bb371
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_edges.out
@@ -0,0 +1,21 @@
+"id","source_node_id","target_node_id","importance"
+0,1734,1863,0
+1,1728,1839,0
+2,1732,1772,0
+3,1729,1786,0
+4,1730,1805,0
+5,1735,1859,0
+6,1733,1844,0
+7,1727,1840,0
+8,1731,1850,0
+9,1726,1849,0
+10,1636,1639,0
+11,1681,1846,0
+12,1683,1744,0
+13,1680,1849,0
+14,1682,1859,0
+15,1707,1621,0
+16,1634,1638,0
+17,1399,1193,0
+18,1083,1795,2
+19,1358,1846,0
diff --git a/test/trace_processor/chrome/memory_snapshot_edges.sql b/test/trace_processor/chrome/memory_snapshot_edges.sql
new file mode 100644
index 0000000..63307ad
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_edges.sql
@@ -0,0 +1,23 @@
+--
+-- 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
+  id,
+  source_node_id,
+  target_node_id,
+  importance
+FROM memory_snapshot_edge
+LIMIT 20
diff --git a/test/trace_processor/chrome/memory_snapshot_general_validation.out b/test/trace_processor/chrome/memory_snapshot_general_validation.out
new file mode 100644
index 0000000..cb203ee
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_general_validation.out
@@ -0,0 +1,2 @@
+"total_snapshots","total_processes","total_process_snapshots","total_nodes","total_edges","total_node_args","total_smaps"
+2,9,18,3584,788,5014,33979
diff --git a/test/trace_processor/chrome/memory_snapshot_general_validation.sql b/test/trace_processor/chrome/memory_snapshot_general_validation.sql
new file mode 100644
index 0000000..4075041
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_general_validation.sql
@@ -0,0 +1,41 @@
+--
+-- 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
+  (
+    SELECT COUNT(*) FROM memory_snapshot
+  ) AS total_snapshots,
+  (
+    SELECT COUNT(*) FROM process
+  ) AS total_processes,
+  (
+    SELECT COUNT(*) FROM process_memory_snapshot
+  ) AS total_process_snapshots,
+  (
+    SELECT COUNT(*) FROM memory_snapshot_node
+  ) AS total_nodes,
+  (
+    SELECT COUNT(*) FROM memory_snapshot_edge
+  ) AS total_edges,
+  (
+    SELECT COUNT(DISTINCT args.id)
+    FROM args
+    INNER JOIN memory_snapshot_node
+    ON args.arg_set_id = memory_snapshot_node.arg_set_id
+  ) AS total_node_args,
+  (
+    SELECT COUNT(*) FROM profiler_smaps
+    INNER JOIN memory_snapshot ON timestamp = ts
+  ) AS total_smaps
diff --git a/test/trace_processor/chrome/memory_snapshot_node_args.out b/test/trace_processor/chrome/memory_snapshot_node_args.out
new file mode 100644
index 0000000..c6adffe
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_node_args.out
@@ -0,0 +1,21 @@
+"node_id","key","value_type","int_value","string_value"
+20,"id.value","string","[NULL]","C7BC3FD59A40689345EB9CB08570641B"
+20,"locked_size.value","int",0,"[NULL]"
+20,"locked_size.unit","string","[NULL]","bytes"
+20,"virtual_size.value","int",961,"[NULL]"
+20,"virtual_size.unit","string","[NULL]","bytes"
+21,"id.value","string","[NULL]","AF621E3BE175B9B618BAADF9138E5598"
+21,"locked_size.value","int",0,"[NULL]"
+21,"locked_size.unit","string","[NULL]","bytes"
+21,"virtual_size.value","int",529,"[NULL]"
+21,"virtual_size.unit","string","[NULL]","bytes"
+30,"free_size.value","int",1048576,"[NULL]"
+30,"free_size.unit","string","[NULL]","bytes"
+32,"free_size.value","int",65472,"[NULL]"
+32,"free_size.unit","string","[NULL]","bytes"
+54,"name.value","string","[NULL]","/home/toki/.config/chromium/Default/Site Characteristics Database"
+57,"name.value","string","[NULL]","/home/toki/.config/chromium/Default/Sync Data/LevelDB"
+60,"name.value","string","[NULL]","/home/toki/.config/chromium/Default/shared_proto_db/metadata"
+63,"name.value","string","[NULL]","/home/toki/.config/chromium/Default/GCM Store/Encryption"
+66,"name.value","string","[NULL]","/home/toki/.config/chromium/Default/Service Worker/Database"
+69,"name.value","string","[NULL]","/home/toki/.config/chromium/Default/Extension Rules"
diff --git a/test/trace_processor/chrome/memory_snapshot_node_args.sql b/test/trace_processor/chrome/memory_snapshot_node_args.sql
new file mode 100644
index 0000000..03df9e9
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_node_args.sql
@@ -0,0 +1,25 @@
+--
+-- 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
+  node.id AS node_id,
+  key,
+  value_type,
+  int_value,
+  string_value
+FROM memory_snapshot_node node
+INNER JOIN args ON node.arg_set_id = args.arg_set_id
+LIMIT 20
diff --git a/test/trace_processor/chrome/memory_snapshot_nodes.out b/test/trace_processor/chrome/memory_snapshot_nodes.out
new file mode 100644
index 0000000..39a8108
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_nodes.out
@@ -0,0 +1,21 @@
+"id","process_snapshot_id","parent_node_id","path","size","effective_size"
+0,0,"[NULL]","cc",2899968,2899968
+1,0,0,"cc/tile_memory",2899968,2899968
+2,0,1,"cc/tile_memory/provider_0",2899968,2899968
+3,0,2,"cc/tile_memory/provider_0/resource_10",262144,262144
+4,0,2,"cc/tile_memory/provider_0/resource_11",131072,131072
+5,0,2,"cc/tile_memory/provider_0/resource_14",262144,262144
+6,0,2,"cc/tile_memory/provider_0/resource_15",131072,131072
+7,0,2,"cc/tile_memory/provider_0/resource_21",262144,262144
+8,0,2,"cc/tile_memory/provider_0/resource_23",262144,262144
+9,0,2,"cc/tile_memory/provider_0/resource_26",131072,131072
+10,0,2,"cc/tile_memory/provider_0/resource_3",262144,262144
+11,0,2,"cc/tile_memory/provider_0/resource_31",131072,131072
+12,0,2,"cc/tile_memory/provider_0/resource_36",262144,262144
+13,0,2,"cc/tile_memory/provider_0/resource_4",131072,131072
+14,0,2,"cc/tile_memory/provider_0/resource_41",16384,16384
+15,0,2,"cc/tile_memory/provider_0/resource_42",262144,262144
+16,0,2,"cc/tile_memory/provider_0/resource_7",262144,262144
+17,0,2,"cc/tile_memory/provider_0/resource_8",131072,131072
+18,0,"[NULL]","discardable",16384,16384
+19,0,18,"discardable/process_ffffffff",16384,16384
diff --git a/test/trace_processor/chrome/memory_snapshot_nodes.sql b/test/trace_processor/chrome/memory_snapshot_nodes.sql
new file mode 100644
index 0000000..0c014d3
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_nodes.sql
@@ -0,0 +1,25 @@
+--
+-- 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
+  id,
+  process_snapshot_id,
+  parent_node_id,
+  path,
+  size,
+  effective_size
+FROM memory_snapshot_node
+LIMIT 20
diff --git a/test/trace_processor/chrome/memory_snapshot_os_dump_events.out b/test/trace_processor/chrome/memory_snapshot_os_dump_events.out
new file mode 100644
index 0000000..b21de21
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_os_dump_events.out
@@ -0,0 +1,19 @@
+"upid","pid","name","timestamp","detail_level","private_footprint_kb","peak_resident_set_kb","is_peak_rss_resettable"
+0,0,"[NULL]",111027205123000,"background","[NULL]","[NULL]","[NULL]"
+1,29694,"Browser",111027205123000,"background",76656640.000000,479354880.000000,1
+2,29725,"GPU Process",111027205123000,"background",62836736.000000,280952832.000000,1
+3,29728,"Service: network.mojom.NetworkService",111027205123000,"background",35360768.000000,229261312.000000,1
+4,29764,"Service: data_decoder.mojom.DataDecoderService",111027205123000,"background",30576640.000000,201347072.000000,1
+5,29737,"Service: storage.mojom.StorageService",111027205123000,"background",30617600.000000,207138816.000000,1
+6,29833,"Renderer",111027205123000,"background",36802560.000000,264036352.000000,1
+7,29877,"Service: tracing.mojom.TracingService",111027205123000,"background",31002624.000000,199208960.000000,1
+8,29807,"Renderer",111027205123000,"background",87248896.000000,451424256.000000,1
+0,0,"[NULL]",111037187565000,"background","[NULL]","[NULL]","[NULL]"
+1,29694,"Browser",111037187565000,"background",85942272.000000,491671552.000000,1
+2,29725,"GPU Process",111037187565000,"background",62754816.000000,275906560.000000,1
+3,29728,"Service: network.mojom.NetworkService",111037187565000,"background",35495936.000000,229351424.000000,1
+4,29764,"Service: data_decoder.mojom.DataDecoderService",111037187565000,"background",30679040.000000,201424896.000000,1
+5,29737,"Service: storage.mojom.StorageService",111037187565000,"background",30650368.000000,205791232.000000,1
+6,29833,"Renderer",111037187565000,"background",36950016.000000,264843264.000000,1
+7,29877,"Service: tracing.mojom.TracingService",111037187565000,"background",33386496.000000,204554240.000000,1
+8,29807,"Renderer",111037187565000,"background",76832768.000000,445292544.000000,1
diff --git a/test/trace_processor/chrome/memory_snapshot_os_dump_events.sql b/test/trace_processor/chrome/memory_snapshot_os_dump_events.sql
new file mode 100644
index 0000000..b266773
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_os_dump_events.sql
@@ -0,0 +1,42 @@
+--
+-- 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
+  p.upid,
+  pid,
+  p.name,
+  timestamp,
+  detail_level,
+  pf.value AS private_footprint_kb,
+  prs.value AS peak_resident_set_kb,
+  EXTRACT_ARG(p.arg_set_id, 'is_peak_rss_resettable') AS is_peak_rss_resettable
+FROM process p
+LEFT JOIN memory_snapshot
+LEFT JOIN (
+  SELECT id, upid
+  FROM process_counter_track
+  WHERE name IS 'chrome.private_footprint_kb'
+  ) AS pct_pf
+  ON p.upid = pct_pf.upid
+LEFT JOIN counter pf ON timestamp = pf.ts AND pct_pf.id = pf.track_id
+LEFT JOIN (
+  SELECT id, upid
+  FROM process_counter_track
+  WHERE name IS 'chrome.peak_resident_set_kb'
+  ) AS pct_prs
+  ON p.upid = pct_prs.upid
+LEFT JOIN counter prs ON timestamp = prs.ts AND pct_prs.id = prs.track_id
+ORDER BY timestamp
diff --git a/test/trace_processor/chrome/memory_snapshot_smaps.out b/test/trace_processor/chrome/memory_snapshot_smaps.out
new file mode 100644
index 0000000..255526d
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_smaps.out
@@ -0,0 +1,21 @@
+"upid","name","ts","path","size_kb","private_dirty_kb","swap_kb","file_name","start_address","module_timestamp","module_debugid","module_debug_path","protection_flags","private_clean_resident_kb","shared_dirty_resident_kb","shared_clean_resident_kb","locked_kb","proportional_resident_kb"
+1,"Browser",111027205123000,"[NULL]",4,0,0,"",14051238432768,0,"[NULL]","[NULL]",0,0,0,0,0,0
+1,"Browser",111027205123000,"[NULL]",40956,40464,0,"",14051238436864,0,"[NULL]","[NULL]",6,0,0,0,0,40464
+1,"Browser",111027205123000,"[NULL]",4,0,0,"",14051280375808,0,"[NULL]","[NULL]",0,0,0,0,0,0
+1,"Browser",111027205123000,"[NULL]",11860,8620,0,"",14051280379904,0,"[NULL]","[NULL]",6,0,0,0,0,8620
+1,"Browser",111027205123000,"[NULL]",67896,4,0,"/home/toki/chromium/src/out/Default/chrome",94168098267136,0,"[NULL]","[NULL]",4,6824,0,14812,0,8788
+1,"Browser",111027205123000,"[NULL]",123784,4,0,"/home/toki/chromium/src/out/Default/chrome",94168167792640,0,"[NULL]","[NULL]",5,62584,0,28044,0,70896
+1,"Browser",111027205123000,"[NULL]",3036,3036,0,"/home/toki/chromium/src/out/Default/chrome",94168294547456,0,"[NULL]","[NULL]",4,0,0,0,0,3036
+1,"Browser",111027205123000,"[NULL]",148,136,0,"/home/toki/chromium/src/out/Default/chrome",94168297656320,0,"[NULL]","[NULL]",6,0,0,12,0,137
+1,"Browser",111027205123000,"[NULL]",404,240,0,"",94168297807872,0,"[NULL]","[NULL]",6,0,0,0,0,240
+1,"Browser",111027205123000,"[NULL]",2048,0,0,"/dev/shm/.org.chromium.Chromium.Doo3XC (deleted)",140632555954176,0,"[NULL]","[NULL]",134,0,4,0,0,2
+1,"Browser",111027205123000,"[NULL]",2048,0,0,"/dev/shm/.org.chromium.Chromium.PxFFQm (deleted)",140632558575616,0,"[NULL]","[NULL]",134,0,4,0,0,2
+1,"Browser",111027205123000,"[NULL]",428,0,0,"/usr/lib/x86_64-linux-gnu/nss/libnssckbi.so",140632563163136,0,"[NULL]","[NULL]",5,128,0,136,0,196
+1,"Browser",111027205123000,"[NULL]",2044,0,0,"/usr/lib/x86_64-linux-gnu/nss/libnssckbi.so",140632563601408,0,"[NULL]","[NULL]",0,0,0,0,0,0
+1,"Browser",111027205123000,"[NULL]",68,68,0,"/usr/lib/x86_64-linux-gnu/nss/libnssckbi.so",140632565694464,0,"[NULL]","[NULL]",4,0,0,0,0,68
+1,"Browser",111027205123000,"[NULL]",40,40,0,"/usr/lib/x86_64-linux-gnu/nss/libnssckbi.so",140632565764096,0,"[NULL]","[NULL]",6,0,0,0,0,40
+1,"Browser",111027205123000,"[NULL]",668,0,0,"/usr/lib/x86_64-linux-gnu/nss/libfreeblpriv3.so",140632565805056,0,"[NULL]","[NULL]",5,340,0,16,0,344
+1,"Browser",111027205123000,"[NULL]",2048,0,0,"/usr/lib/x86_64-linux-gnu/nss/libfreeblpriv3.so",140632566489088,0,"[NULL]","[NULL]",0,0,0,0,0,0
+1,"Browser",111027205123000,"[NULL]",8,8,0,"/usr/lib/x86_64-linux-gnu/nss/libfreeblpriv3.so",140632568586240,0,"[NULL]","[NULL]",4,0,0,0,0,8
+1,"Browser",111027205123000,"[NULL]",4,4,0,"/usr/lib/x86_64-linux-gnu/nss/libfreeblpriv3.so",140632568594432,0,"[NULL]","[NULL]",6,0,0,0,0,4
+1,"Browser",111027205123000,"[NULL]",16,16,0,"",140632568598528,0,"[NULL]","[NULL]",6,0,0,0,0,16
diff --git a/test/trace_processor/chrome/memory_snapshot_smaps.sql b/test/trace_processor/chrome/memory_snapshot_smaps.sql
new file mode 100644
index 0000000..dfd1fce
--- /dev/null
+++ b/test/trace_processor/chrome/memory_snapshot_smaps.sql
@@ -0,0 +1,39 @@
+--
+-- 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
+  process.upid,
+  process.name,
+  smap.ts,
+  path,
+  size_kb,
+  private_dirty_kb,
+  swap_kb,
+  file_name,
+  start_address,
+  module_timestamp,
+  module_debugid,
+  module_debug_path,
+  protection_flags,
+  private_clean_resident_kb,
+  shared_dirty_resident_kb,
+  shared_clean_resident_kb,
+  locked_kb,
+  proportional_resident_kb
+FROM process
+INNER JOIN profiler_smaps smap ON process.upid = smap.upid
+INNER JOIN memory_snapshot ms ON ms.timestamp = smap.ts
+LIMIT 20