Fix issues for memory instrumentation JSON exporting.

1. Enable system probes parsing for minimal storage processor. The
   memory dumps require the system counters added, and this needs to run
   in the minimal processor for JSON conversion. Separates chrome
   related ones to new file and adds support for parsing multiple
   modules for each trace packet field ID.

2. Fix the sizes of mapped regions from KB to bytes. The JSON format for
   these are written in bytes.

3. Fix ordering of level of detail enum.

4. Do not emit empty "vm_regions" entry, since catapult crashes. Handled
   here instead of catapult because traces won't open in older UI.

Change-Id: Ia5e7dd108cf995d880613ee6689f6cbd3936d681
diff --git a/Android.bp b/Android.bp
index ad2d219..e3448ce 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7773,6 +7773,8 @@
     "src/trace_processor/importers/ninja/ninja_log_parser.cc",
     "src/trace_processor/importers/proto/args_table_utils.cc",
     "src/trace_processor/importers/proto/async_track_set_tracker.cc",
+    "src/trace_processor/importers/proto/chrome_system_probes_module.cc",
+    "src/trace_processor/importers/proto/chrome_system_probes_parser.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",
diff --git a/BUILD b/BUILD
index 7e9fe2f..e5d78d3 100644
--- a/BUILD
+++ b/BUILD
@@ -1250,6 +1250,10 @@
         "src/trace_processor/importers/proto/args_table_utils.h",
         "src/trace_processor/importers/proto/async_track_set_tracker.cc",
         "src/trace_processor/importers/proto/async_track_set_tracker.h",
+        "src/trace_processor/importers/proto/chrome_system_probes_module.cc",
+        "src/trace_processor/importers/proto/chrome_system_probes_module.h",
+        "src/trace_processor/importers/proto/chrome_system_probes_parser.cc",
+        "src/trace_processor/importers/proto/chrome_system_probes_parser.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",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 178fb46..ecac01e 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -86,6 +86,10 @@
     "importers/proto/args_table_utils.h",
     "importers/proto/async_track_set_tracker.cc",
     "importers/proto/async_track_set_tracker.h",
+    "importers/proto/chrome_system_probes_module.cc",
+    "importers/proto/chrome_system_probes_module.h",
+    "importers/proto/chrome_system_probes_parser.cc",
+    "importers/proto/chrome_system_probes_parser.h",
     "importers/proto/heap_profile_tracker.cc",
     "importers/proto/heap_profile_tracker.h",
     "importers/proto/memory_tracker_snapshot_module.cc",
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 8ff0a93..d83ebb2 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -1456,9 +1456,13 @@
           }
         }
 
-        Json::Value& smaps =
-            event["args"]["dumps"]["process_mmaps"]["vm_regions"];
         const auto& smaps_table = storage_->profiler_smaps_table();
+        // Do not create vm_regions without memory maps, since catapult expects
+        // to have rows.
+        Json::Value* smaps =
+            smaps_table.row_count() > 0
+                ? &event["args"]["dumps"]["process_mmaps"]["vm_regions"]
+                : nullptr;
         for (uint32_t smaps_index = 0; smaps_index < smaps_table.row_count();
              ++smaps_index) {
           if (smaps_table.upid()[smaps_index] != upid)
@@ -1473,34 +1477,39 @@
           region["sa"] = base::Uint64ToHexStringNoPrefix(
               static_cast<uint64_t>(smaps_table.start_address()[smaps_index]));
           region["sz"] = base::Uint64ToHexStringNoPrefix(
-              static_cast<uint64_t>(smaps_table.size_kb()[smaps_index]));
+              static_cast<uint64_t>(smaps_table.size_kb()[smaps_index]) * 1024);
           region["ts"] =
               Json::Int64(smaps_table.module_timestamp()[smaps_index]);
           region["id"] = GetNonNullString(
               storage_, smaps_table.module_debugid()[smaps_index]);
           region["df"] = GetNonNullString(
               storage_, smaps_table.module_debug_path()[smaps_index]);
