Reapply "tp: fix build in Google3"
Reapply "Refactor StackProfile related classes."

This reverts commits
be8c9b247dd59ebc0d2b8555b4a063ba0e711743
and
037c128f85a73ab7fbe854ffbef792f7ac77a0c6.


+ Adds fixes for various test failures. (See diff patchset 1 and 2)

Bug: b/283794416
Change-Id: Ifcbe212250647c5f6ece0d0971ee97fe1d6188e3
diff --git a/Android.bp b/Android.bp
index 4267433..0ea862b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2375,6 +2375,7 @@
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_profiler_util",
         ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_json",
@@ -11078,6 +11079,7 @@
         "src/trace_processor/importers/common/process_tracker.cc",
         "src/trace_processor/importers/common/slice_tracker.cc",
         "src/trace_processor/importers/common/slice_translation_table.cc",
+        "src/trace_processor/importers/common/stack_profile_tracker.cc",
         "src/trace_processor/importers/common/system_info_tracker.cc",
         "src/trace_processor/importers/common/trace_parser.cc",
         "src/trace_processor/importers/common/track_tracker.cc",
@@ -11397,7 +11399,6 @@
         "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/default_modules.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_minimal_module.cc",
@@ -11406,12 +11407,12 @@
         "src/trace_processor/importers/proto/packet_sequence_state_generation.cc",
         "src/trace_processor/importers/proto/perf_sample_tracker.cc",
         "src/trace_processor/importers/proto/profile_module.cc",
+        "src/trace_processor/importers/proto/profile_packet_sequence_state.cc",
         "src/trace_processor/importers/proto/profile_packet_utils.cc",
-        "src/trace_processor/importers/proto/profiler_util.cc",
         "src/trace_processor/importers/proto/proto_trace_parser.cc",
         "src/trace_processor/importers/proto/proto_trace_reader.cc",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
-        "src/trace_processor/importers/proto/stack_profile_tracker.cc",
+        "src/trace_processor/importers/proto/stack_profile_sequence_state.cc",
         "src/trace_processor/importers/proto/track_event_module.cc",
         "src/trace_processor/importers/proto/track_event_parser.cc",
         "src/trace_processor/importers/proto/track_event_tokenizer.cc",
@@ -11438,9 +11439,9 @@
     srcs: [
         "src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc",
         "src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc",
-        "src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc",
         "src/trace_processor/importers/proto/network_trace_module_unittest.cc",
         "src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc",
+        "src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc",
         "src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
         "src/trace_processor/importers/proto/string_encoding_utils_unittests.cc",
     ],
@@ -12320,6 +12321,14 @@
     ],
 }
 