-          region["bs"]["pc"] =
-              base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(
-                  smaps_table.private_clean_resident_kb()[smaps_index]));
-          region["bs"]["pd"] =
-              base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(
-                  smaps_table.private_dirty_kb()[smaps_index]));
-          region["bs"]["pss"] =
-              base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(
-                  smaps_table.proportional_resident_kb()[smaps_index]));
-          region["bs"]["sc"] =
-              base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(
-                  smaps_table.shared_clean_resident_kb()[smaps_index]));
-          region["bs"]["sd"] =
-              base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(
-                  smaps_table.shared_dirty_resident_kb()[smaps_index]));
+          region["bs"]["pc"] = base::Uint64ToHexStringNoPrefix(
+              static_cast<uint64_t>(
+                  smaps_table.private_clean_resident_kb()[smaps_index]) *
+              1024);
+          region["bs"]["pd"] = base::Uint64ToHexStringNoPrefix(
+              static_cast<uint64_t>(
+                  smaps_table.private_dirty_kb()[smaps_index]) *
+              1024);
+          region["bs"]["pss"] = base::Uint64ToHexStringNoPrefix(
+              static_cast<uint64_t>(
+                  smaps_table.proportional_resident_kb()[smaps_index]) *
+              1024);
+          region["bs"]["sc"] = base::Uint64ToHexStringNoPrefix(
+              static_cast<uint64_t>(
+                  smaps_table.shared_clean_resident_kb()[smaps_index]) *
+              1024);
+          region["bs"]["sd"] = base::Uint64ToHexStringNoPrefix(
+              static_cast<uint64_t>(
+                  smaps_table.shared_dirty_resident_kb()[smaps_index]) *
+              1024);
           region["bs"]["sw"] = base::Uint64ToHexStringNoPrefix(
-              static_cast<uint64_t>(smaps_table.swap_kb()[smaps_index]));
-          smaps.append(region);
+              static_cast<uint64_t>(smaps_table.swap_kb()[smaps_index]) * 1024);
+          smaps->append(region);
         }
 
-        if (!totals.empty() || !smaps.empty())
+        if (!totals.empty() || (smaps && !smaps->empty()))
           writer_.WriteCommonEvent(event);
       }
 
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index 6016f8e..67c5988 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -1806,27 +1806,29 @@
   EXPECT_EQ(region["pf"].asInt64(), kProtectionFlags);
   EXPECT_EQ(region["sa"].asString(), base::Uint64ToHexStringNoPrefix(
                                          static_cast<uint64_t>(kStartAddress)));