+// GN: //src/trace_processor/util:profiler_util
+filegroup {
+    name: "perfetto_src_trace_processor_util_profiler_util",
+    srcs: [
+        "src/trace_processor/util/profiler_util.cc",
+    ],
+}
+
 // GN: //src/trace_processor/util:proto_profiler
 filegroup {
     name: "perfetto_src_trace_processor_util_proto_profiler",
@@ -13871,6 +13880,7 @@
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_profiler_util",
         ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_json",
@@ -14568,6 +14578,7 @@
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_profiler_util",
         ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_json",
@@ -14800,6 +14811,7 @@
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_profiler_util",
         ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_json",
diff --git a/BUILD b/BUILD
index e6649d9..09ab8b4 100644
--- a/BUILD
+++ b/BUILD
@@ -272,6 +272,7 @@
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_profile_builder",
+        ":src_trace_processor_util_profiler_util",
         ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_json",
@@ -1473,6 +1474,8 @@
         "src/trace_processor/importers/common/slice_tracker.h",
         "src/trace_processor/importers/common/slice_translation_table.cc",
         "src/trace_processor/importers/common/slice_translation_table.h",
+        "src/trace_processor/importers/common/stack_profile_tracker.cc",
+        "src/trace_processor/importers/common/stack_profile_tracker.h",
         "src/trace_processor/importers/common/system_info_tracker.cc",
         "src/trace_processor/importers/common/system_info_tracker.h",
         "src/trace_processor/importers/common/trace_parser.cc",
@@ -1821,8 +1824,6 @@
         "src/trace_processor/importers/proto/chrome_system_probes_parser.h",
         "src/trace_processor/importers/proto/default_modules.cc",
         "src/trace_processor/importers/proto/default_modules.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",
@@ -1839,10 +1840,10 @@
         "src/trace_processor/importers/proto/perf_sample_tracker.h",
         "src/trace_processor/importers/proto/profile_module.cc",
         "src/trace_processor/importers/proto/profile_module.h",
+        "src/trace_processor/importers/proto/profile_packet_sequence_state.cc",
+        "src/trace_processor/importers/proto/profile_packet_sequence_state.h",
         "src/trace_processor/importers/proto/profile_packet_utils.cc",
         "src/trace_processor/importers/proto/profile_packet_utils.h",
-        "src/trace_processor/importers/proto/profiler_util.cc",
-        "src/trace_processor/importers/proto/profiler_util.h",
         "src/trace_processor/importers/proto/proto_incremental_state.h",
         "src/trace_processor/importers/proto/proto_trace_parser.cc",
         "src/trace_processor/importers/proto/proto_trace_parser.h",
@@ -1850,8 +1851,8 @@
         "src/trace_processor/importers/proto/proto_trace_reader.h",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.h",
-        "src/trace_processor/importers/proto/stack_profile_tracker.cc",
-        "src/trace_processor/importers/proto/stack_profile_tracker.h",
+        "src/trace_processor/importers/proto/stack_profile_sequence_state.cc",
+        "src/trace_processor/importers/proto/stack_profile_sequence_state.h",
         "src/trace_processor/importers/proto/track_event_module.cc",
         "src/trace_processor/importers/proto/track_event_module.h",
         "src/trace_processor/importers/proto/track_event_parser.cc",
@@ -2713,6 +2714,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:profiler_util
+perfetto_filegroup(
+    name = "src_trace_processor_util_profiler_util",
+    srcs = [
+        "src/trace_processor/util/profiler_util.cc",
+        "src/trace_processor/util/profiler_util.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:proto_profiler
 perfetto_filegroup(
     name = "src_trace_processor_util_proto_profiler",
@@ -5590,6 +5600,7 @@
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_profile_builder",
+        ":src_trace_processor_util_profiler_util",
         ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_json",
@@ -5759,6 +5770,7 @@
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_profile_builder",
+        ":src_trace_processor_util_profiler_util",
         ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_json",
@@ -5980,6 +5992,7 @@
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_profile_builder",
+        ":src_trace_processor_util_profiler_util",
         ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_json",
diff --git a/src/trace_processor/importers/common/BUILD.gn b/src/trace_processor/importers/common/BUILD.gn
index dac6e41..823edd8 100644
--- a/src/trace_processor/importers/common/BUILD.gn
+++ b/src/trace_processor/importers/common/BUILD.gn
@@ -44,6 +44,8 @@
     "slice_tracker.h",
     "slice_translation_table.cc",
     "slice_translation_table.h",
+    "stack_profile_tracker.cc",
+    "stack_profile_tracker.h",
     "system_info_tracker.cc",
     "system_info_tracker.h",
     "trace_parser.cc",
@@ -66,7 +68,10 @@
     "../../../base",
     "../../db:minimal",
     "../../storage",
+    "../../tables:tables",
     "../../types",
+    "../../util:profiler_util",
+    "../../util:stack_traces_util",
     "../fuchsia:fuchsia_record",
     "../systrace:systrace_line",
   ]
diff --git a/src/trace_processor/importers/common/stack_profile_tracker.cc b/src/trace_processor/importers/common/stack_profile_tracker.cc
new file mode 100644
index 0000000..ad57523
--- /dev/null
+++ b/src/trace_processor/importers/common/stack_profile_tracker.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/profiler_util.h"
+#include "src/trace_processor/util/stack_traces_util.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+std::string CleanBuildId(base::StringView build_id) {
+  if (build_id.empty()) {
+    return build_id.ToStdString();
+  }
+  // If the build_id is 33 characters long, we assume it's a Breakpad debug
+  // identifier which is already in Hex and doesn't need conversion.
+  // TODO(b/148109467): Remove workaround once all active Chrome versions
+  // write raw bytes instead of a string as build_id.
+  if (util::IsHexModuleId(build_id)) {
+    return build_id.ToStdString();
+  }
+
+  return base::ToHex(build_id.data(), build_id.size());
+}
+
+}  // namespace
+
+std::vector<FrameId> StackProfileTracker::JavaFramesForName(
+    NameInPackage name) const {
+  if (const auto* frames = java_frames_for_name_.Find(name); frames) {
+    return *frames;
+  }
+  return {};
+}
+
+std::vector<MappingId> StackProfileTracker::FindMappingRow(
+    StringId name,
+    StringId build_id) const {
+  if (const auto* mappings =
+          mappings_by_name_and_build_id_.Find(std::make_pair(name, build_id));
+      mappings) {
+    return *mappings;
+  }
+  return {};
+}
+
+std::vector<FrameId> StackProfileTracker::FindFrameIds(MappingId mapping_id,
+                                                       uint64_t rel_pc) const {
+  if (const auto* frames =
+          frame_by_mapping_and_rel_pc_.Find(std::make_pair(mapping_id, rel_pc));
+      frames) {
+    return *frames;
+  }
+  return {};
+}
+
+MappingId StackProfileTracker::InternMapping(
+    const CreateMappingParams& params) {
+  tables::StackProfileMappingTable::Row row;
+  row.build_id = InternBuildId(params.build_id);
+  row.exact_offset = static_cast<int64_t>(params.exact_offset);
+  row.start_offset = static_cast<int64_t>(params.start_offset);
+  row.start = static_cast<int64_t>(params.start);
+  row.end = static_cast<int64_t>(params.end);
+  row.load_bias = static_cast<int64_t>(params.load_bias);
+  row.name = context_->storage->InternString(params.name);
+
+  if (MappingId* id = mapping_unique_row_index_.Find(row); id) {
+    return *id;
+  }
+
+  MappingId mapping_id =
+      context_->storage->mutable_stack_profile_mapping_table()->Insert(row).id;
+  mapping_unique_row_index_.Insert(row, mapping_id);
+  mappings_by_name_and_build_id_[{row.name, row.build_id}].push_back(
+      mapping_id);
+  return mapping_id;
+}
+
+CallsiteId StackProfileTracker::InternCallsite(
+    std::optional<CallsiteId> parent_callsite_id,
+    FrameId frame_id,
+    uint32_t depth) {
+  tables::StackProfileCallsiteTable::Row row{depth, parent_callsite_id,
+                                             frame_id};
+  if (CallsiteId* id = callsite_unique_row_index_.Find(row); id) {
+    return *id;
+  }
+
+  CallsiteId callsite_id =
+      context_->storage->mutable_stack_profile_callsite_table()->Insert(row).id;
+  callsite_unique_row_index_.Insert(row, callsite_id);
+  return callsite_id;
+}
+
+FrameId StackProfileTracker::InternFrame(MappingId mapping_id,
+                                         uint64_t rel_pc,
+                                         base::StringView function_name) {
+  tables::StackProfileFrameTable::Row row;
+  row.mapping = mapping_id;
+  row.rel_pc = static_cast<int64_t>(rel_pc);
+  row.name = context_->storage->InternString(function_name);
+
+  if (FrameId* id = frame_unique_row_index_.Find(row); id) {
+    return *id;
+  }
+
+  FrameId frame_id =
+      context_->storage->mutable_stack_profile_frame_table()->Insert(row).id;
+  frame_unique_row_index_.Insert(row, frame_id);
+  frame_by_mapping_and_rel_pc_[{mapping_id, rel_pc}].push_back(frame_id);
+
+  if (function_name.find('.') != base::StringView::npos) {
+    // Java frames always contain a '.'
+    base::StringView mapping_name = context_->storage->GetString(
+        context_->storage->stack_profile_mapping_table()
+            .FindById(mapping_id)
+            ->name());
+    std::optional<std::string> package =
+        PackageFromLocation(context_->storage.get(), mapping_name);
+    if (package) {
+      NameInPackage nip{row.name, context_->storage->InternString(
+                                      base::StringView(*package))};
+      java_frames_for_name_[nip].push_back(frame_id);
+    } else if (mapping_name.find("/memfd:") == 0) {
+      NameInPackage nip{row.name, context_->storage->InternString("memfd")};
+      java_frames_for_name_[nip].push_back(frame_id);
+    }
+  }
+
+  return frame_id;
+}
+
+StringId StackProfileTracker::InternBuildId(base::StringView build_id) {
+  return context_->storage->InternString(
+      base::StringView(CleanBuildId(build_id)));
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/common/stack_profile_tracker.h b/src/trace_processor/importers/common/stack_profile_tracker.h
new file mode 100644
index 0000000..a1067b8
--- /dev/null
+++ b/src/trace_processor/importers/common/stack_profile_tracker.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_STACK_PROFILE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_STACK_PROFILE_TRACKER_H_
+
+#include <cstdint>
+#include <optional>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/profiler_tables_py.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct NameInPackage {
+  StringId name;
+  StringId package;
+
+  bool operator==(const NameInPackage& b) const {
+    return std::tie(name, package) == std::tie(b.name, b.package);
+  }
+
+  struct Hasher {
+    size_t operator()(const NameInPackage& o) const {
+      return static_cast<size_t>(
+          base::Hasher::Combine(o.name.raw_id(), o.package.raw_id()));
+    }
+  };
+};
+
+class TraceProcessorContext;
+
+class StackProfileTracker {
+ public:
+  struct CreateMappingParams {
+    base::StringView build_id;
+    uint64_t exact_offset;
+    uint64_t start_offset;
+    uint64_t start;
+    uint64_t end;
+    uint64_t load_bias;
+    base::StringView name;
+  };
+
+  explicit StackProfileTracker(TraceProcessorContext* context)
+      : context_(context) {}
+
+  std::vector<FrameId> JavaFramesForName(NameInPackage name) const;
+  std::vector<MappingId> FindMappingRow(StringId name, StringId build_id) const;
+  std::vector<FrameId> FindFrameIds(MappingId mapping_id,
+                                    uint64_t rel_pc) const;
+
+  MappingId InternMapping(const CreateMappingParams& params);
+  CallsiteId InternCallsite(std::optional<CallsiteId> parent_callsite_id,
+                            FrameId frame_id,
+                            uint32_t depth);
+  FrameId InternFrame(MappingId mapping_id,
+                      uint64_t rel_pc,
+                      base::StringView function_name);
+
+ private:
+  StringId InternBuildId(base::StringView build_id);
+
+  TraceProcessorContext* const context_;
+  base::FlatHashMap<tables::StackProfileMappingTable::Row, MappingId>
+      mapping_unique_row_index_;
+  base::FlatHashMap<tables::StackProfileCallsiteTable::Row, CallsiteId>
+      callsite_unique_row_index_;
+  base::FlatHashMap<tables::StackProfileFrameTable::Row, FrameId>
+      frame_unique_row_index_;
+
+  struct MappingHasher {
+    size_t operator()(const std::pair<StringId, StringId>& o) const {
+      return static_cast<size_t>(
+          base::Hasher::Combine(o.first.raw_id(), o.second.raw_id()));
+    }
+  };
+  base::FlatHashMap<std::pair<StringId, StringId>,
+                    std::vector<MappingId>,
+                    MappingHasher>
+      mappings_by_name_and_build_id_;
+
+  struct FrameHasher {
+    size_t operator()(const std::pair<MappingId, uint64_t>& o) const {
+      return static_cast<size_t>(
+          base::Hasher::Combine(o.first.value, o.second));
+    }
+  };
+  base::FlatHashMap<std::pair<MappingId, uint64_t>,
+                    std::vector<FrameId>,
+                    FrameHasher>
+      frame_by_mapping_and_rel_pc_;
+
+  base::FlatHashMap<NameInPackage, std::vector<FrameId>, NameInPackage::Hasher>
+      java_frames_for_name_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_STACK_PROFILE_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/BUILD.gn b/src/trace_processor/importers/ftrace/BUILD.gn
index 8065a41..c618978 100644
--- a/src/trace_processor/importers/ftrace/BUILD.gn
+++ b/src/trace_processor/importers/ftrace/BUILD.gn
@@ -68,6 +68,7 @@
     "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/ftrace:zero",
     "../../../../protos/perfetto/trace/interned_data:zero",
+    "../../../../protos/perfetto/trace/profiling:zero",
     "../../../protozero",
     "../../sorter",
     "../../storage",
@@ -76,6 +77,7 @@
     "../common:parser_types",
     "../i2c:full",
     "../proto:minimal",
+    "../proto:packet_sequence_state_generation_hdr",
     "../syscalls:full",
     "../systrace:systrace_parser",
   ]
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index cfed1d1..951c03d 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -32,7 +32,7 @@
 #include "src/trace_processor/importers/ftrace/v4l2_tracker.h"
 #include "src/trace_processor/importers/ftrace/virtio_video_tracker.h"
 #include "src/trace_processor/importers/i2c/i2c_tracker.h"
-#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
 #include "src/trace_processor/importers/syscalls/syscall_tracker.h"
 #include "src/trace_processor/importers/systrace/systrace_parser.h"
 #include "src/trace_processor/storage/stats.h"
@@ -85,6 +85,7 @@
 #include "protos/perfetto/trace/ftrace/vmscan.pbzero.h"
 #include "protos/perfetto/trace/ftrace/workqueue.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
index e61a137..d644742 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
@@ -29,12 +29,12 @@
 #include "src/trace_processor/importers/common/metadata_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
 #include "src/trace_processor/importers/proto/additional_modules.h"
 #include "src/trace_processor/importers/proto/default_modules.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/metadata.h"
 #include "src/trace_processor/storage/trace_storage.h"
@@ -236,8 +236,7 @@
     context_.track_tracker.reset(new TrackTracker(&context_));
     context_.global_args_tracker.reset(
         new GlobalArgsTracker(context_.storage.get()));
-    context_.global_stack_profile_tracker.reset(
-        new GlobalStackProfileTracker());
+    context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.args_translation_table.reset(new ArgsTranslationTable(storage_));
     context_.metadata_tracker.reset(
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index a901ae1..2eaa5fe 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -26,8 +26,6 @@
     "chrome_system_probes_parser.h",
     "default_modules.cc",
     "default_modules.h",
-    "heap_profile_tracker.cc",
-    "heap_profile_tracker.h",
     "memory_tracker_snapshot_module.cc",
     "memory_tracker_snapshot_module.h",
     "memory_tracker_snapshot_parser.cc",
@@ -44,10 +42,10 @@
     "perf_sample_tracker.h",
     "profile_module.cc",
     "profile_module.h",
+    "profile_packet_sequence_state.cc",
+    "profile_packet_sequence_state.h",
     "profile_packet_utils.cc",
     "profile_packet_utils.h",
-    "profiler_util.cc",
-    "profiler_util.h",
     "proto_incremental_state.h",
     "proto_trace_parser.cc",
     "proto_trace_parser.h",
@@ -55,8 +53,8 @@
     "proto_trace_reader.h",
     "proto_trace_tokenizer.cc",
     "proto_trace_tokenizer.h",
-    "stack_profile_tracker.cc",
-    "stack_profile_tracker.h",
+    "stack_profile_sequence_state.cc",
+    "stack_profile_sequence_state.h",
     "track_event_module.cc",
     "track_event_module.h",
     "track_event_parser.cc",
@@ -94,6 +92,7 @@
     "../../tables",
     "../../types",
     "../../util:gzip",
+    "../../util:profiler_util",
     "../../util:stack_traces_util",
     "../common",
     "../common:parser_types",
@@ -155,6 +154,7 @@
     ":gen_cc_statsd_atoms_descriptor",
     ":gen_cc_trace_descriptor",
     ":minimal",
+    ":packet_sequence_state_generation_hdr",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/ext/traced:sys_stats_counters",
     "../../../../protos/perfetto/common:zero",
@@ -178,8 +178,10 @@
     "../../tables",
     "../../types",
     "../../util:descriptors",
+    "../../util:profiler_util",
     "../../util:proto_profiler",
     "../../util:proto_to_args_parser",
+    "../../util:stack_traces_util",
     "../common",
     "../common:parser_types",
     "../etw:full",
@@ -243,9 +245,9 @@
   sources = [
     "active_chrome_processes_tracker_unittest.cc",
     "heap_graph_tracker_unittest.cc",
-    "heap_profile_tracker_unittest.cc",
     "network_trace_module_unittest.cc",
     "perf_sample_tracker_unittest.cc",
+    "profile_packet_sequence_state_unittest.cc",
     "proto_trace_parser_unittest.cc",
     "string_encoding_utils_unittests.cc",
   ]
@@ -273,6 +275,8 @@
     "../../storage",
     "../../types",
     "../../util:descriptors",
+    "../../util:profiler_util",
+    "../../util:stack_traces_util",
     "../common",
     "../ftrace:full",
   ]
diff --git a/src/trace_processor/importers/proto/heap_graph_module.cc b/src/trace_processor/importers/proto/heap_graph_module.cc
index cc1d7ad..93fd468 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.cc
+++ b/src/trace_processor/importers/proto/heap_graph_module.cc
@@ -19,9 +19,9 @@
 #include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
-#include "src/trace_processor/importers/proto/profiler_util.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/profiler_util.h"
 
 #include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.cc b/src/trace_processor/importers/proto/heap_graph_tracker.cc
index fcbd054..2e95889 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.cc
@@ -22,8 +22,8 @@
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
-#include "src/trace_processor/importers/proto/profiler_util.h"
 #include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/util/profiler_util.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
index 70c7398..ebc2a5e 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
@@ -18,7 +18,7 @@
 
 #include "perfetto/base/logging.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/importers/proto/profiler_util.h"
+#include "src/trace_processor/util/profiler_util.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
diff --git a/src/trace_processor/importers/proto/heap_profile_tracker.cc b/src/trace_processor/importers/proto/heap_profile_tracker.cc
deleted file mode 100644
index ea20108..0000000
--- a/src/trace_processor/importers/proto/heap_profile_tracker.cc
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/types/trace_processor_context.h"
-
-#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
-#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-HeapProfileTracker::HeapProfileTracker(TraceProcessorContext* context)
-    : context_(context),
-      empty_(context_->storage->InternString({"", 0})),
-      art_heap_(context_->storage->InternString("com.android.art")) {}
-
-HeapProfileTracker::~HeapProfileTracker() = default;
-
-void HeapProfileTracker::SetProfilePacketIndex(uint32_t seq_id,
-                                               uint64_t index) {
-  SequenceState& sequence_state = sequence_state_[seq_id];
-  bool dropped_packet = false;
-  // heapprofd starts counting at index = 0.
-  if (!sequence_state.prev_index && index != 0) {
-    dropped_packet = true;
-  }
-
-  if (sequence_state.prev_index && *sequence_state.prev_index + 1 != index) {
-    dropped_packet = true;
-  }
-
-  if (dropped_packet) {
-    if (sequence_state.prev_index) {
-      PERFETTO_ELOG("Missing packets between %" PRIu64 " and %" PRIu64,
-                    *sequence_state.prev_index, index);
-    } else {
-      PERFETTO_ELOG("Invalid first packet index %" PRIu64 " (!= 0)", index);
-    }
-
-    context_->storage->IncrementStats(stats::heapprofd_missing_packet);
-  }
-  sequence_state.prev_index = index;
-}
-
-void HeapProfileTracker::AddAllocation(
-    uint32_t seq_id,
-    SequenceStackProfileTracker* sequence_stack_profile_tracker,
-    const SourceAllocation& alloc,
-    const SequenceStackProfileTracker::InternLookup* intern_lookup) {
-  SequenceState& sequence_state = sequence_state_[seq_id];
-
-  auto opt_callstack_id = sequence_stack_profile_tracker->FindOrInsertCallstack(
-      alloc.callstack_id, intern_lookup);
-  if (!opt_callstack_id)
-    return;
-
-  CallsiteId callstack_id = *opt_callstack_id;
-
-  UniquePid upid = context_->process_tracker->GetOrCreateProcess(
-      static_cast<uint32_t>(alloc.pid));
-
-  tables::HeapProfileAllocationTable::Row alloc_row{
-      alloc.timestamp,
-      upid,
-      alloc.heap_name,
-      callstack_id,
-      static_cast<int64_t>(alloc.alloc_count),
-      static_cast<int64_t>(alloc.self_allocated)};
-
-  tables::HeapProfileAllocationTable::Row free_row{
-      alloc.timestamp,
-      upid,
-      alloc.heap_name,
-      callstack_id,
-      -static_cast<int64_t>(alloc.free_count),
-      -static_cast<int64_t>(alloc.self_freed)};
-
-  auto prev_alloc_it = sequence_state.prev_alloc.find({upid, callstack_id});
-  if (prev_alloc_it == sequence_state.prev_alloc.end()) {
-    std::tie(prev_alloc_it, std::ignore) = sequence_state.prev_alloc.emplace(
-        std::make_pair(upid, callstack_id),
-        tables::HeapProfileAllocationTable::Row{});
-  }
-
-  tables::HeapProfileAllocationTable::Row& prev_alloc = prev_alloc_it->second;
-
-  auto prev_free_it = sequence_state.prev_free.find({upid, callstack_id});
-  if (prev_free_it == sequence_state.prev_free.end()) {
-    std::tie(prev_free_it, std::ignore) = sequence_state.prev_free.emplace(
-        std::make_pair(upid, callstack_id),
-        tables::HeapProfileAllocationTable::Row{});
-  }
-
-  tables::HeapProfileAllocationTable::Row& prev_free = prev_free_it->second;
-
-  std::set<CallsiteId>& callstacks_for_source_callstack_id =
-      sequence_state.seen_callstacks[SourceAllocationIndex{
-          upid, alloc.callstack_id, alloc.heap_name}];
-  bool new_callstack;
-  std::tie(std::ignore, new_callstack) =
-      callstacks_for_source_callstack_id.emplace(callstack_id);
-
-  if (new_callstack) {
-    sequence_state.alloc_correction[alloc.callstack_id] = prev_alloc;
-    sequence_state.free_correction[alloc.callstack_id] = prev_free;
-  }
-
-  auto alloc_correction_it =
-      sequence_state.alloc_correction.find(alloc.callstack_id);
-  if (alloc_correction_it != sequence_state.alloc_correction.end()) {
-    const auto& alloc_correction = alloc_correction_it->second;
-    alloc_row.count += alloc_correction.count;
-    alloc_row.size += alloc_correction.size;
-  }
-
-  auto free_correction_it =
-      sequence_state.free_correction.find(alloc.callstack_id);
-  if (free_correction_it != sequence_state.free_correction.end()) {
-    const auto& free_correction = free_correction_it->second;
-    free_row.count += free_correction.count;
-    free_row.size += free_correction.size;
-  }
-
-  tables::HeapProfileAllocationTable::Row alloc_delta = alloc_row;
-  tables::HeapProfileAllocationTable::Row free_delta = free_row;
-
-  alloc_delta.count -= prev_alloc.count;
-  alloc_delta.size -= prev_alloc.size;
-
-  free_delta.count -= prev_free.count;
-  free_delta.size -= prev_free.size;
-
-  if (alloc_delta.count < 0 || alloc_delta.size < 0 || free_delta.count > 0 ||
-      free_delta.size > 0) {
-    PERFETTO_DLOG("Non-monotonous allocation.");
-    context_->storage->IncrementIndexedStats(stats::heapprofd_malformed_packet,
-                                             static_cast<int>(upid));
-    return;
-  }
-
-  // Dump at max profiles do not have .count set.
-  if (alloc_delta.count || alloc_delta.size) {
-    context_->storage->mutable_heap_profile_allocation_table()->Insert(
-        alloc_delta);
-  }
-
-  // ART only reports allocations, and not frees. This throws off our logic
-  // that assumes that if a new object was allocated with the same address,
-  // the old one has to have been freed in the meantime.
-  // See HeapTracker::RecordMalloc in bookkeeping.cc.
-  if (alloc.heap_name != art_heap_ && (free_delta.count || free_delta.size)) {
-    context_->storage->mutable_heap_profile_allocation_table()->Insert(
-        free_delta);
-  }
-
-  prev_alloc = alloc_row;
-  prev_free = free_row;
-}
-
-void HeapProfileTracker::StoreAllocation(uint32_t seq_id,
-                                         SourceAllocation alloc) {
-  SequenceState& sequence_state = sequence_state_[seq_id];
-  sequence_state.pending_allocs.emplace_back(std::move(alloc));
-}
-
-void HeapProfileTracker::CommitAllocations(
-    uint32_t seq_id,
-    SequenceStackProfileTracker* sequence_stack_profile_tracker,
-    const SequenceStackProfileTracker::InternLookup* intern_lookup) {
-  SequenceState& sequence_state = sequence_state_[seq_id];
-  for (const auto& p : sequence_state.pending_allocs)
-    AddAllocation(seq_id, sequence_stack_profile_tracker, p, intern_lookup);
-  sequence_state.pending_allocs.clear();
-}
-
-void HeapProfileTracker::FinalizeProfile(
-    uint32_t seq_id,
-    SequenceStackProfileTracker* sequence_stack_profile_tracker,
-    const SequenceStackProfileTracker::InternLookup* intern_lookup) {
-  CommitAllocations(seq_id, sequence_stack_profile_tracker, intern_lookup);
-  sequence_stack_profile_tracker->ClearIndices();
-}
-
-void HeapProfileTracker::NotifyEndOfFile() {
-  for (const auto& key_and_sequence_state : sequence_state_) {
-    const SequenceState& sequence_state = key_and_sequence_state.second;
-    if (!sequence_state.pending_allocs.empty()) {
-      context_->storage->IncrementStats(stats::heapprofd_non_finalized_profile);
-    }
-  }
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/heap_profile_tracker.h b/src/trace_processor/importers/proto/heap_profile_tracker.h
deleted file mode 100644
index 5ed5394..0000000
--- a/src/trace_processor/importers/proto/heap_profile_tracker.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
-
-#include <optional>
-#include <set>
-#include <unordered_map>
-
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
-#include "src/trace_processor/storage/trace_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class TraceProcessorContext;
-
-class HeapProfileTracker {
- public:
-  struct SourceAllocation {
-    uint64_t pid = 0;
-    // This is int64_t, because we get this from the TraceSorter which also
-    // converts this for us.
-    int64_t timestamp = 0;
-    StringPool::Id heap_name;
-    SequenceStackProfileTracker::SourceCallstackId callstack_id = 0;
-    uint64_t self_allocated = 0;
-    uint64_t self_freed = 0;
-    uint64_t alloc_count = 0;
-    uint64_t free_count = 0;
-  };
-
-  void SetProfilePacketIndex(uint32_t seq_id, uint64_t id);
-
-  explicit HeapProfileTracker(TraceProcessorContext* context);
-
-  void StoreAllocation(uint32_t seq_id, SourceAllocation);
-
-  // Call after the last profile packet of a dump to commit the allocations
-  // that had been stored using StoreAllocation and clear internal indices
-  // for that dump.
-  void FinalizeProfile(
-      uint32_t seq_id,
-      SequenceStackProfileTracker* sequence_stack_profile_tracker,
-      const SequenceStackProfileTracker::InternLookup* lookup);
-
-  // Only commit the allocations that had been stored using StoreAllocations.
-  // This is only needed in tests, use FinalizeProfile instead.
-  void CommitAllocations(
-      uint32_t seq_id,
-      SequenceStackProfileTracker* sequence_stack_profile_tracker,
-      const SequenceStackProfileTracker::InternLookup* lookup);
-
-  void NotifyEndOfFile();
-
-  ~HeapProfileTracker();
-
- private:
-  void AddAllocation(
-      uint32_t seq_id,
-      SequenceStackProfileTracker* sequence_stack_profile_tracker,
-      const SourceAllocation&,
-      const SequenceStackProfileTracker::InternLookup* intern_lookup = nullptr);
-  struct SourceAllocationIndex {
-    UniquePid upid;
-    SequenceStackProfileTracker::SourceCallstackId src_callstack_id;
-    StringPool::Id heap_name;
-    bool operator<(const SourceAllocationIndex& o) const {
-      return std::tie(upid, src_callstack_id, heap_name) <
-             std::tie(o.upid, o.src_callstack_id, o.heap_name);
-    }
-  };
-  struct SequenceState {
-    std::vector<SourceAllocation> pending_allocs;
-
-    std::unordered_map<std::pair<UniquePid, CallsiteId>,
-                       tables::HeapProfileAllocationTable::Row>
-        prev_alloc;
-    std::unordered_map<std::pair<UniquePid, CallsiteId>,
-                       tables::HeapProfileAllocationTable::Row>
-        prev_free;
-
-    // For continuous dumps, we only store the delta in the data-base. To do
-    // this, we subtract the previous dump's value. Sometimes, we should not
-    // do that subtraction, because heapprofd garbage collects stacks that
-    // have no unfreed allocations. If the application then allocations again
-    // at that stack, it gets recreated and initialized to zero.
-    //
-    // To correct for this, we add the previous' stacks value to the current
-    // one, and then handle it as normal. If it is the first time we see a
-    // SourceCallstackId for a CallsiteId, we put the previous value into
-    // the correction maps below.
-    std::map<SourceAllocationIndex, std::set<CallsiteId>> seen_callstacks;
-    std::map<SequenceStackProfileTracker::SourceCallstackId,
-             tables::HeapProfileAllocationTable::Row>
-        alloc_correction;
-    std::map<SequenceStackProfileTracker::SourceCallstackId,
-             tables::HeapProfileAllocationTable::Row>
-        free_correction;
-
-    std::optional<uint64_t> prev_index;
-  };
-  std::map<uint32_t, SequenceState> sequence_state_;
-  TraceProcessorContext* const context_;
-  const StringId empty_;
-  const StringId art_heap_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index 8c29222..2ad0c7b 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -19,13 +19,12 @@
 
 #include <stdint.h>
 
-#include <unordered_map>
+#include <memory>
+#include <type_traits>
 #include <vector>
 
 #include "perfetto/base/compiler.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
-#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/util/interned_message_view.h"
 
@@ -34,19 +33,8 @@
 
 class PacketSequenceState {
  public:
-  // Helper to keep per sequence state. These are not reset when the generation
-  // changes.
-  // Trackers or parsers can add their custom per sequence state here instead of
-  // keeping a map from seq_id to some internal state.
-  // TODO(carlscab): We should come up with a nicer API that allows extensions
-  // to be notified of generation changes.
-  // TODO(carlscab): There is some existing code that could use this. Migrate.
-  struct ExtensibleSequenceState {
-    std::unique_ptr<Destructible> v8_sequence_state;
-  };
-
   explicit PacketSequenceState(TraceProcessorContext* context)
-      : context_(context), sequence_stack_profile_tracker_(context) {
+      : context_(context) {
     current_generation_.reset(
         new PacketSequenceStateGeneration(this, generation_index_++));
   }
@@ -87,7 +75,7 @@
     // sequence. Add a new generation with the updated defaults but the
     // current generation's interned data state.
     current_generation_.reset(new PacketSequenceStateGeneration(
-        this, generation_index_++, current_generation_->interned_data_,
+        this, generation_index_++, current_generation_.get(),
         std::move(defaults)));
   }
 
@@ -119,14 +107,6 @@
 
   bool IsIncrementalStateValid() const { return !packet_loss_; }
 
-  SequenceStackProfileTracker& sequence_stack_profile_tracker() {
-    return sequence_stack_profile_tracker_;
-  }
-
-  ExtensibleSequenceState& extensible_sequence_state() {
-    return extensible_sequence_state_;
-  }
-
   // Returns a ref-counted ptr to the current generation.
   RefPtr<PacketSequenceStateGeneration> current_generation() const {
     return current_generation_;
@@ -174,20 +154,8 @@
   int64_t track_event_thread_instruction_count_ = 0;
 
   RefPtr<PacketSequenceStateGeneration> current_generation_;
-  SequenceStackProfileTracker sequence_stack_profile_tracker_;
-  ExtensibleSequenceState extensible_sequence_state_;
 };
 
-template <uint32_t FieldId, typename MessageType>
-typename MessageType::Decoder*
-PacketSequenceStateGeneration::LookupInternedMessage(uint64_t iid) {
-  auto* interned_message_view = GetInternedMessageView(FieldId, iid);
-  if (!interned_message_view)
-    return nullptr;
-
-  return interned_message_view->template GetOrCreateDecoder<MessageType>();
-}
-
 }  // namespace trace_processor
 }  // namespace perfetto
 
diff --git a/src/trace_processor/importers/proto/packet_sequence_state_generation.cc b/src/trace_processor/importers/proto/packet_sequence_state_generation.cc
index 6e8e98d..1e89096 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state_generation.cc
+++ b/src/trace_processor/importers/proto/packet_sequence_state_generation.cc
@@ -15,12 +15,38 @@
  */
 
 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
+#include <cstddef>
 
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
 
+PacketSequenceStateGeneration::PacketSequenceStateGeneration(
+    PacketSequenceState* state,
+    size_t generation_index,
+    PacketSequenceStateGeneration* prev_gen,
+    TraceBlobView defaults)
+    : state_(state),
+      generation_index_(generation_index),
+      interned_data_(prev_gen->interned_data_),
+      trace_packet_defaults_(InternedMessageView(std::move(defaults))),
+      trackers_(prev_gen->trackers_) {
+  for (auto& t : trackers_) {
+    if (t.get() != nullptr) {
+      t->set_generation(this);
+    }
+  }
+}
+
+PacketSequenceStateGeneration::InternedDataTracker::~InternedDataTracker() =
+    default;
+
+TraceProcessorContext* PacketSequenceStateGeneration::GetContext() const {
+  return state_->context();
+}
+
 void PacketSequenceStateGeneration::InternMessage(uint32_t field_id,
                                                   TraceBlobView message) {
   constexpr auto kIidFieldNumber = 1;
diff --git a/src/trace_processor/importers/proto/packet_sequence_state_generation.h b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
index 9c7aa5a..1f05d5c 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state_generation.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
@@ -17,10 +17,17 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_SEQUENCE_STATE_GENERATION_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_SEQUENCE_STATE_GENERATION_H_
 
+#include <array>
+#include <cstddef>
+#include <memory>
 #include <optional>
+#include <tuple>
+#include <type_traits>
 #include <unordered_map>
 
+#include "perfetto/public/compiler.h"
 #include "perfetto/trace_processor/ref_counted.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/util/interned_message_view.h"
 
 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
@@ -35,9 +42,65 @@
     std::unordered_map<uint32_t /*field_id*/, InternedMessageMap>;
 
 class PacketSequenceState;
+class TraceProcessorContext;
+
+class StackProfileSequenceState;
+class ProfilePacketSequenceState;
+class V8SequenceState;
+
+using InternedDataTrackers = std::tuple<StackProfileSequenceState,
+                                        ProfilePacketSequenceState,
+                                        V8SequenceState>;
 
 class PacketSequenceStateGeneration : public RefCounted {
  public:
+  // Base class to add custom sequence state. This state is keep per sequence
+  // and per incremental state interval, that is, each time incremental state is
+  // reset a new instance is created but not each time `TracePacketDefaults` are
+  // updated. Note that this means that different
+  // `PacketSequenceStateGeneration` instances might point to the same
+  // `InternedDataTracker` (because they only differ in their
+  // `TracePacketDefaults`).
+  //
+  // ATTENTION: You should not create instances of these classes yourself but
+  // use the `PacketSequenceStateGeneration::GetOrCreate<>' method instead.
+  class InternedDataTracker : public RefCounted {
+   public:
+    virtual ~InternedDataTracker();
+
+   protected:
+    template <uint32_t FieldId, typename MessageType>
+    typename MessageType::Decoder* LookupInternedMessage(uint64_t iid) {
+      return generation_->LookupInternedMessage<FieldId, MessageType>(iid);
+    }
+
+    InternedMessageView* GetInternedMessageView(uint32_t field_id,
+                                                uint64_t iid) {
+      return generation_->GetInternedMessageView(field_id, iid);
+    }
+
+    template <typename T>
+    std::remove_cv_t<T>* GetOrCreate() {
+      return generation_->GetOrCreate<T>();
+    }
+
+   private:
+    friend PacketSequenceStateGeneration;
+    // Called when the a new generation is created as a result of
+    // `TracePacketDefaults` being updated.
+    void set_generation(PacketSequenceStateGeneration* generation) {
+      generation_ = generation;
+    }
+
+    // Note: A `InternedDataTracker` instance can be linked to multiple
+    // `PacketSequenceStateGeneration` instances (when there are multiple
+    // `TracePacketDefaults` in the same interning context). `generation_` will
+    // point to the latest one. We keep this member private to prevent misuse /
+    // confusion around this fact. Instead subclasses should access the public
+    // methods of this class to get any interned data.
+    PacketSequenceStateGeneration* generation_ = nullptr;
+  };
+
   // Returns |nullptr| if the message with the given |iid| was not found (also
   // records a stat in this case).
   template <uint32_t FieldId, typename MessageType>
@@ -79,21 +142,47 @@
   PacketSequenceState* state() const { return state_; }
   size_t generation_index() const { return generation_index_; }
 
+  // Extension point for custom sequence state. To add new per sequence state
+  // just subclass ´PacketSequenceStateGeneration´ and get your sequence bound
+  // instance by calling this method.
+  template <typename T>
+  std::remove_cv_t<T>* GetOrCreate();
+
  private:
   friend class PacketSequenceState;
 
+  // Helper to find the index in a tuple of a given type. Lookups are done
+  // ignoring cv qualifiers. If no index is found size of the tuple is returned.
+  //
+  // ATTENTION: Duplicate types in the tuple will trigger a compiler error.
+  template <typename Tuple, typename Type, size_t index = 0>
+  static constexpr size_t FindUniqueType() {
+    constexpr size_t kSize = std::tuple_size_v<Tuple>;
+    if constexpr (index < kSize) {
+      using TypeAtIndex = typename std::tuple_element<index, Tuple>::type;
+      if constexpr (std::is_same_v<std::remove_cv_t<Type>,
+                                   std::remove_cv_t<TypeAtIndex>>) {
+        static_assert(FindUniqueType<Tuple, Type, index + 1>() == kSize,
+                      "Duplicate types.");
+        return index;
+      } else {
+        return FindUniqueType<Tuple, Type, index + 1>();
+      }
+    } else {
+      return kSize;
+    }
+  }
+
   PacketSequenceStateGeneration(PacketSequenceState* state,
                                 size_t generation_index)
       : state_(state), generation_index_(generation_index) {}
 
   PacketSequenceStateGeneration(PacketSequenceState* state,
                                 size_t generation_index,
-                                InternedFieldMap interned_data,
-                                TraceBlobView defaults)
-      : state_(state),
-        generation_index_(generation_index),
-        interned_data_(interned_data),
-        trace_packet_defaults_(InternedMessageView(std::move(defaults))) {}
+                                PacketSequenceStateGeneration* prev_gen,
+                                TraceBlobView defaults);
+
+  TraceProcessorContext* GetContext() const;
 
   void InternMessage(uint32_t field_id, TraceBlobView message);
 
@@ -107,8 +196,34 @@
   size_t generation_index_;
   InternedFieldMap interned_data_;
   std::optional<InternedMessageView> trace_packet_defaults_;
+  std::array<RefPtr<InternedDataTracker>,
+             std::tuple_size_v<InternedDataTrackers>>
+      trackers_;
 };
 
+template <typename T>
+std::remove_cv_t<T>* PacketSequenceStateGeneration::GetOrCreate() {
+  constexpr size_t index = FindUniqueType<InternedDataTrackers, T>();
+  static_assert(index < std::tuple_size_v<InternedDataTrackers>, "Not found");
+  auto& ptr = trackers_[index];
+  if (PERFETTO_UNLIKELY(ptr.get() == nullptr)) {
+    ptr.reset(new T(GetContext()));
+    ptr->set_generation(this);
+  }
+
+  return static_cast<std::remove_cv_t<T>*>(ptr.get());
+}
+
+template <uint32_t FieldId, typename MessageType>
+typename MessageType::Decoder*
+PacketSequenceStateGeneration::LookupInternedMessage(uint64_t iid) {
+  auto* interned_message_view = GetInternedMessageView(FieldId, iid);
+  if (!interned_message_view)
+    return nullptr;
+
+  return interned_message_view->template GetOrCreateDecoder<MessageType>();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
 
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index c337869..61596d3 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -25,17 +25,18 @@
 #include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/perf_sample_tracker.h"
+#include "src/trace_processor/importers/proto/profile_packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
-#include "src/trace_processor/importers/proto/profiler_util.h"
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
 #include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/tables/profiler_tables_py.h"
 #include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/profiler_util.h"
 #include "src/trace_processor/util/stack_traces_util.h"
 
 #include "protos/perfetto/common/builtin_clock.pbzero.h"
@@ -91,8 +92,7 @@
       ParsePerfSample(ts, data.sequence_state.get(), decoder);
       return;
     case TracePacket::kProfilePacketFieldNumber:
-      ParseProfilePacket(ts, data.sequence_state.get(),
-                         decoder.trusted_packet_sequence_id(),
+      ParseProfilePacket(ts, data.sequence_state->state(),
                          decoder.profile_packet());
       return;
     case TracePacket::kModuleSymbolsFieldNumber:
@@ -152,9 +152,8 @@
 
   ProcessTracker* procs = context_->process_tracker.get();
   TraceStorage* storage = context_->storage.get();
-  SequenceStackProfileTracker& sequence_stack_profile_tracker =
-      sequence_state->state()->sequence_stack_profile_tracker();
-  ProfilePacketInternLookup intern_lookup(sequence_state);
+  StackProfileSequenceState& stack_profile_sequence_state =
+      *sequence_state->GetOrCreate<StackProfileSequenceState>();
 
   uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
   uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
@@ -171,8 +170,8 @@
       break;
     }
 
-    auto opt_cs_id = sequence_stack_profile_tracker.FindOrInsertCallstack(
-        *callstack_it, &intern_lookup);
+    auto opt_cs_id =
+        stack_profile_sequence_state.FindOrInsertCallstack(*callstack_it);
     if (!opt_cs_id) {
       context_->storage->IncrementStats(stats::stackprofile_parser_error);
       continue;
@@ -247,12 +246,11 @@
       ts, static_cast<double>(sample.timebase_count()),
       sampling_stream.timebase_track_id);
 
-  SequenceStackProfileTracker& stack_tracker =
-      sequence_state->state()->sequence_stack_profile_tracker();
-  ProfilePacketInternLookup intern_lookup(sequence_state);
+  StackProfileSequenceState& stack_profile_sequence_state =
+      *sequence_state->GetOrCreate<StackProfileSequenceState>();
   uint64_t callstack_iid = sample.callstack_iid();
   std::optional<CallsiteId> cs_id =
-      stack_tracker.FindOrInsertCallstack(callstack_iid, &intern_lookup);
+      stack_profile_sequence_state.FindOrInsertCallstack(callstack_iid);
 
   // A failed lookup of the interned callstack can mean either:
   // (a) This is a counter-only profile without callstacks. Due to an
@@ -298,45 +296,38 @@
   context_->storage->mutable_perf_sample_table()->Insert(sample_row);
 }
 
-void ProfileModule::ParseProfilePacket(
-    int64_t ts,
-    PacketSequenceStateGeneration* sequence_state,
-    uint32_t seq_id,
-    ConstBytes blob) {
+void ProfileModule::ParseProfilePacket(int64_t ts,
+                                       PacketSequenceState* sequence_state,
+                                       ConstBytes blob) {
+  ProfilePacketSequenceState& profile_packet_sequence_state =
+      *sequence_state->current_generation()
+           ->GetOrCreate<ProfilePacketSequenceState>();
   protos::pbzero::ProfilePacket::Decoder packet(blob.data, blob.size);
-  context_->heap_profile_tracker->SetProfilePacketIndex(seq_id, packet.index());
+  profile_packet_sequence_state.SetProfilePacketIndex(packet.index());
 
   for (auto it = packet.strings(); it; ++it) {
     protos::pbzero::InternedString::Decoder entry(*it);
-
     const char* str = reinterpret_cast<const char*>(entry.str().data);
     auto str_view = base::StringView(str, entry.str().size);
-    sequence_state->state()->sequence_stack_profile_tracker().AddString(
-        entry.iid(), str_view);
+    profile_packet_sequence_state.AddString(entry.iid(), str_view);
   }
 
   for (auto it = packet.mappings(); it; ++it) {
     protos::pbzero::Mapping::Decoder entry(*it);
-    SequenceStackProfileTracker::SourceMapping src_mapping =
-        ProfilePacketUtils::MakeSourceMapping(entry);
-    sequence_state->state()->sequence_stack_profile_tracker().AddMapping(
-        entry.iid(), src_mapping);
+    profile_packet_sequence_state.AddMapping(
+        entry.iid(), ProfilePacketUtils::MakeSourceMapping(entry));
   }
 
   for (auto it = packet.frames(); it; ++it) {
     protos::pbzero::Frame::Decoder entry(*it);
-    SequenceStackProfileTracker::SourceFrame src_frame =
-        ProfilePacketUtils::MakeSourceFrame(entry);
-    sequence_state->state()->sequence_stack_profile_tracker().AddFrame(
-        entry.iid(), src_frame);
+    profile_packet_sequence_state.AddFrame(
+        entry.iid(), ProfilePacketUtils::MakeSourceFrame(entry));
   }
 
   for (auto it = packet.callstacks(); it; ++it) {
     protos::pbzero::Callstack::Decoder entry(*it);
-    SequenceStackProfileTracker::SourceCallstack src_callstack =
-        ProfilePacketUtils::MakeSourceCallstack(entry);
-    sequence_state->state()->sequence_stack_profile_tracker().AddCallstack(
-        entry.iid(), src_callstack);
+    profile_packet_sequence_state.AddCallstack(
+        entry.iid(), ProfilePacketUtils::MakeSourceCallstack(entry));
   }
 
   for (auto it = packet.process_dumps(); it; ++it) {
@@ -406,7 +397,7 @@
     for (auto sample_it = entry.samples(); sample_it; ++sample_it) {
       protos::pbzero::ProfilePacket::HeapSample::Decoder sample(*sample_it);
 
-      HeapProfileTracker::SourceAllocation src_allocation;
+      ProfilePacketSequenceState::SourceAllocation src_allocation;
       src_allocation.pid = entry.pid();
       if (entry.heap_name().size != 0) {
         src_allocation.heap_name =
@@ -427,15 +418,11 @@
         src_allocation.free_count = sample.free_count();
       }
 
-      context_->heap_profile_tracker->StoreAllocation(seq_id, src_allocation);
+      profile_packet_sequence_state.StoreAllocation(src_allocation);
     }
   }
   if (!packet.continued()) {
-    PERFETTO_CHECK(sequence_state);
-    ProfilePacketInternLookup intern_lookup(sequence_state);
-    context_->heap_profile_tracker->FinalizeProfile(
-        seq_id, &sequence_state->state()->sequence_stack_profile_tracker(),
-        &intern_lookup);
+    profile_packet_sequence_state.FinalizeProfile();
   }
 }
 
@@ -451,7 +438,7 @@
         module_symbols.build_id().data, module_symbols.build_id().size)));
   }
 