-  EXPECT_EQ(region["sz"].asString(),
-            base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kSizeKb)));
+  EXPECT_EQ(
+      region["sz"].asString(),
+      base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kSizeKb * 1024)));
   EXPECT_EQ(region["id"].asString(), kModuleDebugid);
   EXPECT_EQ(region["df"].asString(), kModuleDebugPath);
   EXPECT_EQ(region["bs"]["pc"].asString(),
             base::Uint64ToHexStringNoPrefix(
-                static_cast<uint64_t>(kPrivateCleanResidentKb)));
-  EXPECT_EQ(
-      region["bs"]["pd"].asString(),
-      base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kPrivateDirtyKb)));
+                static_cast<uint64_t>(kPrivateCleanResidentKb * 1024)));
+  EXPECT_EQ(region["bs"]["pd"].asString(),
+            base::Uint64ToHexStringNoPrefix(
+                static_cast<uint64_t>(kPrivateDirtyKb * 1024)));
   EXPECT_EQ(region["bs"]["pss"].asString(),
             base::Uint64ToHexStringNoPrefix(
-                static_cast<uint64_t>(kProportionalResidentKb)));
+                static_cast<uint64_t>(kProportionalResidentKb * 1024)));
   EXPECT_EQ(region["bs"]["sc"].asString(),
             base::Uint64ToHexStringNoPrefix(
-                static_cast<uint64_t>(kSharedCleanResidentKb)));
+                static_cast<uint64_t>(kSharedCleanResidentKb * 1024)));
   EXPECT_EQ(region["bs"]["sd"].asString(),
             base::Uint64ToHexStringNoPrefix(
-                static_cast<uint64_t>(kSharedDirtyResidentKb)));
-  EXPECT_EQ(region["bs"]["sw"].asString(),
-            base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kSwapKb)));
+                static_cast<uint64_t>(kSharedDirtyResidentKb * 1024)));
+  EXPECT_EQ(
+      region["bs"]["sw"].asString(),
+      base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kSwapKb * 1024)));
 }
 
 TEST_F(ExportJsonTest, MemorySnapshotChromeDumpEvent) {
diff --git a/src/trace_processor/importers/default_modules.cc b/src/trace_processor/importers/default_modules.cc
index 88fa696..9e47ddc 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/chrome_system_probes_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"
@@ -32,6 +33,7 @@
       static_cast<FtraceModule*>(context->modules.back().get());
 
   context->modules.emplace_back(new MemoryTrackerSnapshotModule(context));
+  context->modules.emplace_back(new ChromeSystemProbesModule(context));
   context->modules.emplace_back(new TrackEventModule(context));
   context->modules.emplace_back(new ProfileModule(context));
 }
diff --git a/src/trace_processor/importers/proto/chrome_system_probes_module.cc b/src/trace_processor/importers/proto/chrome_system_probes_module.cc
new file mode 100644
index 0000000..775d64f
--- /dev/null
+++ b/src/trace_processor/importers/proto/chrome_system_probes_module.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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/chrome_system_probes_module.h"
+#include "perfetto/base/build_config.h"
+#include "src/trace_processor/importers/proto/chrome_system_probes_parser.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
+
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using perfetto::protos::pbzero::TracePacket;
+
+ChromeSystemProbesModule::ChromeSystemProbesModule(
+    TraceProcessorContext* context)
+    : parser_(context) {
+  RegisterForField(TracePacket::kProcessStatsFieldNumber, context);
+}
+
+void ChromeSystemProbesModule::ParsePacket(const TracePacket::Decoder& decoder,
+                                           const TimestampedTracePiece& ttp,
+                                           uint32_t field_id) {
+  switch (field_id) {
+    case TracePacket::kProcessStatsFieldNumber:
+      parser_.ParseProcessStats(ttp.timestamp, decoder.process_stats());
+      return;
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/chrome_system_probes_module.h b/src/trace_processor/importers/proto/chrome_system_probes_module.h
new file mode 100644
index 0000000..0fc4615
--- /dev/null
+++ b/src/trace_processor/importers/proto/chrome_system_probes_module.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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_CHROME_SYSTEM_PROBES_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CHROME_SYSTEM_PROBES_MODULE_H_
+
+#include "perfetto/base/build_config.h"
+#include "src/trace_processor/importers/proto/chrome_system_probes_parser.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Parses only the Chrome recorded system stats fields. This is separated from
+// SystemProbesModule due to the binary size impact of the system probes parser.
+class ChromeSystemProbesModule : public ProtoImporterModule {
+ public:
+  explicit ChromeSystemProbesModule(TraceProcessorContext* context);
+
+  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
+                   const TimestampedTracePiece& ttp,
+                   uint32_t field_id) override;
+
+ private:
+  ChromeSystemProbesParser parser_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CHROME_SYSTEM_PROBES_MODULE_H_
diff --git a/src/trace_processor/importers/proto/chrome_system_probes_parser.cc b/src/trace_processor/importers/proto/chrome_system_probes_parser.cc
new file mode 100644
index 0000000..e3c95bd
--- /dev/null
+++ b/src/trace_processor/importers/proto/chrome_system_probes_parser.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 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/chrome_system_probes_parser.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/protozero/proto_decoder.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#include "protos/perfetto/trace/ps/process_stats.pbzero.h"
+#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
+#include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+ChromeSystemProbesParser::ChromeSystemProbesParser(
+    TraceProcessorContext* context)
+    : context_(context),
+      is_peak_rss_resettable_id_(
+          context->storage->InternString("is_peak_rss_resettable")) {
+  using ProcessStats = protos::pbzero::ProcessStats;
+  proc_stats_process_names_
+      [ProcessStats::Process::kChromePrivateFootprintKbFieldNumber] =
+          context->storage->InternString("chrome.private_footprint_kb");
+  proc_stats_process_names_
+      [ProcessStats::Process::kChromePeakResidentSetKbFieldNumber] =
+          context->storage->InternString("chrome.peak_resident_set_kb");
+}
+
+void ChromeSystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) {
+  protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size);
+  for (auto it = stats.processes(); it; ++it) {
+    protozero::ProtoDecoder proc(*it);
+    uint32_t pid = 0;
+    for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) {
+      if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) {
+        pid = fld.as_uint32();
+        break;
+      }
+    }
+
+    for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) {
+      if (fld.id() == protos::pbzero::ProcessStats::Process::
+                          kIsPeakRssResettableFieldNumber) {
+        UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
+        context_->process_tracker->AddArgsTo(upid).AddArg(
+            is_peak_rss_resettable_id_, Variadic::Boolean(fld.as_bool()));
+        continue;
+      }
+
+      if (fld.id() >= proc_stats_process_names_.size())
+        continue;
+      const StringId& name = proc_stats_process_names_[fld.id()];
+      if (name == StringId::Null())
+        continue;
+      UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
+      TrackId track =
+          context_->track_tracker->InternProcessCounterTrack(name, upid);
+      int64_t value = fld.as_int64() * 1024;
+      context_->event_tracker->PushCounter(ts, static_cast<double>(value),
+                                           track);
+    }
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/chrome_system_probes_parser.h b/src/trace_processor/importers/proto/chrome_system_probes_parser.h
new file mode 100644
index 0000000..2e86b6b
--- /dev/null
+++ b/src/trace_processor/importers/proto/chrome_system_probes_parser.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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_CHROME_SYSTEM_PROBES_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CHROME_SYSTEM_PROBES_PARSER_H_
+
+#include <array>
+
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class ChromeSystemProbesParser {
+ public:
+  using ConstBytes = protozero::ConstBytes;
+
+  explicit ChromeSystemProbesParser(TraceProcessorContext*);
+
+  void ParseProcessStats(int64_t timestamp, ConstBytes);
+
+ private:
+  TraceProcessorContext* const context_;
+
+  const StringId is_peak_rss_resettable_id_;
+
+  // 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. Also update SystemProbesParser.
+  static constexpr size_t kProcStatsProcessSize = 15;
+  std::array<StringId, kProcStatsProcessSize> proc_stats_process_names_{};
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CHROME_SYSTEM_PROBES_PARSER_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
index 4fd7a58..7dac0e3 100644
--- a/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc
@@ -28,9 +28,9 @@
 MemoryTrackerSnapshotParser::MemoryTrackerSnapshotParser(
     TraceProcessorContext* context)
     : context_(context),
-      level_of_detail_ids_{{context_->storage->InternString("detailed"),
+      level_of_detail_ids_{{context_->storage->InternString("background"),
                             context_->storage->InternString("light"),
-                            context_->storage->InternString("background")}},
+                            context_->storage->InternString("detailed")}},
       unit_ids_{{context_->storage->InternString("objects"),
                  context_->storage->InternString("bytes")}},
       aggregate_raw_nodes_(),
diff --git a/src/trace_processor/importers/proto/proto_importer_module.cc b/src/trace_processor/importers/proto/proto_importer_module.cc
index 4f23f21..7bc1cec 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.cc
+++ b/src/trace_processor/importers/proto/proto_importer_module.cc
@@ -44,10 +44,9 @@
 void ProtoImporterModule::RegisterForField(uint32_t field_id,
                                            TraceProcessorContext* context) {
   if (context->modules_by_field.size() <= field_id) {
-    context->modules_by_field.resize(field_id + 1, nullptr);
+    context->modules_by_field.resize(field_id + 1);
   }
-  PERFETTO_DCHECK(!context->modules_by_field[field_id]);
-  context->modules_by_field[field_id] = this;
+  context->modules_by_field[field_id].push_back(this);
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 99e41b6..3aaa5cb 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -125,8 +125,9 @@
   // TODO(eseckler): Propagate statuses from modules.
   auto& modules = context_->modules_by_field;
   for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
-    if (modules[field_id] && packet.Get(field_id).valid()) {
-      modules[field_id]->ParsePacket(packet, ttp, field_id);
+    if (!modules[field_id].empty() && packet.Get(field_id).valid()) {
+      for (ProtoImporterModule* module : modules[field_id])
+        module->ParsePacket(packet, ttp, field_id);
       return;
     }
   }
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index 3ccfc8b..1023636 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -215,11 +215,13 @@
 
   auto& modules = context_->modules_by_field;
   for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
-    if (modules[field_id] && decoder.Get(field_id).valid()) {
-      ModuleResult res = modules[field_id]->TokenizePacket(
-          decoder, &packet, timestamp, state, field_id);
-      if (!res.ignored())
-        return res.ToStatus();
+    if (!modules[field_id].empty() && decoder.Get(field_id).valid()) {
+      for (ProtoImporterModule* module : modules[field_id]) {
+        ModuleResult res = module->TokenizePacket(decoder, &packet, timestamp,
+                                                  state, field_id);
+        if (!res.ignored())
+          return res.ToStatus();
+      }
     }
   }
 
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index d81e335..2319a13 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -69,12 +69,6 @@
       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")),
@@ -105,12 +99,6 @@
       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) {
@@ -282,6 +270,7 @@
 }
 
 void SystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) {
+  using Process = protos::pbzero::ProcessStats::Process;
   protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size);
   const auto kOomScoreAdjFieldNumber =
       protos::pbzero::ProcessStats::Process::kOomScoreAdjFieldNumber;