-  auto mapping_ids = context_->global_stack_profile_tracker->FindMappingRow(
+  auto mapping_ids = context_->stack_profile_tracker->FindMappingRow(
       context_->storage->InternString(module_symbols.path()), build_id);
   if (mapping_ids.empty()) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
@@ -484,7 +471,7 @@
       context_->args_translation_table->AddNativeSymbolTranslationRule(
           mapping_id, address_symbols.address(), last_location);
       std::vector<FrameId> frame_ids =
-          context_->global_stack_profile_tracker->FindFrameIds(
+          context_->stack_profile_tracker->FindFrameIds(
               mapping_id, address_symbols.address());
 
       for (const FrameId frame_id : frame_ids) {
@@ -536,21 +523,16 @@
 
       std::vector<tables::StackProfileFrameTable::Id> frames;
       if (opt_package_name_id) {
-        const std::vector<tables::StackProfileFrameTable::Id>* pkg_frames =
-            context_->global_stack_profile_tracker->JavaFramesForName(
+        const std::vector<tables::StackProfileFrameTable::Id> pkg_frames =
+            context_->stack_profile_tracker->JavaFramesForName(
                 {*merged_obfuscated_id, *opt_package_name_id});
-        if (pkg_frames) {
-          frames.insert(frames.end(), pkg_frames->begin(), pkg_frames->end());
-        }
+        frames.insert(frames.end(), pkg_frames.begin(), pkg_frames.end());
       }
       if (opt_memfd_id) {
-        const std::vector<tables::StackProfileFrameTable::Id>* memfd_frames =
-            context_->global_stack_profile_tracker->JavaFramesForName(
+        const std::vector<tables::StackProfileFrameTable::Id> memfd_frames =
+            context_->stack_profile_tracker->JavaFramesForName(
                 {*merged_obfuscated_id, *opt_memfd_id});
-        if (memfd_frames) {
-          frames.insert(frames.end(), memfd_frames->begin(),
-                        memfd_frames->end());
-        }
+        frames.insert(frames.end(), memfd_frames.begin(), memfd_frames.end());
       }
 
       for (tables::StackProfileFrameTable::Id frame_id : frames) {
diff --git a/src/trace_processor/importers/proto/profile_module.h b/src/trace_processor/importers/proto/profile_module.h
index e6674d7..883fb7a 100644
--- a/src/trace_processor/importers/proto/profile_module.h
+++ b/src/trace_processor/importers/proto/profile_module.h
@@ -65,8 +65,7 @@
 
   // heap profiling:
   void ParseProfilePacket(int64_t ts,
-                          PacketSequenceStateGeneration*,
-                          uint32_t seq_id,
+                          PacketSequenceState*,
                           protozero::ConstBytes);
   void ParseDeobfuscationMapping(int64_t ts,
                                  PacketSequenceStateGeneration*,
diff --git a/src/trace_processor/importers/proto/profile_packet_sequence_state.cc b/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
new file mode 100644
index 0000000..9d889ef
--- /dev/null
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/profile_packet_sequence_state.h"
+
+#include "perfetto/base/flat_set.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
+#include "src/trace_processor/importers/proto/profile_packet_utils.h"
+#include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+const char kArtHeapName[] = "com.android.art";
+}
+
+ProfilePacketSequenceState::ProfilePacketSequenceState(
+    TraceProcessorContext* context)
+    : context_(context) {
+  strings_.Insert(0, "");
+}
+
+ProfilePacketSequenceState::~ProfilePacketSequenceState() = default;
+
+void ProfilePacketSequenceState::SetProfilePacketIndex(uint64_t index) {
+  bool dropped_packet = false;
+  // heapprofd starts counting at index = 0.
+  if (!prev_index.has_value() && index != 0) {
+    dropped_packet = true;
+  }
+
+  if (prev_index.has_value() && *prev_index + 1 != index) {
+    dropped_packet = true;
+  }
+
+  if (dropped_packet) {
+    context_->storage->IncrementStats(stats::heapprofd_missing_packet);
+  }
+  prev_index = index;
+}
+
+void ProfilePacketSequenceState::AddString(SourceStringId id,
+                                           base::StringView str) {
+  PERFETTO_CHECK(id != 0 || str.empty());
+  strings_.Insert(id, str.ToStdString());
+}
+
+void ProfilePacketSequenceState::AddMapping(SourceMappingId id,
+                                            const SourceMapping& mapping) {
+  StackProfileTracker::CreateMappingParams params;
+  if (std::string* str = strings_.Find(mapping.build_id); str) {
+    params.build_id = base::StringView(*str);
+  } else {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
+    return;
+  }
+  params.exact_offset = mapping.exact_offset;
+  params.start_offset = mapping.start_offset;
+  params.start = mapping.start;
+  params.end = mapping.end;
+  params.load_bias = mapping.load_bias;
+
+  std::vector<base::StringView> path_components;
+  path_components.reserve(mapping.name_ids.size());
+  for (SourceStringId string_id : mapping.name_ids) {
+    if (std::string* str = strings_.Find(string_id); str) {
+      path_components.push_back(base::StringView(*str));
+    } else {
+      context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
+      // For backward compatibility reasons we do not return an error but
+      // instead stop adding path components.
+      break;
+    }
+  }
+  std::string path = ProfilePacketUtils::MakeMappingName(path_components);
+  params.name = base::StringView(path);
+  MappingId mapping_id = context_->stack_profile_tracker->InternMapping(params);
+  mappings_.Insert(id, mapping_id);
+}
+
+void ProfilePacketSequenceState::AddFrame(SourceFrameId id,
+                                          const SourceFrame& frame) {
+  MappingId* mapping_id = mappings_.Find(frame.mapping_id);
+  if (!mapping_id) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
+    return;
+  }
+
+  std::string* function_name = strings_.Find(frame.name_id);
+  if (!function_name) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
+    return;
+  }
+
+  FrameId frame_id = context_->stack_profile_tracker->InternFrame(
+      *mapping_id, frame.rel_pc, base::StringView(*function_name));
+
+  frames_.Insert(id, frame_id);
+}
+
+void ProfilePacketSequenceState::AddCallstack(
+    SourceCallstackId id,
+    const SourceCallstack& callstack) {
+  std::optional<CallsiteId> parent_callsite_id;
+  uint32_t depth = 0;
+  for (SourceFrameId source_frame_id : callstack) {
+    FrameId* frame_id = frames_.Find(source_frame_id);
+    if (!frame_id) {
+      context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
+      return;
+    }
+    parent_callsite_id = context_->stack_profile_tracker->InternCallsite(
+        parent_callsite_id, *frame_id, depth);
+    ++depth;
+  }
+
+  if (!parent_callsite_id) {
+    context_->storage->IncrementStats(stats::stackprofile_empty_callstack);
+    return;
+  }
+
+  callstacks_.Insert(id, *parent_callsite_id);
+}
+
+void ProfilePacketSequenceState::StoreAllocation(
+    const SourceAllocation& alloc) {
+  pending_allocs_.push_back(std::move(alloc));
+}
+
+void ProfilePacketSequenceState::CommitAllocations() {
+  for (const SourceAllocation& alloc : pending_allocs_)
+    AddAllocation(alloc);
+  pending_allocs_.clear();
+}
+
+void ProfilePacketSequenceState::FinalizeProfile() {
+  CommitAllocations();
+  strings_.Clear();
+  mappings_.Clear();
+  frames_.Clear();
+  callstacks_.Clear();
+}
+
+FrameId ProfilePacketSequenceState::GetDatabaseFrameIdForTesting(
+    SourceFrameId source_frame_id) {
+  FrameId* frame_id = frames_.Find(source_frame_id);
+  if (!frame_id) {
+    PERFETTO_DLOG("Invalid frame.");
+    return {};
+  }
+  return *frame_id;
+}
+
+void ProfilePacketSequenceState::AddAllocation(const SourceAllocation& alloc) {
+  auto opt_callstack_id = FindOrInsertCallstack(alloc.callstack_id);
+  if (!opt_callstack_id)
+    return;
+
+  CallsiteId callstack_id = *opt_callstack_id;
+
+  UniquePid upid = context_->process_tracker->GetOrCreateProcess(
+      static_cast<uint32_t>(alloc.pid));
+
+  tables::HeapProfileAllocationTable::Row alloc_row{
+      alloc.timestamp,
+      upid,
+      alloc.heap_name,
+      callstack_id,
+      static_cast<int64_t>(alloc.alloc_count),
+      static_cast<int64_t>(alloc.self_allocated)};
+
+  tables::HeapProfileAllocationTable::Row free_row{
+      alloc.timestamp,
+      upid,
+      alloc.heap_name,
+      callstack_id,
+      -static_cast<int64_t>(alloc.free_count),
+      -static_cast<int64_t>(alloc.self_freed)};
+
+  auto* prev_alloc = prev_alloc_.Find({upid, callstack_id});
+  if (!prev_alloc) {
+    prev_alloc = prev_alloc_
+                     .Insert(std::make_pair(upid, callstack_id),
+                             tables::HeapProfileAllocationTable::Row{})
+                     .first;
+  }
+
+  auto* prev_free = prev_free_.Find({upid, callstack_id});
+  if (!prev_free) {
+    prev_free = prev_free_
+                    .Insert(std::make_pair(upid, callstack_id),
+                            tables::HeapProfileAllocationTable::Row{})
+                    .first;
+  }
+
+  base::FlatSet<CallsiteId>& callstacks_for_source_callstack_id =
+      seen_callstacks_[SourceAllocationIndex{upid, alloc.callstack_id,
+                                             alloc.heap_name}];
+  bool new_callstack;
+  std::tie(std::ignore, new_callstack) =
+      callstacks_for_source_callstack_id.insert(callstack_id);
+
+  if (new_callstack) {
+    alloc_correction_[alloc.callstack_id] = *prev_alloc;
+    free_correction_[alloc.callstack_id] = *prev_free;
+  }
+
+  const auto* alloc_correction = alloc_correction_.Find(alloc.callstack_id);
+  if (alloc_correction) {
+    alloc_row.count += alloc_correction->count;
+    alloc_row.size += alloc_correction->size;
+  }
+
+  const auto* free_correction = free_correction_.Find(alloc.callstack_id);
+  if (free_correction) {
+    free_row.count += free_correction->count;
+    free_row.size += free_correction->size;
+  }
+
+  tables::HeapProfileAllocationTable::Row alloc_delta = alloc_row;
+  tables::HeapProfileAllocationTable::Row free_delta = free_row;
+
+  alloc_delta.count -= prev_alloc->count;
+  alloc_delta.size -= prev_alloc->size;
+
+  free_delta.count -= prev_free->count;
+  free_delta.size -= prev_free->size;
+
+  if (alloc_delta.count < 0 || alloc_delta.size < 0 || free_delta.count > 0 ||
+      free_delta.size > 0) {
+    PERFETTO_DLOG("Non-monotonous allocation.");
+    context_->storage->IncrementIndexedStats(stats::heapprofd_malformed_packet,
+                                             static_cast<int>(upid));
+    return;
+  }
+
+  // Dump at max profiles do not have .count set.
+  if (alloc_delta.count || alloc_delta.size) {
+    context_->storage->mutable_heap_profile_allocation_table()->Insert(
+        alloc_delta);
+  }
+
+  // ART only reports allocations, and not frees. This throws off our logic
+  // that assumes that if a new object was allocated with the same address,
+  // the old one has to have been freed in the meantime.
+  // See HeapTracker::RecordMalloc in bookkeeping.cc.
+  if (context_->storage->GetString(alloc.heap_name) != kArtHeapName &&
+      (free_delta.count || free_delta.size)) {
+    context_->storage->mutable_heap_profile_allocation_table()->Insert(
+        free_delta);
+  }
+
+  *prev_alloc = alloc_row;
+  *prev_free = free_row;
+}
+
+std::optional<CallsiteId> ProfilePacketSequenceState::FindOrInsertCallstack(
+    uint64_t iid) {
+  if (CallsiteId* id = callstacks_.Find(iid); id) {
+    return *id;
+  }
+  return GetOrCreate<StackProfileSequenceState>()->FindOrInsertCallstack(iid);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/profile_packet_sequence_state.h b/src/trace_processor/importers/proto/profile_packet_sequence_state.h
new file mode 100644
index 0000000..678aab2
--- /dev/null
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_SEQUENCE_STATE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_SEQUENCE_STATE_H_
+
+#include <cstdint>
+#include "perfetto/base/flat_set.h"
+#include "perfetto/ext/base/flat_hash_map.h"
+
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_view.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
+#include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Keeps sequence specific state for profile packets.
+class ProfilePacketSequenceState final
+    : public PacketSequenceStateGeneration::InternedDataTracker {
+ public:
+  using SourceStringId = uint64_t;
+
+  struct SourceMapping {
+    SourceStringId build_id = 0;
+    uint64_t exact_offset = 0;
+    uint64_t start_offset = 0;
+    uint64_t start = 0;
+    uint64_t end = 0;
+    uint64_t load_bias = 0;
+    std::vector<SourceStringId> name_ids;
+  };
+  using SourceMappingId = uint64_t;
+
+  struct SourceFrame {
+    SourceStringId name_id = 0;
+    SourceMappingId mapping_id = 0;
+    uint64_t rel_pc = 0;
+  };
+  using SourceFrameId = uint64_t;
+
+  using SourceCallstack = std::vector<SourceFrameId>;
+  using SourceCallstackId = uint64_t;
+  struct SourceAllocation {
+    uint64_t pid = 0;
+    // This is int64_t, because we get this from the TraceSorter which also
+    // converts this for us.
+    int64_t timestamp = 0;
+    StringId heap_name;
+    uint64_t callstack_id = 0;
+    uint64_t self_allocated = 0;
+    uint64_t self_freed = 0;
+    uint64_t alloc_count = 0;
+    uint64_t free_count = 0;
+  };
+
+  explicit ProfilePacketSequenceState(TraceProcessorContext* context);
+  virtual ~ProfilePacketSequenceState() override;
+
+  // Profile packets keep track of a index to detect packet loss. Call this
+  // method to update this index with the latest seen value.
+  void SetProfilePacketIndex(uint64_t index);
+
+  // In Android version Q we did not intern Mappings, Frames nor Callstacks,
+  // instead the profile packed "interned these". The following methods are used
+  // to support this old use case. They add the given object to a sequence local
+  // index for them to be retrieved later (see Find* Lookup* methods).
+  void AddString(SourceStringId id, base::StringView str);
+  void AddMapping(SourceMappingId id, const SourceMapping& mapping);
+  void AddFrame(SourceFrameId id, const SourceFrame& frame);
+  void AddCallstack(SourceCallstackId id, const SourceCallstack& callstack);
+
+  void StoreAllocation(const SourceAllocation& allocation);
+  void FinalizeProfile();
+  void CommitAllocations();
+
+  FrameId GetDatabaseFrameIdForTesting(SourceFrameId);
+
+ private:
+  struct SourceAllocationIndex {
+    UniquePid upid;
+    SourceCallstackId src_callstack_id;
+    StringPool::Id heap_name;
+    bool operator==(const SourceAllocationIndex& o) const {
+      return std::tie(upid, src_callstack_id, heap_name) ==
+             std::tie(o.upid, o.src_callstack_id, o.heap_name);
+    }
+    struct Hasher {
+      size_t operator()(const SourceAllocationIndex& o) const {
+        return static_cast<size_t>(base::Hasher::Combine(
+            o.upid, o.src_callstack_id, o.heap_name.raw_id()));
+      }
+    };
+  };
+
+  void AddAllocation(const SourceAllocation& alloc);
+
+  // The following methods deal with interned data. In Android version Q we did
+  // not intern Mappings, Frames nor Callstacks, instead the profile packed
+  // "interned these" and this class keeps those ina  sequence local index. In
+  // newer versions, these objects are in InternedData (see
+  // protos/perfetto/trace/interned_data) and are shared across multiple
+  // ProfilePackets. For backwards compatibility, the following methods first
+  // look up interned data in the private sequence local index (for values added
+  // via the Add* methods), and then, if this lookup fails, in the InternedData
+  // instead.
+  std::optional<MappingId> FindOrInsertMapping(uint64_t iid);
+  std::optional<CallsiteId> FindOrInsertCallstack(uint64_t iid);
+
+  TraceProcessorContext* const context_;
+
+  base::FlatHashMap<SourceStringId, std::string> strings_;
+  base::FlatHashMap<SourceMappingId, MappingId> mappings_;
+  base::FlatHashMap<SourceFrameId, FrameId> frames_;
+  base::FlatHashMap<SourceCallstackId, CallsiteId> callstacks_;
+
+  std::vector<SourceAllocation> pending_allocs_;
+
+  struct Hasher {
+    size_t operator()(const std::pair<UniquePid, CallsiteId>& p) const {
+      return static_cast<size_t>(
+          base::Hasher::Combine(p.first, p.second.value));
+    }
+  };
+  base::FlatHashMap<std::pair<UniquePid, CallsiteId>,
+                    tables::HeapProfileAllocationTable::Row,
+                    Hasher>
+      prev_alloc_;
+  base::FlatHashMap<std::pair<UniquePid, CallsiteId>,
+                    tables::HeapProfileAllocationTable::Row,
+                    Hasher>
+      prev_free_;
+
+  // For continuous dumps, we only store the delta in the data-base. To do
+  // this, we subtract the previous dump's value. Sometimes, we should not
+  // do that subtraction, because heapprofd garbage collects stacks that
+  // have no unfreed allocations. If the application then allocations again
+  // at that stack, it gets recreated and initialized to zero.
+  //
+  // To correct for this, we add the previous' stacks value to the current
+  // one, and then handle it as normal. If it is the first time we see a
+  // SourceCallstackId for a CallsiteId, we put the previous value into
+  // the correction maps below.
+  base::FlatHashMap<SourceAllocationIndex,
+                    base::FlatSet<CallsiteId>,
+                    SourceAllocationIndex::Hasher>
+      seen_callstacks_;
+  base::FlatHashMap<SourceCallstackId, tables::HeapProfileAllocationTable::Row>
+      alloc_correction_;
+  base::FlatHashMap<SourceCallstackId, tables::HeapProfileAllocationTable::Row>
+      free_correction_;
+
+  std::optional<uint64_t> prev_index;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_SEQUENCE_STATE_H_
diff --git a/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc b/src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc
similarity index 74%
rename from src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
rename to src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc
index 558ad91..ab947fa 100644
--- a/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
+#include "src/trace_processor/importers/proto/profile_packet_sequence_state.h"
 
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include <memory>
+
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 #include "test/gtest_and_gmock.h"
 
@@ -40,7 +43,6 @@
 constexpr auto kMappingStart = 234;
 constexpr auto kMappingEnd = 345;
 constexpr auto kMappingLoadBias = 456;
-constexpr auto kDefaultSequence = 1;
 
 // heapprofd on Android Q has large callstack ideas, explicitly test large
 // values.
@@ -56,10 +58,8 @@
  public:
   HeapProfileTrackerDupTest() {
     context.storage.reset(new TraceStorage());
-    context.global_stack_profile_tracker.reset(new GlobalStackProfileTracker());
-    sequence_stack_profile_tracker.reset(
-        new SequenceStackProfileTracker(&context));
-    context.heap_profile_tracker.reset(new HeapProfileTracker(&context));
+    context.stack_profile_tracker.reset(new StackProfileTracker(&context));
+    packet_sequence_state.reset(new PacketSequenceState(&context));
 
     mapping_name = context.storage->InternString("[mapping]");
     fully_qualified_mapping_name = context.storage->InternString("/[mapping]");
@@ -68,13 +68,17 @@
   }
 
  protected:
+  ProfilePacketSequenceState& profile_packet_sequence_state() {
+    return *packet_sequence_state->current_generation()
+                ->GetOrCreate<ProfilePacketSequenceState>();
+  }
   void InsertMapping(const Packet& packet) {
-    sequence_stack_profile_tracker->AddString(packet.mapping_name_id,
+    profile_packet_sequence_state().AddString(packet.mapping_name_id,
                                               "[mapping]");
 
-    sequence_stack_profile_tracker->AddString(packet.build_id, kBuildIDName);
+    profile_packet_sequence_state().AddString(packet.build_id, kBuildIDName);
 
-    SequenceStackProfileTracker::SourceMapping first_frame;
+    ProfilePacketSequenceState::SourceMapping first_frame;
     first_frame.build_id = packet.build_id;
     first_frame.exact_offset = kMappingExactOffset;
     first_frame.start_offset = kMappingStartOffset;
@@ -83,27 +87,27 @@
     first_frame.load_bias = kMappingLoadBias;
     first_frame.name_ids = {packet.mapping_name_id};
 
-    sequence_stack_profile_tracker->AddMapping(packet.mapping_id, first_frame);
+    profile_packet_sequence_state().AddMapping(packet.mapping_id, first_frame);
   }
 
   void InsertFrame(const Packet& packet) {
     InsertMapping(packet);
-    sequence_stack_profile_tracker->AddString(packet.frame_name_id, "[frame]");
+    profile_packet_sequence_state().AddString(packet.frame_name_id, "[frame]");
 
-    SequenceStackProfileTracker::SourceFrame first_frame;
+    ProfilePacketSequenceState::SourceFrame first_frame;
     first_frame.name_id = packet.frame_name_id;
     first_frame.mapping_id = packet.mapping_id;
     first_frame.rel_pc = kFrameRelPc;
 
-    sequence_stack_profile_tracker->AddFrame(packet.frame_id, first_frame);
+    profile_packet_sequence_state().AddFrame(packet.frame_id, first_frame);
   }
 
   void InsertCallsite(const Packet& packet) {
     InsertFrame(packet);
 
-    SequenceStackProfileTracker::SourceCallstack first_callsite = {
+    ProfilePacketSequenceState::SourceCallstack first_callsite = {
         packet.frame_id, packet.frame_id};
-    sequence_stack_profile_tracker->AddCallstack(kCallstackId, first_callsite);
+    profile_packet_sequence_state().AddCallstack(kCallstackId, first_callsite);
   }
 
   StringId mapping_name;
@@ -111,18 +115,16 @@
   StringId build;
   StringId frame_name;
   TraceProcessorContext context;
-  std::unique_ptr<SequenceStackProfileTracker> sequence_stack_profile_tracker;
+  std::unique_ptr<PacketSequenceState> packet_sequence_state;
 };
 
 // Insert the same mapping from two different packets, with different strings
 // interned, and assert we only store one.
 TEST_F(HeapProfileTrackerDupTest, Mapping) {
   InsertMapping(kFirstPacket);
-  context.heap_profile_tracker->FinalizeProfile(
-      kDefaultSequence, sequence_stack_profile_tracker.get(), nullptr);
+  profile_packet_sequence_state().FinalizeProfile();
   InsertMapping(kSecondPacket);
-  context.heap_profile_tracker->FinalizeProfile(
-      kDefaultSequence, sequence_stack_profile_tracker.get(), nullptr);
+  profile_packet_sequence_state().FinalizeProfile();
 
   EXPECT_THAT(context.storage->stack_profile_mapping_table().build_id()[0],
               context.storage->InternString({kBuildIDHexName}));
@@ -144,11 +146,9 @@
 // interned, and assert we only store one.
 TEST_F(HeapProfileTrackerDupTest, Frame) {
   InsertFrame(kFirstPacket);
-  context.heap_profile_tracker->FinalizeProfile(
-      kDefaultSequence, sequence_stack_profile_tracker.get(), nullptr);
+  profile_packet_sequence_state().FinalizeProfile();
   InsertFrame(kSecondPacket);
-  context.heap_profile_tracker->FinalizeProfile(
-      kDefaultSequence, sequence_stack_profile_tracker.get(), nullptr);
+  profile_packet_sequence_state().FinalizeProfile();
 
   const auto& frames = context.storage->stack_profile_frame_table();
   EXPECT_THAT(frames.name()[0], frame_name);
@@ -160,11 +160,9 @@
 // stored once.
 TEST_F(HeapProfileTrackerDupTest, Callstack) {
   InsertCallsite(kFirstPacket);
-  context.heap_profile_tracker->FinalizeProfile(
-      kDefaultSequence, sequence_stack_profile_tracker.get(), nullptr);
+  profile_packet_sequence_state().FinalizeProfile();
   InsertCallsite(kSecondPacket);
-  context.heap_profile_tracker->FinalizeProfile(
-      kDefaultSequence, sequence_stack_profile_tracker.get(), nullptr);
+  profile_packet_sequence_state().FinalizeProfile();
 
   const auto& callsite_table = context.storage->stack_profile_callsite_table();
   const auto& depth = callsite_table.depth();
@@ -198,22 +196,20 @@
 TEST(HeapProfileTrackerTest, SourceMappingPath) {
   TraceProcessorContext context;
   context.storage.reset(new TraceStorage());
-  context.global_stack_profile_tracker.reset(new GlobalStackProfileTracker());
-  context.heap_profile_tracker.reset(new HeapProfileTracker(&context));
-
-  HeapProfileTracker* hpt = context.heap_profile_tracker.get();
-  std::unique_ptr<SequenceStackProfileTracker> spt(
-      new SequenceStackProfileTracker(&context));
+  context.stack_profile_tracker.reset(new StackProfileTracker(&context));
+  PacketSequenceState pss(&context);
+  ProfilePacketSequenceState& ppss =
+      *pss.current_generation()->GetOrCreate<ProfilePacketSequenceState>();
 
   constexpr auto kBuildId = 1u;
   constexpr auto kMappingNameId1 = 2u;
   constexpr auto kMappingNameId2 = 3u;
 
-  spt->AddString(kBuildId, "buildid");
-  spt->AddString(kMappingNameId1, "foo");
-  spt->AddString(kMappingNameId2, "bar");
+  ppss.AddString(kBuildId, "buildid");
+  ppss.AddString(kMappingNameId1, "foo");
+  ppss.AddString(kMappingNameId2, "bar");
 
-  SequenceStackProfileTracker::SourceMapping mapping;
+  ProfilePacketSequenceState::SourceMapping mapping;
   mapping.build_id = kBuildId;
   mapping.exact_offset = 1;
   mapping.start_offset = 1;
@@ -221,8 +217,8 @@
   mapping.end = 3;
   mapping.load_bias = 0;
   mapping.name_ids = {kMappingNameId1, kMappingNameId2};
-  spt->AddMapping(0, mapping);
-  hpt->CommitAllocations(kDefaultSequence, spt.get(), nullptr);
+  ppss.AddMapping(0, mapping);
+  ppss.CommitAllocations();
   auto foo_bar_id = context.storage->string_pool().GetId("/foo/bar");
   ASSERT_NE(foo_bar_id, std::nullopt);
   EXPECT_THAT(context.storage->stack_profile_mapping_table().name()[0],
@@ -233,12 +229,11 @@
 TEST(HeapProfileTrackerTest, Functional) {
   TraceProcessorContext context;
   context.storage.reset(new TraceStorage());
-  context.global_stack_profile_tracker.reset(new GlobalStackProfileTracker());
-  context.heap_profile_tracker.reset(new HeapProfileTracker(&context));
+  context.stack_profile_tracker.reset(new StackProfileTracker(&context));
 
-  HeapProfileTracker* hpt = context.heap_profile_tracker.get();
-  std::unique_ptr<SequenceStackProfileTracker> spt(
-      new SequenceStackProfileTracker(&context));
+  PacketSequenceState pss(&context);
+  ProfilePacketSequenceState& ppss =
+      *pss.current_generation()->GetOrCreate<ProfilePacketSequenceState>();
 
   uint32_t next_string_intern_id = 1;
 
@@ -252,7 +247,7 @@
   for (size_t i = 0; i < base::ArraySize(mapping_names); ++i)
     mapping_name_ids[i] = next_string_intern_id++;
 
-  SequenceStackProfileTracker::SourceMapping
+  ProfilePacketSequenceState::SourceMapping
       mappings[base::ArraySize(mapping_names)] = {};
   mappings[0].build_id = build_id_ids[0];
   mappings[0].exact_offset = 1;
@@ -283,7 +278,7 @@
   for (size_t i = 0; i < base::ArraySize(function_names); ++i)
     function_name_ids[i] = next_string_intern_id++;
 
-  SequenceStackProfileTracker::SourceFrame
+  ProfilePacketSequenceState::SourceFrame
       frames[base::ArraySize(function_names)];
   frames[0].name_id = function_name_ids[0];
   frames[0].mapping_id = 0;
@@ -301,41 +296,41 @@
   frames[3].mapping_id = 2;
   frames[3].rel_pc = 123;
 
-  SequenceStackProfileTracker::SourceCallstack callstacks[3];
+  ProfilePacketSequenceState::SourceCallstack callstacks[3];
   callstacks[0] = {2, 1, 0};
   callstacks[1] = {2, 1, 0, 1, 0};
   callstacks[2] = {0, 2, 0, 1, 2};
 
   for (size_t i = 0; i < base::ArraySize(build_ids); ++i) {
     auto interned = base::StringView(build_ids[i].data(), build_ids[i].size());
-    spt->AddString(build_id_ids[i], interned);
+    ppss.AddString(build_id_ids[i], interned);
   }
   for (size_t i = 0; i < base::ArraySize(mapping_names); ++i) {
     auto interned =
         base::StringView(mapping_names[i].data(), mapping_names[i].size());
-    spt->AddString(mapping_name_ids[i], interned);
+    ppss.AddString(mapping_name_ids[i], interned);
   }
   for (size_t i = 0; i < base::ArraySize(function_names); ++i) {
     auto interned =
         base::StringView(function_names[i].data(), function_names[i].size());
-    spt->AddString(function_name_ids[i], interned);
+    ppss.AddString(function_name_ids[i], interned);
   }
 
   for (uint32_t i = 0; i < base::ArraySize(mappings); ++i)
-    spt->AddMapping(i, mappings[i]);
+    ppss.AddMapping(i, mappings[i]);
   for (uint32_t i = 0; i < base::ArraySize(frames); ++i)
-    spt->AddFrame(i, frames[i]);
+    ppss.AddFrame(i, frames[i]);
   for (uint32_t i = 0; i < base::ArraySize(callstacks); ++i)
-    spt->AddCallstack(i, callstacks[i]);
+    ppss.AddCallstack(i, callstacks[i]);
 
-  hpt->CommitAllocations(kDefaultSequence, spt.get(), nullptr);
+  ppss.CommitAllocations();
 
   for (size_t i = 0; i < base::ArraySize(callstacks); ++i) {
     std::optional<CallsiteId> parent;
-    const SequenceStackProfileTracker::SourceCallstack& callstack =
+    const ProfilePacketSequenceState::SourceCallstack& callstack =
         callstacks[i];
     for (size_t depth = 0; depth < callstack.size(); ++depth) {
-      auto frame_id = spt->GetDatabaseFrameIdForTesting(callstack[depth]);
+      auto frame_id = ppss.GetDatabaseFrameIdForTesting(callstack[depth]);
       std::optional<CallsiteId> self = FindCallstack(
           *context.storage, static_cast<int64_t>(depth), parent, frame_id);
       ASSERT_TRUE(self.has_value());
@@ -343,7 +338,7 @@
     }
   }
 
-  hpt->FinalizeProfile(kDefaultSequence, spt.get(), nullptr);
+  ppss.FinalizeProfile();
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/proto/profile_packet_utils.cc b/src/trace_processor/importers/proto/profile_packet_utils.cc
index 9478db9..3cc5dc1 100644
--- a/src/trace_processor/importers/proto/profile_packet_utils.cc
+++ b/src/trace_processor/importers/proto/profile_packet_utils.cc
@@ -15,11 +15,27 @@
  */
 
 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
+#include "perfetto/ext/base/string_utils.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-ProfilePacketInternLookup::~ProfilePacketInternLookup() = default;
+// static
+std::string ProfilePacketUtils::MakeMappingName(
+    const std::vector<base::StringView>& path_components) {
+  std::string name;
+  for (base::StringView p : path_components) {
+    name.push_back('/');
+    name.append(p.data(), p.size());
+  }
+
+  // When path strings just have single full path(like Chrome does), the mapping
+  // path gets an extra '/' prepended, strip the extra '/'.
+  if (base::StartsWith(name, "//")) {
+    name = name.substr(1);
+  }
+  return name;
+}
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/profile_packet_utils.h b/src/trace_processor/importers/proto/profile_packet_utils.h
index 64b2c50..a9ea0e6 100644
--- a/src/trace_processor/importers/proto/profile_packet_utils.h
+++ b/src/trace_processor/importers/proto/profile_packet_utils.h
@@ -20,7 +20,7 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/profile_packet_sequence_state.h"
 
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
@@ -31,9 +31,12 @@
 
 class ProfilePacketUtils {
  public:
-  static SequenceStackProfileTracker::SourceMapping MakeSourceMapping(
+  static std::string MakeMappingName(
+      const std::vector<base::StringView>& path_components);
+
+  static ProfilePacketSequenceState::SourceMapping MakeSourceMapping(
       const protos::pbzero::Mapping::Decoder& entry) {
-    SequenceStackProfileTracker::SourceMapping src_mapping{};
+    ProfilePacketSequenceState::SourceMapping src_mapping{};
     src_mapping.build_id = entry.build_id();
     src_mapping.exact_offset = entry.exact_offset();
     src_mapping.start_offset = entry.start_offset();
@@ -47,18 +50,18 @@
     return src_mapping;
   }
 
-  static SequenceStackProfileTracker::SourceFrame MakeSourceFrame(
+  static ProfilePacketSequenceState::SourceFrame MakeSourceFrame(
       const protos::pbzero::Frame::Decoder& entry) {
-    SequenceStackProfileTracker::SourceFrame src_frame;
+    ProfilePacketSequenceState::SourceFrame src_frame;
     src_frame.name_id = entry.function_name_id();
     src_frame.mapping_id = entry.mapping_id();
     src_frame.rel_pc = entry.rel_pc();
     return src_frame;
   }
 
-  static SequenceStackProfileTracker::SourceCallstack MakeSourceCallstack(
+  static ProfilePacketSequenceState::SourceCallstack MakeSourceCallstack(
       const protos::pbzero::Callstack::Decoder& entry) {
-    SequenceStackProfileTracker::SourceCallstack src_callstack;
+    ProfilePacketSequenceState::SourceCallstack src_callstack;
     for (auto frame_it = entry.frame_ids(); frame_it; ++frame_it)
       src_callstack.emplace_back(*frame_it);
     return src_callstack;
@@ -126,76 +129,6 @@
   }
 };
 
-class ProfilePacketInternLookup
-    : public SequenceStackProfileTracker::InternLookup {
- public:
-  explicit ProfilePacketInternLookup(PacketSequenceStateGeneration* seq_state)
-      : seq_state_(seq_state) {}
-  ~ProfilePacketInternLookup() override;
-
-  std::optional<base::StringView> GetString(
-      SequenceStackProfileTracker::SourceStringId iid,
-      SequenceStackProfileTracker::InternedStringType type) const override {
-    protos::pbzero::InternedString::Decoder* decoder = nullptr;
-    switch (type) {
-      case SequenceStackProfileTracker::InternedStringType::kBuildId:
-        decoder = seq_state_->LookupInternedMessage<
-            protos::pbzero::InternedData::kBuildIdsFieldNumber,
-            protos::pbzero::InternedString>(iid);
-        break;
-      case SequenceStackProfileTracker::InternedStringType::kFunctionName:
-        decoder = seq_state_->LookupInternedMessage<
-            protos::pbzero::InternedData::kFunctionNamesFieldNumber,
-            protos::pbzero::InternedString>(iid);
-        break;
-      case SequenceStackProfileTracker::InternedStringType::kMappingPath:
-        decoder = seq_state_->LookupInternedMessage<
-            protos::pbzero::InternedData::kMappingPathsFieldNumber,
-            protos::pbzero::InternedString>(iid);
-        break;
-    }
-    if (!decoder)
-      return std::nullopt;
-    return base::StringView(reinterpret_cast<const char*>(decoder->str().data),
-                            decoder->str().size);
-  }
-
-  std::optional<SequenceStackProfileTracker::SourceMapping> GetMapping(
-      SequenceStackProfileTracker::SourceMappingId iid) const override {
-    auto* decoder = seq_state_->LookupInternedMessage<
-        protos::pbzero::InternedData::kMappingsFieldNumber,
-        protos::pbzero::Mapping>(iid);
-    if (!decoder)
-      return std::nullopt;
-    return ProfilePacketUtils::MakeSourceMapping(*decoder);
-  }
-
-  std::optional<SequenceStackProfileTracker::SourceFrame> GetFrame(
-      SequenceStackProfileTracker::SourceFrameId iid) const override {
-    auto* decoder = seq_state_->LookupInternedMessage<
-        protos::pbzero::InternedData::kFramesFieldNumber,
-        protos::pbzero::Frame>(iid);
-    if (!decoder)
-      return std::nullopt;
-    return ProfilePacketUtils::MakeSourceFrame(*decoder);
-  }
-
-  std::optional<SequenceStackProfileTracker::SourceCallstack> GetCallstack(
-      SequenceStackProfileTracker::SourceCallstackId iid) const override {
-    auto* interned_message_view = seq_state_->GetInternedMessageView(
-        protos::pbzero::InternedData::kCallstacksFieldNumber, iid);
-    if (!interned_message_view)
-      return std::nullopt;
-    protos::pbzero::Callstack::Decoder decoder(
-        interned_message_view->message().data(),
-        interned_message_view->message().length());
-    return ProfilePacketUtils::MakeSourceCallstack(std::move(decoder));
-  }
-
- private:
-  PacketSequenceStateGeneration* seq_state_;
-};
-
 }  // namespace trace_processor
 }  // namespace perfetto
 
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index af96d34..148bf60 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -28,12 +28,12 @@
 #include "src/trace_processor/importers/common/metadata_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
 #include "src/trace_processor/importers/proto/additional_modules.h"
 #include "src/trace_processor/importers/proto/default_modules.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/metadata.h"
 #include "src/trace_processor/storage/trace_storage.h"
@@ -55,6 +55,7 @@
 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
 #include "protos/perfetto/trace/ftrace/task.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
@@ -251,8 +252,7 @@
     context_.track_tracker.reset(new TrackTracker(&context_));
     context_.global_args_tracker.reset(
         new GlobalArgsTracker(context_.storage.get()));
-    context_.global_stack_profile_tracker.reset(
-        new GlobalStackProfileTracker());
+    context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.args_translation_table.reset(new ArgsTranslationTable(storage_));
     context_.metadata_tracker.reset(
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.h b/src/trace_processor/importers/proto/proto_trace_reader.h
index 1c554a6..8afc40c 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.h
+++ b/src/trace_processor/importers/proto/proto_trace_reader.h
@@ -24,6 +24,7 @@
 #include "src/trace_processor/importers/common/chunked_trace_reader.h"
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace protozero {
 struct ConstBytes;
diff --git a/src/trace_processor/importers/proto/stack_profile_sequence_state.cc b/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
new file mode 100644
index 0000000..e503a09
--- /dev/null
+++ b/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
+
+#include <optional>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_view.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
+#include "src/trace_processor/importers/proto/profile_packet_utils.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+base::StringView ToStringView(protozero::ConstBytes bytes) {
+  return base::StringView(reinterpret_cast<const char*>(bytes.data),
+                          bytes.size);
+}
+}  // namespace
+
+StackProfileSequenceState::StackProfileSequenceState(
+    TraceProcessorContext* context)
+    : context_(context) {}
+
+StackProfileSequenceState::~StackProfileSequenceState() = default;
+
+std::optional<MappingId> StackProfileSequenceState::FindOrInsertMapping(
+    uint64_t iid) {
+  if (MappingId* id = cached_mappings_.Find(iid); id) {
+    return *id;
+  }
+  auto* decoder =
+      LookupInternedMessage<protos::pbzero::InternedData::kMappingsFieldNumber,
+                            protos::pbzero::Mapping>(iid);
+  if (!decoder) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
+    return std::nullopt;
+  }
+
+  std::optional<base::StringView> build_id =
+      LookupInternedBuildId(decoder->build_id());
+  if (!build_id) {
+    return std::nullopt;
+  }
+
+  std::vector<base::StringView> path_components;
+  for (auto it = decoder->path_string_ids(); it; ++it) {
+    std::optional<base::StringView> str = LookupInternedMappingPath(*it);
+    if (!str) {
+      // For backward compatibility reasons we do not return an error but
+      // instead stop adding path components.
+      break;
+    }
+    path_components.push_back(*str);
+  }
+  std::string path = ProfilePacketUtils::MakeMappingName(path_components);
+
+  StackProfileTracker::CreateMappingParams params;
+  params.build_id = *build_id;
+  params.exact_offset = decoder->exact_offset();
+  params.start_offset = decoder->start_offset();
+  params.start = decoder->start();
+  params.end = decoder->end();
+  params.load_bias = decoder->load_bias();
+  params.name = base::StringView(path);
+  MappingId mapping_id = context_->stack_profile_tracker->InternMapping(params);
+  cached_mappings_.Insert(iid, mapping_id);
+  return mapping_id;
+}
+
+std::optional<base::StringView>
+StackProfileSequenceState::LookupInternedBuildId(uint64_t iid) {
+  // This should really be an error (value not set) or at the very least return
+  // a null string, but for backward compatibility use an empty string instead.
+  if (iid == 0) {
+    return "";
+  }
+  auto* decoder =
+      LookupInternedMessage<protos::pbzero::InternedData::kBuildIdsFieldNumber,
+                            protos::pbzero::InternedString>(iid);
+  if (!decoder) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
+    return std::nullopt;
+  }
+
+  return ToStringView(decoder->str());
+}
+
+std::optional<base::StringView>
+StackProfileSequenceState::LookupInternedMappingPath(uint64_t iid) {
+  // This should really be an error (value not set) or at the very least return
+  // a null string, but for backward compatibility use an empty string instead.
+  if (iid == 0) {
+    return "";
+  }
+  auto* decoder = LookupInternedMessage<
+      protos::pbzero::InternedData::kMappingPathsFieldNumber,
+      protos::pbzero::InternedString>(iid);
+  if (!decoder) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
+    return std::nullopt;
+  }
+
+  return ToStringView(decoder->str());
+}
+
+std::optional<CallsiteId> StackProfileSequenceState::FindOrInsertCallstack(
+    uint64_t iid) {
+  if (CallsiteId* id = cached_callstacks_.Find(iid); id) {
+    return *id;
+  }
+  auto* decoder = LookupInternedMessage<
+      protos::pbzero::InternedData::kCallstacksFieldNumber,
+      protos::pbzero::Callstack>(iid);
+  if (!decoder) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_callstack_id);
+    return std::nullopt;
+  }
+
+  std::optional<CallsiteId> parent_callsite_id;
+  uint32_t depth = 0;
+  for (auto it = decoder->frame_ids(); it; ++it) {
+    std::optional<FrameId> frame_id = FindOrInsertFrame(*it);
+    if (!frame_id) {
+      return std::nullopt;
+    }
+    parent_callsite_id = context_->stack_profile_tracker->InternCallsite(
+        parent_callsite_id, *frame_id, depth);
+    ++depth;
+  }
+
+  if (!parent_callsite_id) {
+    context_->storage->IncrementStats(stats::stackprofile_empty_callstack);
+    return std::nullopt;
+  }
+
+  cached_callstacks_.Insert(iid, *parent_callsite_id);
+
+  return *parent_callsite_id;
+}
+
+std::optional<FrameId> StackProfileSequenceState::FindOrInsertFrame(
+    uint64_t iid) {
+  if (FrameId* id = cached_frames_.Find(iid); id) {
+    return *id;
+  }
+  auto* decoder =
+      LookupInternedMessage<protos::pbzero::InternedData::kFramesFieldNumber,
+                            protos::pbzero::Frame>(iid);
+  if (!decoder) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
+    return std::nullopt;
+  }
+
+  std::optional<MappingId> mapping_id =
+      FindOrInsertMapping(decoder->mapping_id());
+  if (!mapping_id) {
+    return std::nullopt;
+  }
+
+  base::StringView function_name;
+  if (decoder->function_name_id() != 0) {
+    std::optional<base::StringView> func =
+        LookupInternedFunctionName(decoder->function_name_id());
+    if (!func) {
+      return std::nullopt;
+    }
+    function_name = *func;
+  }
+
+  FrameId frame_id = context_->stack_profile_tracker->InternFrame(
+      *mapping_id, decoder->rel_pc(), function_name);
+
+  cached_frames_.Insert(iid, frame_id);
+
+  return frame_id;
+}
+
+std::optional<base::StringView>
+StackProfileSequenceState::LookupInternedFunctionName(uint64_t iid) {
+  // This should really be an error (value not set) or at the very least return
+  // a null string, but for backward compatibility use an empty string instead.
+  if (iid == 0) {
+    return "";
+  }
+  auto* decoder = LookupInternedMessage<
+      protos::pbzero::InternedData::kFunctionNamesFieldNumber,
+      protos::pbzero::InternedString>(iid);
+  if (!decoder) {
+    context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
+    return std::nullopt;
+  }
+
+  return ToStringView(decoder->str());
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/stack_profile_sequence_state.h b/src/trace_processor/importers/proto/stack_profile_sequence_state.h
new file mode 100644
index 0000000..7a7879a
--- /dev/null
+++ b/src/trace_processor/importers/proto/stack_profile_sequence_state.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_SEQUENCE_STATE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_SEQUENCE_STATE_H_
+
+#include <cstdint>
+#include <optional>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.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 StackProfileSequenceState final
+    : public PacketSequenceStateGeneration::InternedDataTracker {
+ public:
+  explicit StackProfileSequenceState(TraceProcessorContext* context);
+
+  StackProfileSequenceState(const StackProfileSequenceState&);
+
+  virtual ~StackProfileSequenceState() override;
+
+  std::optional<MappingId> FindOrInsertMapping(uint64_t iid);
+  std::optional<CallsiteId> FindOrInsertCallstack(uint64_t iid);
+
+ private:
+  std::optional<base::StringView> LookupInternedBuildId(uint64_t iid);
+  std::optional<base::StringView> LookupInternedMappingPath(uint64_t iid);
+  std::optional<base::StringView> LookupInternedFunctionName(uint64_t iid);
+  std::optional<FrameId> FindOrInsertFrame(uint64_t iid);
+
+  TraceProcessorContext* const context_;
+  base::FlatHashMap<uint64_t, MappingId> cached_mappings_;
+  base::FlatHashMap<uint64_t, CallsiteId> cached_callstacks_;
+  base::FlatHashMap<uint64_t, FrameId> cached_frames_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_SEQUENCE_STATE_H_
diff --git a/src/trace_processor/importers/proto/stack_profile_tracker.cc b/src/trace_processor/importers/proto/stack_profile_tracker.cc
deleted file mode 100644
index d00f287..0000000
--- a/src/trace_processor/importers/proto/stack_profile_tracker.cc
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
-
-#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/importers/proto/profiler_util.h"
-#include "src/trace_processor/types/trace_processor_context.h"
-#include "src/trace_processor/util/stack_traces_util.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-SequenceStackProfileTracker::InternLookup::~InternLookup() = default;
-
-SequenceStackProfileTracker::SequenceStackProfileTracker(
-    TraceProcessorContext* context)
-    : context_(context), empty_(kNullStringId) {}
-
-SequenceStackProfileTracker::~SequenceStackProfileTracker() = default;
-
-StringId SequenceStackProfileTracker::GetEmptyStringId() {
-  if (empty_ == kNullStringId) {
-    empty_ = context_->storage->InternString({"", 0});
-  }
-
-  return empty_;
-}
-
-void SequenceStackProfileTracker::AddString(SourceStringId id,
-                                            base::StringView str) {
-  string_map_.emplace(id, str.ToStdString());
-}
-
-std::optional<MappingId> SequenceStackProfileTracker::AddMapping(
-    SourceMappingId id,
-    const SourceMapping& mapping,
-    const InternLookup* intern_lookup) {
-  std::string path;
-  for (SourceStringId str_id : mapping.name_ids) {
-    auto opt_str = FindOrInsertString(str_id, intern_lookup,
-                                      InternedStringType::kMappingPath);
-    if (!opt_str)
-      break;
-    path += "/" + *opt_str;
-  }
-  // When path strings just have single full path(like Chrome does), the mapping
-  // path gets an extra '/' prepended, strip the extra '/'.
-  if (base::StartsWith(path, "//")) {
-    path = path.substr(1);
-  }
-
-  auto opt_build_id = FindAndInternString(mapping.build_id, intern_lookup,
-                                          InternedStringType::kBuildId);
-  if (!opt_build_id) {
-    context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
-    PERFETTO_DLOG("Invalid string.");
-    return std::nullopt;
-  }
-  const StringId raw_build_id = opt_build_id.value();
-  NullTermStringView raw_build_id_str =
-      context_->storage->GetString(raw_build_id);
-  StringId build_id = GetEmptyStringId();
-  if (!raw_build_id_str.empty()) {
-    // If the build_id is 33 characters long, we assume it's a Breakpad debug
-    // identifier which is already in Hex and doesn't need conversion.
-    // TODO(b/148109467): Remove workaround once all active Chrome versions
-    // write raw bytes instead of a string as build_id.
-    if (util::IsHexModuleId(raw_build_id_str)) {
-      build_id = raw_build_id;
-    } else {
-      std::string hex_build_id =
-          base::ToHex(raw_build_id_str.c_str(), raw_build_id_str.size());
-      build_id =
-          context_->storage->InternString(base::StringView(hex_build_id));
-    }
-  }
-
-  tables::StackProfileMappingTable::Row row{
-      build_id,
-      static_cast<int64_t>(mapping.exact_offset),
-      static_cast<int64_t>(mapping.start_offset),
-      static_cast<int64_t>(mapping.start),
-      static_cast<int64_t>(mapping.end),
-      static_cast<int64_t>(mapping.load_bias),
-      context_->storage->InternString(base::StringView(path))};
-
-  tables::StackProfileMappingTable* mappings =
-      context_->storage->mutable_stack_profile_mapping_table();
-  std::optional<MappingId> cur_id;
-  auto it = mapping_idx_.find(row);
-  if (it != mapping_idx_.end()) {
-    cur_id = it->second;
-  } else {
-    std::vector<MappingId> db_mappings =
-        context_->global_stack_profile_tracker->FindMappingRow(row.name,
-                                                               row.build_id);
-    for (const MappingId preexisting_mapping : db_mappings) {
-      uint32_t preexisting_row = *mappings->id().IndexOf(preexisting_mapping);
-      tables::StackProfileMappingTable::Row preexisting_data{
-          mappings->build_id()[preexisting_row],
-          mappings->exact_offset()[preexisting_row],
-          mappings->start_offset()[preexisting_row],
-          mappings->start()[preexisting_row],
-          mappings->end()[preexisting_row],
-          mappings->load_bias()[preexisting_row],
-          mappings->name()[preexisting_row]};
-
-      if (row == preexisting_data) {
-        cur_id = preexisting_mapping;
-      }
-    }
-    if (!cur_id) {
-      MappingId mapping_id = mappings->Insert(row).id;
-      context_->global_stack_profile_tracker->InsertMappingId(
-          row.name, row.build_id, mapping_id);
-      cur_id = mapping_id;
-    }
-    mapping_idx_.emplace(row, *cur_id);
-  }
-  mapping_ids_.emplace(id, *cur_id);
-  return cur_id;
-}
-
-std::optional<FrameId> SequenceStackProfileTracker::AddFrame(
-    SourceFrameId id,
-    const SourceFrame& frame,
-    const InternLookup* intern_lookup) {
-  std::optional<std::string> opt_name = FindOrInsertString(
-      frame.name_id, intern_lookup, InternedStringType::kFunctionName);
-  if (!opt_name) {
-    context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
-    PERFETTO_DLOG("Invalid string.");
-    return std::nullopt;
-  }
-  const std::string& name = *opt_name;
-  const StringId str_id =
-      context_->storage->InternString(base::StringView(name));
-
-  auto opt_mapping = FindOrInsertMapping(frame.mapping_id, intern_lookup);
-  if (!opt_mapping) {
-    context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
-    return std::nullopt;
-  }
-  MappingId mapping_id = *opt_mapping;
-  const auto& mappings = context_->storage->stack_profile_mapping_table();
-  StringId mapping_name_id =
-      mappings.name()[*mappings.id().IndexOf(mapping_id)];
-  auto mapping_name = context_->storage->GetString(mapping_name_id);
-
-  tables::StackProfileFrameTable::Row row{str_id, mapping_id,
-                                          static_cast<int64_t>(frame.rel_pc)};
-
-  auto* frames = context_->storage->mutable_stack_profile_frame_table();
-
-  std::optional<FrameId> cur_id;
-  auto it = frame_idx_.find(row);
-  if (it != frame_idx_.end()) {
-    cur_id = it->second;
-  } else {
-    std::vector<FrameId> db_frames =
-        context_->global_stack_profile_tracker->FindFrameIds(mapping_id,
-                                                             frame.rel_pc);
-    for (const FrameId preexisting_frame : db_frames) {
-      uint32_t preexisting_row_id = *frames->id().IndexOf(preexisting_frame);
-      tables::StackProfileFrameTable::Row preexisting_row{
-          frames->name()[preexisting_row_id],
-          frames->mapping()[preexisting_row_id],
-          frames->rel_pc()[preexisting_row_id]};
-
-      if (row == preexisting_row) {
-        cur_id = preexisting_frame;
-      }
-    }
-    if (!cur_id) {
-      cur_id = frames->Insert(row).id;
-      context_->global_stack_profile_tracker->InsertFrameRow(
-          mapping_id, static_cast<uint64_t>(row.rel_pc), *cur_id);
-      if (base::Contains(name, '.')) {
-        // Java frames always contain a '.'
-        std::optional<std::string> package =
-            PackageFromLocation(context_->storage.get(), mapping_name);
-        if (package) {
-          NameInPackage nip{str_id, context_->storage->InternString(
-                                        base::StringView(*package))};
-          context_->global_stack_profile_tracker->InsertJavaFrameForName(
-              nip, *cur_id);
-        } else if (mapping_name.find("/memfd:") == 0) {
-          NameInPackage nip{str_id, context_->storage->InternString("memfd")};
-          context_->global_stack_profile_tracker->InsertJavaFrameForName(
-              nip, *cur_id);
-        }
-      }
-    }
-    frame_idx_.emplace(row, *cur_id);
-  }
-  frame_ids_.emplace(id, *cur_id);
-  return cur_id;
-}
-
-std::optional<CallsiteId> SequenceStackProfileTracker::AddCallstack(
-    SourceCallstackId id,
-    const SourceCallstack& frame_ids,
-    const InternLookup* intern_lookup) {
-  if (frame_ids.empty())
-    return std::nullopt;
-
-  std::optional<CallsiteId> parent_id;
-  for (uint32_t depth = 0; depth < frame_ids.size(); ++depth) {
-    auto opt_frame_id = FindOrInsertFrame(frame_ids[depth], intern_lookup);
-    if (!opt_frame_id) {
-      context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
-      return std::nullopt;
-    }
-    FrameId frame_id = *opt_frame_id;
-
-    tables::StackProfileCallsiteTable::Row row{depth, parent_id, frame_id};
-    CallsiteId self_id;
-    auto callsite_it = callsite_idx_.find(row);
-    if (callsite_it != callsite_idx_.end()) {
-      self_id = callsite_it->second;
-    } else {
-      auto* callsite =
-          context_->storage->mutable_stack_profile_callsite_table();
-      self_id = callsite->Insert(row).id;
-      callsite_idx_.emplace(row, self_id);
-    }
-    parent_id = self_id;
-  }
-  PERFETTO_DCHECK(parent_id);  // The loop ran at least once.
-  callstack_ids_.emplace(id, *parent_id);
-  return parent_id;
-}
-
-FrameId SequenceStackProfileTracker::GetDatabaseFrameIdForTesting(
-    SourceFrameId frame_id) {
-  auto it = frame_ids_.find(frame_id);
-  if (it == frame_ids_.end()) {
-    PERFETTO_DLOG("Invalid frame.");
-    return {};
-  }
-  return it->second;
-}
-
-std::optional<StringId> SequenceStackProfileTracker::FindAndInternString(
-    SourceStringId id,
-    const InternLookup* intern_lookup,
-    SequenceStackProfileTracker::InternedStringType type) {
-  if (id == 0)
-    return GetEmptyStringId();
-
-  auto opt_str = FindOrInsertString(id, intern_lookup, type);
-  if (!opt_str)
-    return GetEmptyStringId();
-
-  return context_->storage->InternString(base::StringView(*opt_str));
-}
-
-std::optional<std::string> SequenceStackProfileTracker::FindOrInsertString(
-    SourceStringId id,
-    const InternLookup* intern_lookup,
-    SequenceStackProfileTracker::InternedStringType type) {
-  if (id == 0)
-    return "";
-
-  auto it = string_map_.find(id);
-  if (it == string_map_.end()) {
-    if (intern_lookup) {
-      auto str = intern_lookup->GetString(id, type);
-      if (!str) {
-        context_->storage->IncrementStats(
-            stats::stackprofile_invalid_string_id);
-        PERFETTO_DLOG("Invalid string.");
-        return std::nullopt;
-      }
-      return str->ToStdString();
-    }
-    return std::nullopt;
-  }
-
-  return it->second;
-}
-
-std::optional<MappingId> SequenceStackProfileTracker::FindOrInsertMapping(
-    SourceMappingId mapping_id,
-    const InternLookup* intern_lookup) {
-  std::optional<MappingId> res;
-  auto it = mapping_ids_.find(mapping_id);
-  if (it == mapping_ids_.end()) {
-    if (intern_lookup) {
-      auto interned_mapping = intern_lookup->GetMapping(mapping_id);
-      if (interned_mapping) {
-        res = AddMapping(mapping_id, *interned_mapping, intern_lookup);
-        return res;
-      }
-    }
-    context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
-    return res;
-  }
-  res = it->second;
-  return res;
-}
-
-std::optional<FrameId> SequenceStackProfileTracker::FindOrInsertFrame(
-    SourceFrameId frame_id,
-    const InternLookup* intern_lookup) {
-  std::optional<FrameId> res;
-  auto it = frame_ids_.find(frame_id);
-  if (it == frame_ids_.end()) {
-    if (intern_lookup) {
-      auto interned_frame = intern_lookup->GetFrame(frame_id);
-      if (interned_frame) {
-        res = AddFrame(frame_id, *interned_frame, intern_lookup);
-        return res;
-      }
-    }
-    context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
-    PERFETTO_DLOG("Unknown frame %" PRIu64 " : %zu", frame_id,
-                  frame_ids_.size());
-    return res;
-  }
-  res = it->second;
-  return res;
-}
-
-std::optional<CallsiteId> SequenceStackProfileTracker::FindOrInsertCallstack(
-    SourceCallstackId callstack_id,
-    const InternLookup* intern_lookup) {
-  std::optional<CallsiteId> res;
-  auto it = callstack_ids_.find(callstack_id);
-  if (it == callstack_ids_.end()) {
-    auto interned_callstack = intern_lookup->GetCallstack(callstack_id);
-    if (interned_callstack) {
-      res = AddCallstack(callstack_id, *interned_callstack, intern_lookup);
-      return res;
-    }
-    context_->storage->IncrementStats(stats::stackprofile_invalid_callstack_id);
-    PERFETTO_DLOG("Unknown callstack %" PRIu64 " : %zu", callstack_id,
-                  callstack_ids_.size());
-    return res;
-  }
-  res = it->second;
-  return res;
-}
-
-void SequenceStackProfileTracker::ClearIndices() {
-  string_map_.clear();
-  mapping_ids_.clear();
-  callstack_ids_.clear();
-  frame_ids_.clear();
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/stack_profile_tracker.h b/src/trace_processor/importers/proto/stack_profile_tracker.h
deleted file mode 100644
index a29d3c1..0000000
--- a/src/trace_processor/importers/proto/stack_profile_tracker.h
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_
-
-#include <deque>
-#include <optional>
-#include <unordered_map>
-
-#include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/tables/profiler_tables_py.h"
-
-#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
-#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
-
-template <>
-struct std::hash<std::pair<uint32_t, int64_t>> {
-  using argument_type = std::pair<uint32_t, int64_t>;
-  using result_type = size_t;
-
-  result_type operator()(const argument_type& p) const {
-    return std::hash<uint32_t>{}(p.first) ^ std::hash<int64_t>{}(p.second);
-  }
-};
-
-template <>
-struct std::hash<std::pair<uint32_t, perfetto::trace_processor::CallsiteId>> {
-  using argument_type =
-      std::pair<uint32_t, perfetto::trace_processor::CallsiteId>;
-  using result_type = size_t;
-
-  result_type operator()(const argument_type& p) const {
-    return std::hash<uint32_t>{}(p.first) ^
-           std::hash<uint32_t>{}(p.second.value);
-  }
-};
-
-template <>
-struct std::hash<std::pair<uint32_t, perfetto::trace_processor::MappingId>> {
-  using argument_type =
-      std::pair<uint32_t, perfetto::trace_processor::MappingId>;
-  using result_type = size_t;
-
-  result_type operator()(const argument_type& p) const {
-    return std::hash<uint32_t>{}(p.first) ^
-           std::hash<uint32_t>{}(p.second.value);
-  }
-};
-
-template <>
-struct std::hash<std::pair<uint32_t, perfetto::trace_processor::FrameId>> {
-  using argument_type = std::pair<uint32_t, perfetto::trace_processor::FrameId>;
-  using result_type = size_t;
-
-  result_type operator()(const argument_type& p) const {
-    return std::hash<uint32_t>{}(p.first) ^
-           std::hash<uint32_t>{}(p.second.value);
-  }
-};
-
-template <>
-struct std::hash<std::vector<uint64_t>> {
-  using argument_type = std::vector<uint64_t>;
-  using result_type = size_t;
-
-  result_type operator()(const argument_type& p) const {
-    size_t h = 0u;
-    for (auto v : p)
-      h = h ^ std::hash<uint64_t>{}(v);
-    return h;
-  }
-};
-
-namespace perfetto {
-namespace trace_processor {
-
-struct NameInPackage {
-  StringId name;
-  StringId package;
-
-  bool operator<(const NameInPackage& b) const {
-    return std::tie(name, package) < std::tie(b.name, b.package);
-  }
-};
-
-class TraceProcessorContext;
-
-class GlobalStackProfileTracker {
- public:
-  std::vector<MappingId> FindMappingRow(StringId name,
-                                        StringId build_id) const {
-    auto it = stack_profile_mapping_index_.find(std::make_pair(name, build_id));
-    if (it == stack_profile_mapping_index_.end())
-      return {};
-    return it->second;
-  }
-
-  void InsertMappingId(StringId name, StringId build_id, MappingId row) {
-    auto pair = std::make_pair(name, build_id);
-    stack_profile_mapping_index_[pair].emplace_back(row);
-  }
-
-  std::vector<FrameId> FindFrameIds(MappingId mapping_row,
-                                    uint64_t rel_pc) const {
-    auto it =
-        stack_profile_frame_index_.find(std::make_pair(mapping_row, rel_pc));
-    if (it == stack_profile_frame_index_.end())
-      return {};
-    return it->second;
-  }
-
-  void InsertFrameRow(MappingId mapping_row, uint64_t rel_pc, FrameId row) {
-    auto pair = std::make_pair(mapping_row, rel_pc);
-    stack_profile_frame_index_[pair].emplace_back(row);
-  }
-
-  const std::vector<tables::StackProfileFrameTable::Id>* JavaFramesForName(
-      NameInPackage name) {
-    auto it = java_frames_for_name_.find(name);
-    if (it == java_frames_for_name_.end())
-      return nullptr;
-    return &it->second;
-  }
-
-  void InsertJavaFrameForName(NameInPackage name,
-                              tables::StackProfileFrameTable::Id id) {
-    java_frames_for_name_[name].push_back(id);
-  }
-
- private:
-  using MappingKey = std::pair<StringId /* name */, StringId /* build id */>;
-  std::map<MappingKey, std::vector<MappingId>> stack_profile_mapping_index_;
-
-  using FrameKey = std::pair<MappingId, uint64_t /* rel_pc */>;
-  std::map<FrameKey, std::vector<FrameId>> stack_profile_frame_index_;
-
-  std::map<NameInPackage, std::vector<tables::StackProfileFrameTable::Id>>
-      java_frames_for_name_;
-};
-
-// TODO(lalitm): Overhaul this class to make row vs id consistent and use
-// base::Optional instead of int64_t.
-class SequenceStackProfileTracker {
- public:
-  using SourceStringId = uint64_t;
-
-  enum class InternedStringType {
-    kMappingPath,
-    kBuildId,
-    kFunctionName,
-  };
-
-  struct SourceMapping {
-    SourceStringId build_id = 0;
-    uint64_t exact_offset = 0;
-    uint64_t start_offset = 0;
-    uint64_t start = 0;
-    uint64_t end = 0;
-    uint64_t load_bias = 0;
-    std::vector<SourceStringId> name_ids;
-  };
-  using SourceMappingId = uint64_t;
-
-  struct SourceFrame {
-    SourceStringId name_id = 0;
-    SourceMappingId mapping_id = 0;
-    uint64_t rel_pc = 0;
-  };
-  using SourceFrameId = uint64_t;
-
-  using SourceCallstack = std::vector<SourceFrameId>;
-  using SourceCallstackId = uint64_t;
-
-  struct SourceAllocation {
-    uint64_t pid = 0;
-    // This is int64_t, because we get this from the TraceSorter which also
-    // converts this for us.
-    int64_t timestamp = 0;
-    SourceCallstackId callstack_id = 0;
-    uint64_t self_allocated = 0;
-    uint64_t self_freed = 0;
-    uint64_t alloc_count = 0;
-    uint64_t free_count = 0;
-  };
-
-  class InternLookup {
-   public:
-    virtual ~InternLookup();
-
-    virtual std::optional<base::StringView> GetString(
-        SourceStringId,
-        InternedStringType) const = 0;
-    virtual std::optional<SourceMapping> GetMapping(SourceMappingId) const = 0;
-    virtual std::optional<SourceFrame> GetFrame(SourceFrameId) const = 0;
-    virtual std::optional<SourceCallstack> GetCallstack(
-        SourceCallstackId) const = 0;
-  };
-
-  explicit SequenceStackProfileTracker(TraceProcessorContext* context);
-  ~SequenceStackProfileTracker();
-
-  void AddString(SourceStringId, base::StringView);
-  std::optional<MappingId> AddMapping(
-      SourceMappingId,
-      const SourceMapping&,
-      const InternLookup* intern_lookup = nullptr);
-  std::optional<FrameId> AddFrame(SourceFrameId,
-                                  const SourceFrame&,
-                                  const InternLookup* intern_lookup = nullptr);
-  std::optional<CallsiteId> AddCallstack(
-      SourceCallstackId,
-      const SourceCallstack&,
-      const InternLookup* intern_lookup = nullptr);
-
-  FrameId GetDatabaseFrameIdForTesting(SourceFrameId);
-
-  // Gets the row number of string / mapping / frame / callstack previously
-  // added through AddString / AddMapping/ AddFrame / AddCallstack.
-  //
-  // If it is not found, look up the string / mapping / frame / callstack in
-  // the global InternedData state, and if found, add to the database, if not
-  // already added before.
-  //
-  // This is to support both ProfilePackets that contain the interned data
-  // (for Android Q) and where the interned data is kept globally in
-  // InternedData (for versions newer than Q).
-  std::optional<StringId> FindAndInternString(SourceStringId,
-                                              const InternLookup* intern_lookup,
-                                              InternedStringType type);
-  std::optional<std::string> FindOrInsertString(
-      SourceStringId,
-      const InternLookup* intern_lookup,
-      InternedStringType type);
-  std::optional<MappingId> FindOrInsertMapping(
-      SourceMappingId,
-      const InternLookup* intern_lookup);
-  std::optional<FrameId> FindOrInsertFrame(SourceFrameId,
-                                           const InternLookup* intern_lookup);
-
-  std::optional<CallsiteId> FindOrInsertCallstack(
-      SourceCallstackId,
-      const InternLookup* intern_lookup);
-
-  // Clear indices when they're no longer needed.
-  void ClearIndices();
-
- private:
-  StringId GetEmptyStringId();
-
-  std::unordered_map<SourceStringId, std::string> string_map_;
-
-  // Mapping from ID of mapping / frame / callstack in original trace and the
-  // index in the respective table it was inserted into.
-  std::unordered_map<SourceMappingId, MappingId> mapping_ids_;
-  std::unordered_map<SourceFrameId, FrameId> frame_ids_;
-  std::unordered_map<SourceCallstackId, CallsiteId> callstack_ids_;
-
-  // TODO(oysteine): Share these indices between the StackProfileTrackers,
-  // since they're not sequence-specific.
-  //
-  // Mapping from content of database row to the index of the raw.
-  std::unordered_map<tables::StackProfileMappingTable::Row, MappingId>
-      mapping_idx_;
-  std::unordered_map<tables::StackProfileFrameTable::Row, FrameId> frame_idx_;
-  std::unordered_map<tables::StackProfileCallsiteTable::Row, CallsiteId>
-      callsite_idx_;
-
-  TraceProcessorContext* const context_;
-  StringId empty_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 06e5c39..00bf52e 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -34,6 +34,7 @@
 #include "src/trace_processor/importers/proto/packet_analyzer.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
+#include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
 #include "src/trace_processor/importers/proto/track_event_tracker.h"
 #include "src/trace_processor/util/debug_annotation_parser.h"
 #include "src/trace_processor/util/proto_to_args_parser.h"
@@ -195,12 +196,9 @@
   }
   // Interned mapping_id loses it's meaning when the sequence ends. So we need
   // to get an id from stack_profile_mapping table.
-  ProfilePacketInternLookup intern_lookup(delegate.seq_state());
-  auto mapping_id =
-      delegate.seq_state()
-          ->state()
-          ->sequence_stack_profile_tracker()
-          .FindOrInsertMapping(decoder->mapping_id(), &intern_lookup);
+  auto mapping_id = delegate.seq_state()
+                        ->GetOrCreate<StackProfileSequenceState>()
+                        ->FindOrInsertMapping(decoder->mapping_id());
   if (!mapping_id) {
     return std::nullopt;
   }
diff --git a/src/trace_processor/importers/proto/v8_module.cc b/src/trace_processor/importers/proto/v8_module.cc
index ac2086a..94684e3 100644
--- a/src/trace_processor/importers/proto/v8_module.cc
+++ b/src/trace_processor/importers/proto/v8_module.cc
@@ -88,8 +88,7 @@
 void V8Module::ParseV8JsCode(protozero::ConstBytes bytes,
                              int64_t ts,
                              const TracePacketData& data) {
-  V8SequenceState& state =
-      *V8SequenceState::GetOrCreate(data.sequence_state->state());
+  V8SequenceState& state = *data.sequence_state->GetOrCreate<V8SequenceState>();
 
   V8JsCode::Decoder code(bytes);
 
@@ -110,8 +109,7 @@
 void V8Module::ParseV8InternalCode(protozero::ConstBytes bytes,
                                    int64_t ts,
                                    const TracePacketData& data) {
-  V8SequenceState& state =
-      *V8SequenceState::GetOrCreate(data.sequence_state->state());
+  V8SequenceState& state = *data.sequence_state->GetOrCreate<V8SequenceState>();
 
   V8InternalCode::Decoder code(bytes);
 
@@ -126,8 +124,7 @@
 void V8Module::ParseV8WasmCode(protozero::ConstBytes bytes,
                                int64_t ts,
                                const TracePacketData& data) {
-  V8SequenceState& state =
-      *V8SequenceState::GetOrCreate(data.sequence_state->state());
+  V8SequenceState& state = *data.sequence_state->GetOrCreate<V8SequenceState>();
 
   V8WasmCode::Decoder code(bytes);
 
@@ -148,8 +145,7 @@
 void V8Module::ParseV8RegExpCode(protozero::ConstBytes bytes,
                                  int64_t ts,
                                  const TracePacketData& data) {
-  V8SequenceState& state =
-      *V8SequenceState::GetOrCreate(data.sequence_state->state());
+  V8SequenceState& state = *data.sequence_state->GetOrCreate<V8SequenceState>();
 
   V8RegExpCode::Decoder code(bytes);
 
@@ -164,8 +160,7 @@
 void V8Module::ParseV8CodeMove(protozero::ConstBytes bytes,
                                int64_t,
                                const TracePacketData& data) {
-  V8SequenceState& state =
-      *V8SequenceState::GetOrCreate(data.sequence_state->state());
+  V8SequenceState& state = *data.sequence_state->GetOrCreate<V8SequenceState>();
   protos::pbzero::V8CodeMove::Decoder v8_code_move(bytes);
 
   std::optional<tables::V8IsolateTable::Id> isolate_id =
diff --git a/src/trace_processor/importers/proto/v8_sequence_state.cc b/src/trace_processor/importers/proto/v8_sequence_state.cc
index 946ab22..33abb24 100644
--- a/src/trace_processor/importers/proto/v8_sequence_state.cc
+++ b/src/trace_processor/importers/proto/v8_sequence_state.cc
@@ -17,10 +17,10 @@
 #include "src/trace_processor/importers/proto/v8_sequence_state.h"
 #include <optional>
 
-#include "perfetto/ext/base/string_utils.h"
 #include "protos/perfetto/trace/chrome/v8.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
 #include "src/trace_processor/importers/proto/string_encoding_utils.h"
 #include "src/trace_processor/importers/proto/v8_tracker.h"
 #include "src/trace_processor/storage/stats.h"
@@ -41,9 +41,8 @@
 
 }  // namespace
 
-V8SequenceState::V8SequenceState(PacketSequenceState* sequence_state)
-    : sequence_state_(sequence_state),
-      v8_tracker_(V8Tracker::GetOrCreate(sequence_state->context())) {}
+V8SequenceState::V8SequenceState(TraceProcessorContext* context)
+    : context_(context), v8_tracker_(V8Tracker::GetOrCreate(context_)) {}
 
 V8SequenceState::~V8SequenceState() = default;
 
@@ -53,11 +52,9 @@
     return *id;
   }
 
-  auto* view = sequence_state_->current_generation()->GetInternedMessageView(
-      InternedData::kV8IsolateFieldNumber, iid);
+  auto* view = GetInternedMessageView(InternedData::kV8IsolateFieldNumber, iid);
   if (!view) {
-    sequence_state_->context()->storage->IncrementStats(
-        stats::v8_intern_errors);
+    context_->storage->IncrementStats(stats::v8_intern_errors);
     return std::nullopt;
   }
 
@@ -73,11 +70,10 @@
     return *id;
   }
 
-  auto* view = sequence_state_->current_generation()->GetInternedMessageView(
-      InternedData::kV8JsFunctionFieldNumber, iid);
+  auto* view =
+      GetInternedMessageView(InternedData::kV8JsFunctionFieldNumber, iid);
   if (!view) {
-    sequence_state_->context()->storage->IncrementStats(
-        stats::v8_intern_errors);
+    context_->storage->IncrementStats(stats::v8_intern_errors);
     return std::nullopt;
   }
 
@@ -107,11 +103,10 @@
   if (auto* id = wasm_scripts_.Find(iid); id != nullptr) {
     return *id;
   }
-  auto* view = sequence_state_->current_generation()->GetInternedMessageView(
-      InternedData::kV8WasmScriptFieldNumber, iid);
+  auto* view =
+      GetInternedMessageView(InternedData::kV8WasmScriptFieldNumber, iid);
   if (!view) {
-    sequence_state_->context()->storage->IncrementStats(
-        stats::v8_intern_errors);
+    context_->storage->IncrementStats(stats::v8_intern_errors);
     return std::nullopt;
   }
 
@@ -127,11 +122,10 @@
   if (auto* id = js_scripts_.Find(iid); id != nullptr) {
     return *id;
   }
-  auto* view = sequence_state_->current_generation()->GetInternedMessageView(
-      InternedData::kV8JsScriptFieldNumber, iid);
+  auto* view =
+      GetInternedMessageView(InternedData::kV8JsScriptFieldNumber, iid);
   if (!view) {
-    sequence_state_->context()->storage->IncrementStats(
-        stats::v8_intern_errors);
+    context_->storage->IncrementStats(stats::v8_intern_errors);
     return std::nullopt;
   }
 
@@ -147,17 +141,16 @@
     return *id;
   }
 
-  auto* view = sequence_state_->current_generation()->GetInternedMessageView(
-      InternedData::kV8JsFunctionNameFieldNumber, iid);
+  auto* view =
+      GetInternedMessageView(InternedData::kV8JsFunctionNameFieldNumber, iid);
 
   if (!view) {
-    sequence_state_->context()->storage->IncrementStats(
-        stats::v8_intern_errors);
+    context_->storage->IncrementStats(stats::v8_intern_errors);
     return std::nullopt;
   }
 
   InternedV8String::Decoder function_name(ToConstBytes(view->message()));
-  auto& storage = *sequence_state_->context()->storage;
+  auto& storage = *context_->storage;
   StringId id;
   if (function_name.has_latin1()) {
     id = storage.InternString(
diff --git a/src/trace_processor/importers/proto/v8_sequence_state.h b/src/trace_processor/importers/proto/v8_sequence_state.h
index 781960d..d2e008a 100644
--- a/src/trace_processor/importers/proto/v8_sequence_state.h
+++ b/src/trace_processor/importers/proto/v8_sequence_state.h
@@ -21,7 +21,7 @@
 #include <optional>
 
 #include "perfetto/ext/base/flat_hash_map.h"
-#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/tables/v8_tables_py.h"
 #include "src/trace_processor/types/destructible.h"
@@ -29,19 +29,14 @@
 namespace perfetto {
 namespace trace_processor {
 
+class TraceProcessorContext;
 class V8Tracker;
 
 // Helper class to deal with V8 related interned data.
-class V8SequenceState : public Destructible {
+class V8SequenceState final
+    : public PacketSequenceStateGeneration::InternedDataTracker {
  public:
-  static V8SequenceState* GetOrCreate(PacketSequenceState* sequence_state) {
-    auto& v8_sequence_state =
-        sequence_state->extensible_sequence_state().v8_sequence_state;
-    if (!v8_sequence_state) {
-      v8_sequence_state.reset(new V8SequenceState(sequence_state));
-    }
-    return static_cast<V8SequenceState*>(v8_sequence_state.get());
-  }
+  explicit V8SequenceState(TraceProcessorContext* context);
 
   ~V8SequenceState() override;
 
@@ -54,13 +49,12 @@
       tables::V8IsolateTable::Id isolate_id);
 
  private:
-  explicit V8SequenceState(PacketSequenceState* sequence_state);
   std::optional<tables::V8JsScriptTable::Id> GetOrInsertJsScript(
       uint64_t iid,
       tables::V8IsolateTable::Id isolate_id);
   std::optional<StringId> GetOrInsertJsFunctionName(uint64_t iid);
 
-  PacketSequenceState* const sequence_state_;
+  TraceProcessorContext* const context_;
   V8Tracker* const v8_tracker_;
 
   using InterningId = uint64_t;
diff --git a/src/trace_processor/importers/proto/vulkan_memory_tracker.h b/src/trace_processor/importers/proto/vulkan_memory_tracker.h
index 1457923..9d1afc5 100644
--- a/src/trace_processor/importers/proto/vulkan_memory_tracker.h
+++ b/src/trace_processor/importers/proto/vulkan_memory_tracker.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_VULKAN_MEMORY_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_VULKAN_MEMORY_TRACKER_H_
 
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
diff --git a/src/trace_processor/importers/proto/winscope/protolog_parser.h b/src/trace_processor/importers/proto/winscope/protolog_parser.h
index fbcf9d9..97d9183 100644
--- a/src/trace_processor/importers/proto/winscope/protolog_parser.h
+++ b/src/trace_processor/importers/proto/winscope/protolog_parser.h
@@ -19,6 +19,7 @@
 
 #include "protos/perfetto/trace/android/protolog.pbzero.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/util/descriptors.h"
 #include "src/trace_processor/util/proto_to_args_parser.h"
 
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.cc
index fde9046..b4d76cf 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.cc
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.cc
@@ -31,8 +31,8 @@
 #include "src/trace_processor/db/column/types.h"
 #include "src/trace_processor/db/table.h"
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
-#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/tables/profiler_tables_py.h"
 #include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 66a524e..ec72287 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -115,6 +115,8 @@
   F(flow_end_without_start,               kSingle,  kInfo,     kTrace,    ""), \
   F(flow_invalid_id,                      kSingle,  kError,    kTrace,    ""), \
   F(flow_without_direction,               kSingle,  kError,    kTrace,    ""), \
+  F(stackprofile_empty_callstack,         kSingle,  kError,    kTrace,         \
+      "Callstack had no frames. Ignored"),                                     \
   F(stackprofile_invalid_string_id,       kSingle,  kError,    kTrace,    ""), \
   F(stackprofile_invalid_mapping_id,      kSingle,  kError,    kTrace,    ""), \
   F(stackprofile_invalid_frame_id,        kSingle,  kError,    kTrace,    ""), \
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index 34da487..c7cea38 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -31,13 +31,12 @@
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/common/slice_translation_table.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
-#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
 #include "src/trace_processor/importers/proto/perf_sample_tracker.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/importers/proto/track_event_module.h"
 #include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/types/destructible.h"
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index f24a1fc..05b88c0 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -30,15 +30,14 @@
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/common/slice_translation_table.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/proto/chrome_track_event.descriptor.h"
 #include "src/trace_processor/importers/proto/default_modules.h"
-#include "src/trace_processor/importers/proto/heap_profile_tracker.h"
 #include "src/trace_processor/importers/proto/packet_analyzer.h"
 #include "src/trace_processor/importers/proto/perf_sample_tracker.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/importers/proto/proto_trace_reader.h"
-#include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/importers/proto/track_event.descriptor.h"
 #include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/util/descriptors.h"
@@ -63,9 +62,8 @@
   context_.process_tracker.reset(new ProcessTracker(&context_));
   context_.clock_tracker.reset(new ClockTracker(&context_));
   context_.clock_converter.reset(new ClockConverter(&context_));
-  context_.heap_profile_tracker.reset(new HeapProfileTracker(&context_));
   context_.perf_sample_tracker.reset(new PerfSampleTracker(&context_));
-  context_.global_stack_profile_tracker.reset(new GlobalStackProfileTracker());
+  context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
   context_.metadata_tracker.reset(new MetadataTracker(context_.storage.get()));
   context_.global_args_tracker.reset(
       new GlobalArgsTracker(context_.storage.get()));
@@ -143,7 +141,6 @@
   }
   context_.event_tracker->FlushPendingEvents();
   context_.slice_tracker->FlushPendingSlices();
-  context_.heap_profile_tracker->NotifyEndOfFile();
   context_.args_tracker->Flush();
   context_.process_tracker->NotifyEndOfFile();
 }
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 25f129b..ad4e148 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -52,9 +52,8 @@
 class ForwardingTraceParser;
 class FtraceModule;
 class GlobalArgsTracker;
-class GlobalStackProfileTracker;
+class StackProfileTracker;
 class HeapGraphTracker;
-class HeapProfileTracker;
 class PerfSampleTracker;
 class MetadataTracker;
 class PacketAnalyzer;
@@ -101,9 +100,8 @@
   std::unique_ptr<EventTracker> event_tracker;
   std::unique_ptr<ClockTracker> clock_tracker;
   std::unique_ptr<ClockConverter> clock_converter;
-  std::unique_ptr<HeapProfileTracker> heap_profile_tracker;
   std::unique_ptr<PerfSampleTracker> perf_sample_tracker;
-  std::unique_ptr<GlobalStackProfileTracker> global_stack_profile_tracker;
+  std::unique_ptr<StackProfileTracker> stack_profile_tracker;
   std::unique_ptr<MetadataTracker> metadata_tracker;
 
   // These fields are stored as pointers to Destructible objects rather than
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index c717d3e..8420a06 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -61,6 +61,19 @@
   }
 }
 
+source_set("profiler_util") {
+  sources = [
+    "profiler_util.cc",
+    "profiler_util.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/base:base",
+    "../../../protos/perfetto/trace/profiling:zero",
+    "../storage:storage",
+  ]
+}
+
 source_set("stack_traces_util") {
   sources = [
     "stack_traces_util.cc",
@@ -69,6 +82,7 @@
   deps = [
     "../../../gn:default_deps",
     "../../../include/perfetto/ext/base:base",
+    "../../../protos/perfetto/trace/profiling:zero",
   ]
 }
 
diff --git a/src/trace_processor/importers/proto/profiler_util.cc b/src/trace_processor/util/profiler_util.cc
similarity index 98%
rename from src/trace_processor/importers/proto/profiler_util.cc
rename to src/trace_processor/util/profiler_util.cc
index 79a5448..71c3c25 100644
--- a/src/trace_processor/importers/proto/profiler_util.cc
+++ b/src/trace_processor/util/profiler_util.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/importers/proto/profiler_util.h"
+#include "src/trace_processor/util/profiler_util.h"
 #include <optional>
 
 #include "perfetto/ext/base/string_utils.h"
diff --git a/src/trace_processor/importers/proto/profiler_util.h b/src/trace_processor/util/profiler_util.h
similarity index 86%
rename from src/trace_processor/importers/proto/profiler_util.h
rename to src/trace_processor/util/profiler_util.h
index cbf8ad1..3af3606 100644
--- a/src/trace_processor/importers/proto/profiler_util.h
+++ b/src/trace_processor/util/profiler_util.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILER_UTIL_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILER_UTIL_H_
+#ifndef SRC_TRACE_PROCESSOR_UTIL_PROFILER_UTIL_H_
+#define SRC_TRACE_PROCESSOR_UTIL_PROFILER_UTIL_H_
 
 #include <optional>
 #include <string>
@@ -38,4 +38,4 @@
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILER_UTIL_H_
+#endif  // SRC_TRACE_PROCESSOR_UTIL_PROFILER_UTIL_H_