@@ -293,19 +282,11 @@
 
     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 ||
@@ -327,16 +308,16 @@
                                        : fld.as_int64() * 1024;
         has_counter[fld.id()] = true;
       } else {
+        // Chrome fields are processed by ChromeSystemProbesParser.
+        if (fld.id() == Process::kIsPeakRssResettableFieldNumber ||
+            fld.id() == Process::kChromePrivateFootprintKbFieldNumber ||
+            fld.id() == Process::kChromePrivateFootprintKbFieldNumber) {
+          continue;
+        }
         context_->storage->IncrementStats(stats::proc_stat_unknown_counters);
       }
     }
 
-    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] || field_id ==
@@ -347,7 +328,7 @@
 
       // 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];
+      const StringId& name = proc_stats_process_names_[field_id];
       UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
       TrackId track =
           context_->track_tracker->InternProcessCounterTrack(name, upid);
diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h
index b604b6c..c9f66c0 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.h
+++ b/src/trace_processor/importers/proto/system_probes_parser.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_SYSTEM_PROBES_PARSER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_SYSTEM_PROBES_PARSER_H_
 
+#include <array>
 #include <set>
 #include <vector>
 
@@ -60,9 +61,6 @@
   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_;
@@ -71,7 +69,8 @@
 
   // 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.
+  // id of ProcessStats::Process. Also update the value in
+  // ChromeSystemProbesParser.
   static constexpr size_t kProcStatsProcessSize = 15;
   std::array<StringId, kProcStatsProcessSize> proc_stats_process_names_{};
 
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index f2a74b1..eb6bf13 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -112,7 +112,7 @@
 
   // The module at the index N is registered to handle field id N in
   // TracePacket.
-  std::vector<ProtoImporterModule*> modules_by_field;
+  std::vector<std::vector<ProtoImporterModule*>> modules_by_field;
   std::vector<std::unique_ptr<ProtoImporterModule>> modules;
   FtraceModule* ftrace_module = nullptr;
 };
diff --git a/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out b/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out
index 1d07a47..aea49aa 100644
--- a/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out
+++ b/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out
@@ -1,19 +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"
+0,1,0,111027205123000,"detailed"
+1,2,0,111027205123000,"detailed"
+2,3,0,111027205123000,"detailed"
+3,5,0,111027205123000,"detailed"
+4,4,0,111027205123000,"detailed"
+5,8,0,111027205123000,"detailed"
+6,6,0,111027205123000,"detailed"
+7,7,0,111027205123000,"detailed"
+8,0,0,111027205123000,"detailed"
+9,1,1,111037187565000,"detailed"
+10,2,1,111037187565000,"detailed"
+11,3,1,111037187565000,"detailed"
+12,5,1,111037187565000,"detailed"
+13,4,1,111037187565000,"detailed"
+14,8,1,111037187565000,"detailed"
+15,6,1,111037187565000,"detailed"
+16,7,1,111037187565000,"detailed"
+17,0,1,111037187565000,"detailed"
diff --git a/test/trace_processor/chrome/memory_snapshot_os_dump_events.out b/test/trace_processor/chrome/memory_snapshot_os_dump_events.out
index b21de21..f1ce018 100644
--- a/test/trace_processor/chrome/memory_snapshot_os_dump_events.out
+++ b/test/trace_processor/chrome/memory_snapshot_os_dump_events.out
@@ -1,19 +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
+0,0,"[NULL]",111027205123000,"detailed","[NULL]","[NULL]","[NULL]"
+1,29694,"Browser",111027205123000,"detailed",76656640.000000,479354880.000000,1
+2,29725,"GPU Process",111027205123000,"detailed",62836736.000000,280952832.000000,1
+3,29728,"Service: network.mojom.NetworkService",111027205123000,"detailed",35360768.000000,229261312.000000,1
+4,29764,"Service: data_decoder.mojom.DataDecoderService",111027205123000,"detailed",30576640.000000,201347072.000000,1
+5,29737,"Service: storage.mojom.StorageService",111027205123000,"detailed",30617600.000000,207138816.000000,1
+6,29833,"Renderer",111027205123000,"detailed",36802560.000000,264036352.000000,1
+7,29877,"Service: tracing.mojom.TracingService",111027205123000,"detailed",31002624.000000,199208960.000000,1
+8,29807,"Renderer",111027205123000,"detailed",87248896.000000,451424256.000000,1
+0,0,"[NULL]",111037187565000,"detailed","[NULL]","[NULL]","[NULL]"
+1,29694,"Browser",111037187565000,"detailed",85942272.000000,491671552.000000,1
+2,29725,"GPU Process",111037187565000,"detailed",62754816.000000,275906560.000000,1
+3,29728,"Service: network.mojom.NetworkService",111037187565000,"detailed",35495936.000000,229351424.000000,1
+4,29764,"Service: data_decoder.mojom.DataDecoderService",111037187565000,"detailed",30679040.000000,201424896.000000,1
+5,29737,"Service: storage.mojom.StorageService",111037187565000,"detailed",30650368.000000,205791232.000000,1
+6,29833,"Renderer",111037187565000,"detailed",36950016.000000,264843264.000000,1
+7,29877,"Service: tracing.mojom.TracingService",111037187565000,"detailed",33386496.000000,204554240.000000,1
+8,29807,"Renderer",111037187565000,"detailed",76832768.000000,445292544.000000,1