processor: Extract generations of incremental state into helper class

Adds a PacketSequenceStateGeneration class, which effectively
encapsulates the state pointer and generation data into a single object.

This way, it's possible to store a single pointer in
TimestampedTracePiece, rather than a pointer and index.

Bug: 123864183, 142557489
Change-Id: I14fd3d97ca67fb2ddaa33b23d4f28c4563affa41
diff --git a/Android.bp b/Android.bp
index 9a0c021..40693fa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5812,6 +5812,7 @@
     "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc",
     "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc",
     "src/trace_processor/importers/proto/args_table_utils.cc",
+    "src/trace_processor/importers/proto/packet_sequence_state.cc",
     "src/trace_processor/importers/proto/proto_importer_module.cc",
     "src/trace_processor/importers/proto/proto_trace_parser.cc",
     "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
diff --git a/BUILD b/BUILD
index b9f8000..f94416d 100644
--- a/BUILD
+++ b/BUILD
@@ -914,6 +914,7 @@
         "src/trace_processor/importers/proto/args_table_utils.cc",
         "src/trace_processor/importers/proto/args_table_utils.h",
         "src/trace_processor/importers/proto/chrome_compositor_scheduler_state.descriptor.h",
+        "src/trace_processor/importers/proto/packet_sequence_state.cc",
         "src/trace_processor/importers/proto/packet_sequence_state.h",
         "src/trace_processor/importers/proto/proto_importer_module.cc",
         "src/trace_processor/importers/proto/proto_importer_module.h",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 097fd06..76835be 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -96,6 +96,7 @@
     "importers/proto/args_table_utils.cc",
     "importers/proto/args_table_utils.h",
     "importers/proto/chrome_compositor_scheduler_state.descriptor.h",
+    "importers/proto/packet_sequence_state.cc",
     "importers/proto/packet_sequence_state.h",
     "importers/proto/proto_importer_module.cc",
     "importers/proto/proto_importer_module.h",
diff --git a/src/trace_processor/importers/proto/args_table_utils.cc b/src/trace_processor/importers/proto/args_table_utils.cc
index aec9800..0727718 100644
--- a/src/trace_processor/importers/proto/args_table_utils.cc
+++ b/src/trace_processor/importers/proto/args_table_utils.cc
@@ -24,13 +24,12 @@
 namespace perfetto {
 namespace trace_processor {
 
-ProtoToArgsTable::ProtoToArgsTable(PacketSequenceState* sequence_state,
-                                   size_t sequence_state_generation,
-                                   TraceProcessorContext* context,
-                                   std::string starting_prefix,
-                                   size_t prefix_size_hint)
-    : state_{context, sequence_state, sequence_state_generation},
-      prefix_(std::move(starting_prefix)) {
+ProtoToArgsTable::ProtoToArgsTable(
+    PacketSequenceStateGeneration* sequence_state,
+    TraceProcessorContext* context,
+    std::string starting_prefix,
+    size_t prefix_size_hint)
+    : state_{context, sequence_state}, prefix_(std::move(starting_prefix)) {
   prefix_.reserve(prefix_size_hint);
 }
 
diff --git a/src/trace_processor/importers/proto/args_table_utils.h b/src/trace_processor/importers/proto/args_table_utils.h
index 1ba6e9a..9eb95b2 100644
--- a/src/trace_processor/importers/proto/args_table_utils.h
+++ b/src/trace_processor/importers/proto/args_table_utils.h
@@ -71,8 +71,7 @@
  public:
   struct ParsingOverrideState {
     TraceProcessorContext* context;
-    PacketSequenceState* sequence_state;
-    size_t sequence_generation;
+    PacketSequenceStateGeneration* sequence_state;
   };
   using ParsingOverride = bool (*)(const ParsingOverrideState& state,
                                    const protozero::Field&,
@@ -99,14 +98,13 @@
     std::string* str_;
   };
 
-  // |sequence_state| and |sequence_state_generation| provide access to
-  // interning data. |context| provides access to storage.
+  // |sequence_state| provides access to interning data.
+  // |context| provides access to storage.
   //
   // |starting_prefix| will be prepended to all columns.
   // |prefix_size_hint| allows the class to upfront reserve the expected string
   // size needed.
-  ProtoToArgsTable(PacketSequenceState* sequence_state,
-                   size_t sequence_state_generation,
+  ProtoToArgsTable(PacketSequenceStateGeneration* sequence_state,
                    TraceProcessorContext* context,
                    std::string starting_prefix = "",
                    size_t prefix_size_hint = 64);
diff --git a/src/trace_processor/importers/proto/args_table_utils_unittest.cc b/src/trace_processor/importers/proto/args_table_utils_unittest.cc
index d5d3a3b..4cc03e9 100644
--- a/src/trace_processor/importers/proto/args_table_utils_unittest.cc
+++ b/src/trace_processor/importers/proto/args_table_utils_unittest.cc
@@ -77,8 +77,7 @@
 };
 
 TEST_F(ArgsTableUtilsTest, EnsureChromeCompositorStateDescriptorParses) {
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   auto status = helper.AddProtoFileDescriptor(
       kChromeCompositorSchedulerStateDescriptor.data(),
@@ -89,8 +88,7 @@
 }
 
 TEST_F(ArgsTableUtilsTest, EnsureTestMessageProtoParses) {
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
@@ -127,8 +125,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
@@ -201,8 +198,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
@@ -231,8 +227,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
@@ -261,8 +256,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
@@ -304,8 +298,7 @@
   auto binary_proto = msg.SerializeAsArray();
 
   storage_->mutable_track_table()->Insert({});
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   auto status = helper.AddProtoFileDescriptor(kTestMessagesDescriptor.data(),
                                               kTestMessagesDescriptor.size());
@@ -361,8 +354,7 @@
       protos::pbzero::InternedData::kSourceLocationsFieldNumber,
       std::move(blob));
 
-  ProtoToArgsTable helper(sequence_state_.get(),
-                          sequence_state_->current_generation(), &context_, "",
+  ProtoToArgsTable helper(sequence_state_->current_generation(), &context_, "",
                           0);
   // Now we override the behaviour of |value_c| so we can expand the iid into
   // multiple args rows.
@@ -373,8 +365,7 @@
         EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
         auto* decoder = state.sequence_state->LookupInternedMessage<
             protos::pbzero::InternedData::kSourceLocationsFieldNumber,
-            protos::pbzero::SourceLocation>(state.sequence_generation,
-                                            field.as_uint64());
+            protos::pbzero::SourceLocation>(field.as_uint64());
         if (!decoder) {
           // Lookup failed fall back on default behaviour.
           return false;
diff --git a/src/trace_processor/importers/proto/graphics_event_module.cc b/src/trace_processor/importers/proto/graphics_event_module.cc
index e9fda0a..38979f2 100644
--- a/src/trace_processor/importers/proto/graphics_event_module.cc
+++ b/src/trace_processor/importers/proto/graphics_event_module.cc
@@ -53,10 +53,8 @@
       return;
     case TracePacket::kVulkanMemoryEventFieldNumber:
       PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      parser_.ParseVulkanMemoryEvent(
-          ttp.packet_data.packet_sequence_state,
-          ttp.packet_data.packet_sequence_state_generation,
-          decoder.vulkan_memory_event());
+      parser_.ParseVulkanMemoryEvent(ttp.packet_data.sequence_state,
+                                     decoder.vulkan_memory_event());
       return;
     case TracePacket::kVulkanApiEventFieldNumber:
       parser_.ParseVulkanApiEvent(decoder.vulkan_api_event());
diff --git a/src/trace_processor/importers/proto/graphics_event_parser.cc b/src/trace_processor/importers/proto/graphics_event_parser.cc
index 240c4d9..9f0f163 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.cc
+++ b/src/trace_processor/importers/proto/graphics_event_parser.cc
@@ -570,8 +570,7 @@
 }
 
 void GraphicsEventParser::ParseVulkanMemoryEvent(
-    PacketSequenceState* sequence_state,
-    size_t sequence_state_generation,
+    PacketSequenceStateGeneration* sequence_state,
     ConstBytes blob) {
   using protos::pbzero::InternedData;
   VulkanMemoryEvent::Decoder vulkan_memory_event(blob.data, blob.size);
@@ -599,7 +598,7 @@
     vulkan_memory_event_row.function_name =
         vulkan_memory_tracker_
             .GetInternedString<InternedData::kFunctionNamesFieldNumber>(
-                sequence_state, sequence_state_generation,
+                sequence_state,
                 static_cast<uint64_t>(vulkan_memory_event.caller_iid()));
   }
   if (vulkan_memory_event.has_object_handle())
@@ -634,8 +633,7 @@
       auto key_id =
           vulkan_memory_tracker_
               .GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>(
-                  sequence_state, sequence_state_generation,
-                  static_cast<uint64_t>(annotation.key_iid()));
+                  sequence_state, static_cast<uint64_t>(annotation.key_iid()));
 
       if (annotation.has_int_value()) {
         inserter.AddArg(key_id, Variadic::Integer(annotation.int_value()));
@@ -645,7 +643,7 @@
         auto string_id =
             vulkan_memory_tracker_
                 .GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>(
-                    sequence_state, sequence_state_generation,
+                    sequence_state,
                     static_cast<uint64_t>(annotation.string_iid()));
 
         inserter.AddArg(key_id, Variadic::String(string_id.id));
diff --git a/src/trace_processor/importers/proto/graphics_event_parser.h b/src/trace_processor/importers/proto/graphics_event_parser.h
index 7d31175..d9e71c3 100644
--- a/src/trace_processor/importers/proto/graphics_event_parser.h
+++ b/src/trace_processor/importers/proto/graphics_event_parser.h
@@ -60,9 +60,7 @@
   void ParseGraphicsFrameEvent(int64_t timestamp, ConstBytes);
   void ParseGpuLog(int64_t ts, ConstBytes);
 
-  void ParseVulkanMemoryEvent(PacketSequenceState*,
-                              size_t sequence_state_generation,
-                              ConstBytes);
+  void ParseVulkanMemoryEvent(PacketSequenceStateGeneration*, ConstBytes);
   void UpdateVulkanMemoryAllocationCounters(UniquePid,
                                             const VulkanMemoryEvent::Decoder&);
 
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.cc b/src/trace_processor/importers/proto/packet_sequence_state.cc
new file mode 100644
index 0000000..c63c2d7
--- /dev/null
+++ b/src/trace_processor/importers/proto/packet_sequence_state.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void PacketSequenceStateGeneration::InternMessage(uint32_t field_id,
+                                                  TraceBlobView message) {
+  constexpr auto kIidFieldNumber = 1;
+
+  uint64_t iid = 0;
+  auto message_start = message.data();
+  auto message_size = message.length();
+  protozero::ProtoDecoder decoder(message_start, message_size);
+
+  auto field = decoder.FindField(kIidFieldNumber);
+  if (PERFETTO_UNLIKELY(!field)) {
+    PERFETTO_DLOG("Interned message without interning_id");
+    state_->context()->storage->IncrementStats(
+        stats::interned_data_tokenizer_errors);
+    return;
+  }
+  iid = field.as_uint64();
+
+  auto res = interned_data_[field_id].emplace(
+      iid, InternedMessageView(std::move(message)));
+
+  // If a message with this ID is already interned in the same generation,
+  // its data should not have changed (this is forbidden by the InternedData
+  // proto).
+  // TODO(eseckler): This DCHECK assumes that the message is encoded the
+  // same way if it is re-emitted.
+  PERFETTO_DCHECK(res.second ||
+                  (res.first->second.message().length() == message_size &&
+                   memcmp(res.first->second.message().data(), message_start,
+                          message_size) == 0));
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index c020758..903f99d 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -42,127 +42,174 @@
 #define PERFETTO_TYPE_IDENTIFIER nullptr
 #endif  // PERFETTO_DCHECK_IS_ON()
 
+// Entry in an interning index, refers to the interned message.
+class InternedMessageView {
+ public:
+  InternedMessageView(TraceBlobView msg) : message_(std::move(msg)) {}
+
+  InternedMessageView(InternedMessageView&&) noexcept = default;
+  InternedMessageView& operator=(InternedMessageView&&) = default;
+
+  // Allow copy by cloning the TraceBlobView. This is required for
+  // UpdateTracePacketDefaults().
+  InternedMessageView(const InternedMessageView& view)
+      : message_(view.message_.slice(0, view.message_.length())) {}
+  InternedMessageView& operator=(const InternedMessageView& view) {
+    this->message_ = view.message_.slice(0, view.message_.length());
+    this->decoder_ = nullptr;
+    this->decoder_type_ = nullptr;
+    this->submessages_.clear();
+    return *this;
+  }
+
+  // Lazily initializes and returns the decoder object for the message. The
+  // decoder is stored in the InternedMessageView to avoid having to parse the
+  // message multiple times.
+  template <typename MessageType>
+  typename MessageType::Decoder* GetOrCreateDecoder() {
+    if (!decoder_) {
+      // Lazy init the decoder and save it away, so that we don't have to
+      // reparse the message every time we access the interning entry.
+      decoder_ = std::unique_ptr<void, std::function<void(void*)>>(
+          new typename MessageType::Decoder(message_.data(), message_.length()),
+          [](void* obj) {
+            delete reinterpret_cast<typename MessageType::Decoder*>(obj);
+          });
+      decoder_type_ = PERFETTO_TYPE_IDENTIFIER;
+    }
+    // Verify that the type of the decoder didn't change.
+    if (PERFETTO_TYPE_IDENTIFIER &&
+        strcmp(decoder_type_,
+               // GCC complains if this arg can be null.
+               PERFETTO_TYPE_IDENTIFIER ? PERFETTO_TYPE_IDENTIFIER : "") != 0) {
+      PERFETTO_FATAL(
+          "Interning entry accessed under different types! previous type: "
+          "%s. new type: %s.",
+          decoder_type_, __PRETTY_FUNCTION__);
+    }
+    return reinterpret_cast<typename MessageType::Decoder*>(decoder_.get());
+  }
+
+  // Lookup a submessage of the interned message, which is then itself stored
+  // as InternedMessageView, so that we only need to parse it once. Returns
+  // nullptr if the field isn't set.
+  // TODO(eseckler): Support repeated fields.
+  template <typename MessageType, uint32_t FieldId>
+  InternedMessageView* GetOrCreateSubmessageView() {
+    auto it = submessages_.find(FieldId);
+    if (it != submessages_.end())
+      return it->second.get();
+    auto* decoder = GetOrCreateDecoder<MessageType>();
+    // Calls the at() template method on the decoder.
+    auto field = decoder->template at<FieldId>().as_bytes();
+    if (!field.data)
+      return nullptr;
+    const size_t offset = message_.offset_of(field.data);
+    TraceBlobView submessage = message_.slice(offset, field.size);
+    InternedMessageView* submessage_view =
+        new InternedMessageView(std::move(submessage));
+    submessages_.emplace_hint(
+        it, FieldId, std::unique_ptr<InternedMessageView>(submessage_view));
+    return submessage_view;
+  }
+
+  const TraceBlobView& message() { return message_; }
+
+ private:
+  using SubMessageViewMap =
+      std::unordered_map<uint32_t /*field_id*/,
+                         std::unique_ptr<InternedMessageView>>;
+
+  TraceBlobView message_;
+
+  // Stores the decoder for the message_, so that the message does not have to
+  // be re-decoded every time the interned message is looked up. Lazily
+  // initialized in GetOrCreateDecoder(). Since we don't know the type of the
+  // decoder until GetOrCreateDecoder() is called, we store the decoder as a
+  // void* unique_pointer with a destructor function that's supplied in
+  // GetOrCreateDecoder() when the decoder is created.
+  std::unique_ptr<void, std::function<void(void*)>> decoder_;
+
+  // Type identifier for the decoder. Only valid in debug builds and on
+  // supported platforms. Used to verify that GetOrCreateDecoder() is always
+  // called with the same template argument.
+  const char* decoder_type_ = nullptr;
+
+  // Views of submessages of the interned message. Submessages are lazily
+  // added by GetOrCreateSubmessageView(). By storing submessages and their
+  // decoders, we avoid having to decode submessages multiple times if they
+  // looked up often.
+  SubMessageViewMap submessages_;
+};
+
+using InternedMessageMap =
+    std::unordered_map<uint64_t /*iid*/, InternedMessageView>;
+using InternedFieldMap =
+    std::unordered_map<uint32_t /*field_id*/, InternedMessageMap>;
+
+class PacketSequenceState;
+
+class PacketSequenceStateGeneration {
+ public:
+  // 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>
+  typename MessageType::Decoder* LookupInternedMessage(uint64_t iid);
+
+  // Returns |nullptr| if no defaults were set in the given generation.
+  InternedMessageView* GetTracePacketDefaultsView() {
+    if (!trace_packet_defaults_)
+      return nullptr;
+    return &trace_packet_defaults_.value();
+  }
+
+  // Returns |nullptr| if no defaults were set in the given generation.
+  typename protos::pbzero::TracePacketDefaults::Decoder*
+  GetTracePacketDefaults() {
+    InternedMessageView* view = GetTracePacketDefaultsView();
+    if (!view)
+      return nullptr;
+    return view->GetOrCreateDecoder<protos::pbzero::TracePacketDefaults>();
+  }
+
+  PacketSequenceState* state() const { return state_; }
+
+ private:
+  friend class PacketSequenceState;
+
+  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))) {}
+
+  void InternMessage(uint32_t field_id, TraceBlobView message);
+
+  void SetTracePacketDefaults(TraceBlobView defaults) {
+    // Defaults should only be set once per generation.
+    PERFETTO_DCHECK(!trace_packet_defaults_);
+    trace_packet_defaults_ = InternedMessageView(std::move(defaults));
+  }
+
+  PacketSequenceState* state_;
+  size_t generation_index_;
+  InternedFieldMap interned_data_;
+  base::Optional<InternedMessageView> trace_packet_defaults_;
+};
+
 class PacketSequenceState {
  public:
-  // Entry in an interning index, refers to the interned message.
-  class InternedMessageView {
-   public:
-    InternedMessageView(TraceBlobView msg) : message_(std::move(msg)) {}
-
-    InternedMessageView(InternedMessageView&&) noexcept = default;
-    InternedMessageView& operator=(InternedMessageView&&) = default;
-
-    // Allow copy by cloning the TraceBlobView. This is required for
-    // UpdateTracePacketDefaults().
-    InternedMessageView(const InternedMessageView& view)
-        : message_(view.message_.slice(0, view.message_.length())) {}
-    InternedMessageView& operator=(const InternedMessageView& view) {
-      this->message_ = view.message_.slice(0, view.message_.length());
-      this->decoder_ = nullptr;
-      this->decoder_type_ = nullptr;
-      this->submessages_.clear();
-      return *this;
-    }
-
-    // Lazily initializes and returns the decoder object for the message. The
-    // decoder is stored in the InternedMessageView to avoid having to parse the
-    // message multiple times.
-    template <typename MessageType>
-    typename MessageType::Decoder* GetOrCreateDecoder() {
-      if (!decoder_) {
-        // Lazy init the decoder and save it away, so that we don't have to
-        // reparse the message every time we access the interning entry.
-        decoder_ = std::unique_ptr<void, std::function<void(void*)>>(
-            new
-            typename MessageType::Decoder(message_.data(), message_.length()),
-            [](void* obj) {
-              delete reinterpret_cast<typename MessageType::Decoder*>(obj);
-            });
-        decoder_type_ = PERFETTO_TYPE_IDENTIFIER;
-      }
-      // Verify that the type of the decoder didn't change.
-      if (PERFETTO_TYPE_IDENTIFIER &&
-          strcmp(decoder_type_,
-                 // GCC complains if this arg can be null.
-                 PERFETTO_TYPE_IDENTIFIER ? PERFETTO_TYPE_IDENTIFIER : "") !=
-              0) {
-        PERFETTO_FATAL(
-            "Interning entry accessed under different types! previous type: "
-            "%s. new type: %s.",
-            decoder_type_, __PRETTY_FUNCTION__);
-      }
-      return reinterpret_cast<typename MessageType::Decoder*>(decoder_.get());
-    }
-
-    // Lookup a submessage of the interned message, which is then itself stored
-    // as InternedMessageView, so that we only need to parse it once. Returns
-    // nullptr if the field isn't set.
-    // TODO(eseckler): Support repeated fields.
-    template <typename MessageType, uint32_t FieldId>
-    InternedMessageView* GetOrCreateSubmessageView() {
-      auto it = submessages_.find(FieldId);
-      if (it != submessages_.end())
-        return it->second.get();
-      auto* decoder = GetOrCreateDecoder<MessageType>();
-      // Calls the at() template method on the decoder.
-      auto field = decoder->template at<FieldId>().as_bytes();
-      if (!field.data)
-        return nullptr;
-      const size_t offset = message_.offset_of(field.data);
-      TraceBlobView submessage = message_.slice(offset, field.size);
-      InternedMessageView* submessage_view =
-          new InternedMessageView(std::move(submessage));
-      submessages_.emplace_hint(
-          it, FieldId, std::unique_ptr<InternedMessageView>(submessage_view));
-      return submessage_view;
-    }
-
-    const TraceBlobView& message() { return message_; }
-
-   private:
-    using SubMessageViewMap =
-        std::unordered_map<uint32_t /*field_id*/,
-                           std::unique_ptr<InternedMessageView>>;
-
-    TraceBlobView message_;
-
-    // Stores the decoder for the message_, so that the message does not have to
-    // be re-decoded every time the interned message is looked up. Lazily
-    // initialized in GetOrCreateDecoder(). Since we don't know the type of the
-    // decoder until GetOrCreateDecoder() is called, we store the decoder as a
-    // void* unique_pointer with a destructor function that's supplied in
-    // GetOrCreateDecoder() when the decoder is created.
-    std::unique_ptr<void, std::function<void(void*)>> decoder_;
-
-    // Type identifier for the decoder. Only valid in debug builds and on
-    // supported platforms. Used to verify that GetOrCreateDecoder() is always
-    // called with the same template argument.
-    const char* decoder_type_ = nullptr;
-
-    // Views of submessages of the interned message. Submessages are lazily
-    // added by GetOrCreateSubmessageView(). By storing submessages and their
-    // decoders, we avoid having to decode submessages multiple times if they
-    // looked up often.
-    SubMessageViewMap submessages_;
-  };
-
-  using InternedMessageMap =
-      std::unordered_map<uint64_t /*iid*/, InternedMessageView>;
-  using InternedFieldMap =
-      std::unordered_map<uint32_t /*field_id*/, InternedMessageMap>;
-
-  struct GenerationData {
-    InternedFieldMap interned_data;
-    base::Optional<InternedMessageView> trace_packet_defaults;
-  };
-
-  // TODO(eseckler): Reference count the generations so that we can get rid of
-  // past generations once all packets referring to them have been parsed.
-  using GenerationList = std::vector<GenerationData>;
-
   PacketSequenceState(TraceProcessorContext* context)
       : context_(context), stack_profile_tracker_(context) {
-    generations_.emplace_back();
+    generations_.emplace_back(
+        new PacketSequenceStateGeneration(this, generations_.size()));
   }
 
   int64_t IncrementAndGetTrackEventTimeNs(int64_t delta_ns) {
@@ -183,14 +230,26 @@
     return track_event_thread_instruction_count_;
   }
 
-  void OnPacketLoss() {
-    packet_loss_ = true;
-    track_event_timestamps_valid_ = false;
+  // Intern a message into the current generation.
+  void InternMessage(uint32_t field_id, TraceBlobView message) {
+    generations_.back()->InternMessage(field_id, std::move(message));
   }
 
-  void OnIncrementalStateCleared() {
-    packet_loss_ = false;
-    generations_.emplace_back();  // Bump generation number
+  // Set the trace packet defaults for the current generation. If the current
+  // generation already has defaults set, starts a new generation without
+  // invalidating other incremental state (such as interned data).
+  void UpdateTracePacketDefaults(TraceBlobView defaults) {
+    if (!generations_.back()->GetTracePacketDefaultsView()) {
+      generations_.back()->SetTracePacketDefaults(std::move(defaults));
+      return;
+    }
+
+    // The new defaults should only apply to subsequent messages on the
+    // sequence. Add a new generation with the updated defaults but the
+    // current generation's interned data state.
+    generations_.emplace_back(new PacketSequenceStateGeneration(
+        this, generations_.size(), generations_.back()->interned_data_,
+        std::move(defaults)));
   }
 
   void SetThreadDescriptor(int32_t pid,
@@ -207,14 +266,28 @@
     track_event_thread_instruction_count_ = thread_instruction_count;
   }
 
+  void OnPacketLoss() {
+    packet_loss_ = true;
+    track_event_timestamps_valid_ = false;
+  }
+
+  // Starts a new generation with clean-slate incremental state and defaults.
+  void OnIncrementalStateCleared() {
+    packet_loss_ = false;
+    generations_.emplace_back(
+        new PacketSequenceStateGeneration(this, generations_.size()));
+  }
+
   bool IsIncrementalStateValid() const { return !packet_loss_; }
 
   StackProfileTracker& stack_profile_tracker() {
     return stack_profile_tracker_;
   }
 
-  // Returns the index of the current generation in the GenerationList.
-  size_t current_generation() const { return generations_.size() - 1; }
+  // Returns a pointer to the current generation.
+  PacketSequenceStateGeneration* current_generation() const {
+    return generations_.back().get();
+  }
 
   bool track_event_timestamps_valid() const {
     return track_event_timestamps_valid_;
@@ -225,90 +298,14 @@
   int32_t pid() const { return pid_; }
   int32_t tid() const { return tid_; }
 
-  void InternMessage(uint32_t field_id, TraceBlobView message) {
-    constexpr auto kIidFieldNumber = 1;
-
-    uint64_t iid = 0;
-    auto message_start = message.data();
-    auto message_size = message.length();
-    protozero::ProtoDecoder decoder(message_start, message_size);
-
-    auto field = decoder.FindField(kIidFieldNumber);
-    if (PERFETTO_UNLIKELY(!field)) {
-      PERFETTO_DLOG("Interned message without interning_id");
-      context_->storage->IncrementStats(stats::interned_data_tokenizer_errors);
-      return;
-    }
-    iid = field.as_uint64();
-
-    auto* map = &generations_.back().interned_data[field_id];
-    auto res = map->emplace(iid, InternedMessageView(std::move(message)));
-
-    // If a message with this ID is already interned in the same generation,
-    // its data should not have changed (this is forbidden by the InternedData
-    // proto).
-    // TODO(eseckler): This DCHECK assumes that the message is encoded the
-    // same way if it is re-emitted.
-    PERFETTO_DCHECK(res.second ||
-                    (res.first->second.message().length() == message_size &&
-                     memcmp(res.first->second.message().data(), message_start,
-                            message_size) == 0));
-  }
-
-  // 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>
-  typename MessageType::Decoder* LookupInternedMessage(size_t generation,
-                                                       uint64_t iid) {
-    PERFETTO_CHECK(generation <= generations_.size());
-    auto* field_map = &generations_[generation].interned_data;
-    auto field_it = field_map->find(FieldId);
-    if (field_it != field_map->end()) {
-      auto* message_map = &field_it->second;
-      auto it = message_map->find(iid);
-      if (it != message_map->end()) {
-        return it->second.GetOrCreateDecoder<MessageType>();
-      }
-    }
-    context_->storage->IncrementStats(stats::interned_data_tokenizer_errors);
-    PERFETTO_DLOG("Could not find interning entry for field ID %" PRIu32
-                  ", generation %zu, and IID %" PRIu64,
-                  FieldId, generation, iid);
-    return nullptr;
-  }
-
-  void UpdateTracePacketDefaults(TraceBlobView trace_packet_defaults) {
-    if (generations_.back().trace_packet_defaults) {
-      // The new defaults should only apply to subsequent messages on the
-      // sequence. Add a new generation with the updated defaults but the
-      // current generation's interned data state.
-      const InternedFieldMap& current_interned_data =
-          generations_.back().interned_data;
-      generations_.emplace_back();
-      generations_.back().interned_data = current_interned_data;
-    }
-    generations_.back().trace_packet_defaults =
-        InternedMessageView(std::move(trace_packet_defaults));
-  }
-
-  // Returns |nullptr| if no defaults were set in the given generation.
-  InternedMessageView* GetTracePacketDefaultsView(size_t generation) {
-    PERFETTO_CHECK(generation <= generations_.size());
-    if (!generations_[generation].trace_packet_defaults)
-      return nullptr;
-    return &generations_[generation].trace_packet_defaults.value();
-  }
-
-  // Returns |nullptr| if no defaults were set in the given generation.
-  typename protos::pbzero::TracePacketDefaults::Decoder* GetTracePacketDefaults(
-      size_t generation) {
-    InternedMessageView* view = GetTracePacketDefaultsView(generation);
-    if (!view)
-      return nullptr;
-    return view->GetOrCreateDecoder<protos::pbzero::TracePacketDefaults>();
-  }
+  TraceProcessorContext* context() const { return context_; }
 
  private:
+  // TODO(eseckler): Reference count the generations so that we can get rid of
+  // past generations once all packets referring to them have been parsed.
+  using GenerationList =
+      std::vector<std::unique_ptr<PacketSequenceStateGeneration>>;
+
   TraceProcessorContext* context_;
 
   // If true, incremental state on the sequence is considered invalid until we
@@ -340,6 +337,25 @@
   StackProfileTracker stack_profile_tracker_;
 };
 
+template <uint32_t FieldId, typename MessageType>
+typename MessageType::Decoder*
+PacketSequenceStateGeneration::LookupInternedMessage(uint64_t iid) {
+  auto field_it = interned_data_.find(FieldId);
+  if (field_it != interned_data_.end()) {
+    auto* message_map = &field_it->second;
+    auto it = message_map->find(iid);
+    if (it != message_map->end()) {
+      return it->second.GetOrCreateDecoder<MessageType>();
+    }
+  }
+  state_->context()->storage->IncrementStats(
+      stats::interned_data_tokenizer_errors);
+  PERFETTO_DLOG("Could not find interning entry for field ID %" PRIu32
+                ", generation %zu, and IID %" PRIu64,
+                FieldId, generation_index_, iid);
+  return nullptr;
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
 
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index eb32e46..66def9e 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -95,9 +95,8 @@
 
 class ProfilePacketInternLookup : public StackProfileTracker::InternLookup {
  public:
-  ProfilePacketInternLookup(PacketSequenceState* seq_state,
-                            size_t seq_state_generation)
-      : seq_state_(seq_state), seq_state_generation_(seq_state_generation) {}
+  ProfilePacketInternLookup(PacketSequenceStateGeneration* seq_state)
+      : seq_state_(seq_state) {}
 
   base::Optional<base::StringView> GetString(
       StackProfileTracker::SourceStringId iid,
@@ -107,17 +106,17 @@
       case StackProfileTracker::InternedStringType::kBuildId:
         decoder = seq_state_->LookupInternedMessage<
             protos::pbzero::InternedData::kBuildIdsFieldNumber,
-            protos::pbzero::InternedString>(seq_state_generation_, iid);
+            protos::pbzero::InternedString>(iid);
         break;
       case StackProfileTracker::InternedStringType::kFunctionName:
         decoder = seq_state_->LookupInternedMessage<
             protos::pbzero::InternedData::kFunctionNamesFieldNumber,
-            protos::pbzero::InternedString>(seq_state_generation_, iid);
+            protos::pbzero::InternedString>(iid);
         break;
       case StackProfileTracker::InternedStringType::kMappingPath:
         decoder = seq_state_->LookupInternedMessage<
             protos::pbzero::InternedData::kMappingPathsFieldNumber,
-            protos::pbzero::InternedString>(seq_state_generation_, iid);
+            protos::pbzero::InternedString>(iid);
         break;
     }
     if (!decoder)
@@ -130,7 +129,7 @@
       StackProfileTracker::SourceMappingId iid) const override {
     auto* decoder = seq_state_->LookupInternedMessage<
         protos::pbzero::InternedData::kMappingsFieldNumber,
-        protos::pbzero::Mapping>(seq_state_generation_, iid);
+        protos::pbzero::Mapping>(iid);
     if (!decoder)
       return base::nullopt;
     return MakeSourceMapping(*decoder);
@@ -140,7 +139,7 @@
       StackProfileTracker::SourceFrameId iid) const override {
     auto* decoder = seq_state_->LookupInternedMessage<
         protos::pbzero::InternedData::kFramesFieldNumber,
-        protos::pbzero::Frame>(seq_state_generation_, iid);
+        protos::pbzero::Frame>(iid);
     if (!decoder)
       return base::nullopt;
     return MakeSourceFrame(*decoder);
@@ -150,15 +149,14 @@
       StackProfileTracker::SourceCallstackId iid) const override {
     auto* decoder = seq_state_->LookupInternedMessage<
         protos::pbzero::InternedData::kCallstacksFieldNumber,
-        protos::pbzero::Callstack>(seq_state_generation_, iid);
+        protos::pbzero::Callstack>(iid);
     if (!decoder)
       return base::nullopt;
     return MakeSourceCallstack(*decoder);
   }
 
  private:
-  PacketSequenceState* seq_state_;
-  size_t seq_state_generation_;
+  PacketSequenceStateGeneration* seq_state_;
 };
 
 }  // namespace
@@ -219,14 +217,13 @@
     ParseTraceStats(packet.trace_stats());
 
   if (packet.has_profile_packet()) {
-    ParseProfilePacket(
-        ts, data->packet_sequence_state, data->packet_sequence_state_generation,
-        packet.trusted_packet_sequence_id(), packet.profile_packet());
+    ParseProfilePacket(ts, data->sequence_state,
+                       packet.trusted_packet_sequence_id(),
+                       packet.profile_packet());
   }
 
   if (packet.has_streaming_profile_packet()) {
-    ParseStreamingProfilePacket(data->packet_sequence_state,
-                                data->packet_sequence_state_generation,
+    ParseStreamingProfilePacket(data->sequence_state,
                                 packet.streaming_profile_packet());
   }
 
@@ -328,11 +325,11 @@
   }
 }
 
-void ProtoTraceParser::ParseProfilePacket(int64_t,
-                                          PacketSequenceState* sequence_state,
-                                          size_t sequence_state_generation,
-                                          uint32_t seq_id,
-                                          ConstBytes blob) {
+void ProtoTraceParser::ParseProfilePacket(
+    int64_t,
+    PacketSequenceStateGeneration* sequence_state,
+    uint32_t seq_id,
+    ConstBytes blob) {
   protos::pbzero::ProfilePacket::Decoder packet(blob.data, blob.size);
   context_->heap_profile_tracker->SetProfilePacketIndex(seq_id, packet.index());
 
@@ -341,28 +338,30 @@
 
     const char* str = reinterpret_cast<const char*>(entry.str().data);
     auto str_view = base::StringView(str, entry.str().size);
-    sequence_state->stack_profile_tracker().AddString(entry.iid(), str_view);
+    sequence_state->state()->stack_profile_tracker().AddString(entry.iid(),
+                                                               str_view);
   }
 
   for (auto it = packet.mappings(); it; ++it) {
     protos::pbzero::Mapping::Decoder entry(*it);
     StackProfileTracker::SourceMapping src_mapping = MakeSourceMapping(entry);
-    sequence_state->stack_profile_tracker().AddMapping(entry.iid(),
-                                                       src_mapping);
+    sequence_state->state()->stack_profile_tracker().AddMapping(entry.iid(),
+                                                                src_mapping);
   }
 
   for (auto it = packet.frames(); it; ++it) {
     protos::pbzero::Frame::Decoder entry(*it);
     StackProfileTracker::SourceFrame src_frame = MakeSourceFrame(entry);
-    sequence_state->stack_profile_tracker().AddFrame(entry.iid(), src_frame);
+    sequence_state->state()->stack_profile_tracker().AddFrame(entry.iid(),
+                                                              src_frame);
   }
 
   for (auto it = packet.callstacks(); it; ++it) {
     protos::pbzero::Callstack::Decoder entry(*it);
     StackProfileTracker::SourceCallstack src_callstack =
         MakeSourceCallstack(entry);
-    sequence_state->stack_profile_tracker().AddCallstack(entry.iid(),
-                                                         src_callstack);
+    sequence_state->state()->stack_profile_tracker().AddCallstack(
+        entry.iid(), src_callstack);
   }
 
   for (auto it = packet.process_dumps(); it; ++it) {
@@ -412,28 +411,26 @@
   }
   if (!packet.continued()) {
     PERFETTO_CHECK(sequence_state);
-    ProfilePacketInternLookup intern_lookup(sequence_state,
-                                            sequence_state_generation);
+    ProfilePacketInternLookup intern_lookup(sequence_state);
     context_->heap_profile_tracker->FinalizeProfile(
-        seq_id, &sequence_state->stack_profile_tracker(), &intern_lookup);
+        seq_id, &sequence_state->state()->stack_profile_tracker(),
+        &intern_lookup);
   }
 }
 
 void ProtoTraceParser::ParseStreamingProfilePacket(
-    PacketSequenceState* sequence_state,
-    size_t sequence_state_generation,
+    PacketSequenceStateGeneration* sequence_state,
     ConstBytes blob) {
   protos::pbzero::StreamingProfilePacket::Decoder packet(blob.data, blob.size);
 
   ProcessTracker* procs = context_->process_tracker.get();
   TraceStorage* storage = context_->storage.get();
   StackProfileTracker& stack_profile_tracker =
-      sequence_state->stack_profile_tracker();
-  ProfilePacketInternLookup intern_lookup(sequence_state,
-                                          sequence_state_generation);
+      sequence_state->state()->stack_profile_tracker();
+  ProfilePacketInternLookup intern_lookup(sequence_state);
 
-  uint32_t pid = static_cast<uint32_t>(sequence_state->pid());
-  uint32_t tid = static_cast<uint32_t>(sequence_state->tid());
+  uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
+  uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
   UniqueTid utid = procs->UpdateThread(tid, pid);
 
   auto timestamp_it = packet.timestamp_delta_us();
@@ -457,7 +454,7 @@
     int64_t callstack_id = *maybe_callstack_id;
 
     tables::CpuProfileStackSampleTable::Row sample_row{
-        sequence_state->IncrementAndGetTrackEventTimeNs(*timestamp_it),
+        sequence_state->state()->IncrementAndGetTrackEventTimeNs(*timestamp_it),
         callstack_id, utid};
     storage->mutable_cpu_profile_stack_sample_table()->Insert(sample_row);
   }
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.h b/src/trace_processor/importers/proto/proto_trace_parser.h
index 25b4367..9e20b3e 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.h
+++ b/src/trace_processor/importers/proto/proto_trace_parser.h
@@ -63,13 +63,10 @@
 
   void ParseTraceStats(ConstBytes);
   void ParseProfilePacket(int64_t ts,
-                          PacketSequenceState*,
-                          size_t sequence_state_generation,
+                          PacketSequenceStateGeneration*,
                           uint32_t seq_id,
                           ConstBytes);
-  void ParseStreamingProfilePacket(PacketSequenceState*,
-                                   size_t sequence_state_generation,
-                                   ConstBytes);
+  void ParseStreamingProfilePacket(PacketSequenceStateGeneration*, ConstBytes);
   void ParseChromeBenchmarkMetadata(ConstBytes);
   void ParseChromeEvents(int64_t ts, ConstBytes);
   void ParseMetatraceEvent(int64_t ts, ConstBytes);
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
index 8ac2b36..b964a2c 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
@@ -234,7 +234,7 @@
   }
 
   protos::pbzero::TracePacketDefaults::Decoder* defaults =
-      state->GetTracePacketDefaults(state->current_generation());
+      state->current_generation()->GetTracePacketDefaults();
 
   int64_t timestamp;
   if (decoder.has_timestamp()) {
diff --git a/src/trace_processor/importers/proto/track_event_module.cc b/src/trace_processor/importers/proto/track_event_module.cc
index a96cf53..c731fcc 100644
--- a/src/trace_processor/importers/proto/track_event_module.cc
+++ b/src/trace_processor/importers/proto/track_event_module.cc
@@ -69,9 +69,7 @@
     parser_.ParseTrackEvent(
         ttp.timestamp, ttp.track_event_data->thread_timestamp,
         ttp.track_event_data->thread_instruction_count,
-        ttp.track_event_data->packet_sequence_state,
-        ttp.track_event_data->packet_sequence_state_generation,
-        decoder.track_event());
+        ttp.track_event_data->sequence_state, decoder.track_event());
   }
 }
 
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 37d47a0..c496eae 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -65,8 +65,7 @@
     ArgsTracker::BoundInserter* inserter) {
   auto* decoder = state.sequence_state->LookupInternedMessage<
       protos::pbzero::InternedData::kSourceLocationsFieldNumber,
-      protos::pbzero::SourceLocation>(state.sequence_generation,
-                                      field.as_uint64());
+      protos::pbzero::SourceLocation>(field.as_uint64());
   if (!decoder) {
     // Lookup failed fall back on default behaviour which will just put
     // the source_location_iid into the args table.
@@ -194,17 +193,16 @@
            context->storage->InternString("SUBRESOURCE_FILTER"),
            context->storage->InternString("UNFREEZABLE_FRAME")}} {}
 
-void TrackEventParser::ParseTrackEvent(int64_t ts,
-                                       int64_t tts,
-                                       int64_t ticount,
-                                       PacketSequenceState* sequence_state,
-                                       size_t sequence_state_generation,
-                                       ConstBytes blob) {
+void TrackEventParser::ParseTrackEvent(
+    int64_t ts,
+    int64_t tts,
+    int64_t ticount,
+    PacketSequenceStateGeneration* sequence_state,
+    ConstBytes blob) {
   using LegacyEvent = protos::pbzero::TrackEvent::LegacyEvent;
 
   protos::pbzero::TrackEventDefaults::Decoder* defaults = nullptr;
-  auto* packet_defaults_view =
-      sequence_state->GetTracePacketDefaultsView(sequence_state_generation);
+  auto* packet_defaults_view = sequence_state->GetTracePacketDefaultsView();
   if (packet_defaults_view) {
     auto* track_event_defaults_view =
         packet_defaults_view
@@ -252,8 +250,7 @@
   if (PERFETTO_LIKELY(category_iids.size() == 1 && category_strings.empty())) {
     auto* decoder = sequence_state->LookupInternedMessage<
         protos::pbzero::InternedData::kEventCategoriesFieldNumber,
-        protos::pbzero::EventCategory>(sequence_state_generation,
-                                       category_iids[0]);
+        protos::pbzero::EventCategory>(category_iids[0]);
     if (decoder)
       category_id = storage->InternString(decoder->name());
   } else if (category_iids.empty() && category_strings.size() == 1) {
@@ -266,7 +263,7 @@
     for (uint64_t iid : category_iids) {
       auto* decoder = sequence_state->LookupInternedMessage<
           protos::pbzero::InternedData::kEventCategoriesFieldNumber,
-          protos::pbzero::EventCategory>(sequence_state_generation, iid);
+          protos::pbzero::EventCategory>(iid);
       if (!decoder)
         continue;
       base::StringView name = decoder->name();
@@ -292,7 +289,7 @@
   if (PERFETTO_LIKELY(name_iid)) {
     auto* decoder = sequence_state->LookupInternedMessage<
         protos::pbzero::InternedData::kEventNamesFieldNumber,
-        protos::pbzero::EventName>(sequence_state_generation, name_iid);
+        protos::pbzero::EventName>(name_iid);
     if (decoder)
       name_id = storage->InternString(decoder->name());
   } else if (event.has_name()) {
@@ -337,11 +334,11 @@
       if (process_track_row)
         upid = storage->process_track_table().upid()[*process_track_row];
     }
-  } else if (sequence_state->pid_and_tid_valid() ||
+  } else if (sequence_state->state()->pid_and_tid_valid() ||
              (legacy_event.has_pid_override() &&
               legacy_event.has_tid_override())) {
-    uint32_t pid = static_cast<uint32_t>(sequence_state->pid());
-    uint32_t tid = static_cast<uint32_t>(sequence_state->tid());
+    uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
+    uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
     if (legacy_event.has_pid_override())
       pid = static_cast<uint32_t>(legacy_event.pid_override());
     if (legacy_event.has_tid_override())
@@ -467,26 +464,21 @@
     }
   }
 
-  auto args_callback = [this, &event, &legacy_event, &sequence_state,
-                        sequence_state_generation, ts, utid,
+  auto args_callback = [this, &event, &legacy_event, sequence_state, ts, utid,
                         legacy_tid](ArgsTracker::BoundInserter* inserter) {
     for (auto it = event.debug_annotations(); it; ++it) {
-      ParseDebugAnnotationArgs(*it, sequence_state, sequence_state_generation,
-                               inserter);
+      ParseDebugAnnotationArgs(*it, sequence_state, inserter);
     }
 
     if (event.has_task_execution()) {
-      ParseTaskExecutionArgs(event.task_execution(), sequence_state,
-                             sequence_state_generation, inserter);
+      ParseTaskExecutionArgs(event.task_execution(), sequence_state, inserter);
     }
 
     if (event.has_log_message()) {
-      ParseLogMessage(event.log_message(), sequence_state,
-                      sequence_state_generation, ts, utid, inserter);
+      ParseLogMessage(event.log_message(), sequence_state, ts, utid, inserter);
     }
     if (event.has_cc_scheduler_state()) {
-      ParseCcScheduler(event.cc_scheduler_state(), sequence_state,
-                       sequence_state_generation, inserter);
+      ParseCcScheduler(event.cc_scheduler_state(), sequence_state, inserter);
     }
     if (event.has_chrome_user_event()) {
       ParseChromeUserEvent(event.chrome_user_event(), inserter);
@@ -831,8 +823,7 @@
 
 void TrackEventParser::ParseDebugAnnotationArgs(
     ConstBytes debug_annotation,
-    PacketSequenceState* sequence_state,
-    size_t sequence_state_generation,
+    PacketSequenceStateGeneration* sequence_state,
     ArgsTracker::BoundInserter* inserter) {
   TraceStorage* storage = context_->storage.get();
 
@@ -845,8 +836,7 @@
   if (PERFETTO_LIKELY(name_iid)) {
     auto* decoder = sequence_state->LookupInternedMessage<
         protos::pbzero::InternedData::kDebugAnnotationNamesFieldNumber,
-        protos::pbzero::DebugAnnotationName>(sequence_state_generation,
-                                             name_iid);
+        protos::pbzero::DebugAnnotationName>(name_iid);
     if (!decoder)
       return;
 
@@ -942,8 +932,7 @@
 
 void TrackEventParser::ParseTaskExecutionArgs(
     ConstBytes task_execution,
-    PacketSequenceState* sequence_state,
-    size_t sequence_state_generation,
+    PacketSequenceStateGeneration* sequence_state,
     ArgsTracker::BoundInserter* inserter) {
   protos::pbzero::TaskExecution::Decoder task(task_execution.data,
                                               task_execution.size);
@@ -953,7 +942,7 @@
 
   auto* decoder = sequence_state->LookupInternedMessage<
       protos::pbzero::InternedData::kSourceLocationsFieldNumber,
-      protos::pbzero::SourceLocation>(sequence_state_generation, iid);
+      protos::pbzero::SourceLocation>(iid);
   if (!decoder)
     return;
 
@@ -974,12 +963,12 @@
                    Variadic::UnsignedInteger(line_number));
 }
 
-void TrackEventParser::ParseLogMessage(ConstBytes blob,
-                                       PacketSequenceState* sequence_state,
-                                       size_t sequence_state_generation,
-                                       int64_t ts,
-                                       base::Optional<UniqueTid> utid,
-                                       ArgsTracker::BoundInserter* inserter) {
+void TrackEventParser::ParseLogMessage(
+    ConstBytes blob,
+    PacketSequenceStateGeneration* sequence_state,
+    int64_t ts,
+    base::Optional<UniqueTid> utid,
+    ArgsTracker::BoundInserter* inserter) {
   if (!utid) {
     context_->storage->IncrementStats(stats::track_event_parser_errors);
     PERFETTO_DLOG("LogMessage without thread association");
@@ -994,8 +983,7 @@
 
   auto* decoder = sequence_state->LookupInternedMessage<
       protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
-      protos::pbzero::LogMessageBody>(sequence_state_generation,
-                                      message.body_iid());
+      protos::pbzero::LogMessageBody>(message.body_iid());
   if (!decoder)
     return;
 
@@ -1016,13 +1004,12 @@
 
 void TrackEventParser::ParseCcScheduler(
     ConstBytes cc,
-    PacketSequenceState* sequence_state,
-    size_t sequence_state_generation,
+    PacketSequenceStateGeneration* sequence_state,
     ArgsTracker::BoundInserter* outer_inserter) {
   // The 79 decides the initial amount of memory reserved in the prefix. This
   // was determined my manually counting the length of the longest column.
   constexpr size_t kCcSchedulerStateMaxColumnLength = 79;
-  ProtoToArgsTable helper(sequence_state, sequence_state_generation, context_,
+  ProtoToArgsTable helper(sequence_state, context_,
                           /* starting_prefix = */ "",
                           kCcSchedulerStateMaxColumnLength);
   auto status = helper.AddProtoFileDescriptor(
diff --git a/src/trace_processor/importers/proto/track_event_parser.h b/src/trace_processor/importers/proto/track_event_parser.h
index 8a847d2..a0a42c7 100644
--- a/src/trace_processor/importers/proto/track_event_parser.h
+++ b/src/trace_processor/importers/proto/track_event_parser.h
@@ -32,7 +32,7 @@
 
 namespace trace_processor {
 
-class PacketSequenceState;
+class PacketSequenceStateGeneration;
 class TraceProcessorContext;
 
 class TrackEventParser {
@@ -42,8 +42,7 @@
   void ParseTrackEvent(int64_t ts,
                        int64_t tts,
                        int64_t ticount,
-                       PacketSequenceState*,
-                       size_t sequence_state_generation,
+                       PacketSequenceStateGeneration*,
                        protozero::ConstBytes);
   void ParseLegacyEventAsRawEvent(
       int64_t ts,
@@ -55,26 +54,22 @@
       const protos::pbzero::TrackEvent_LegacyEvent_Decoder& legacy_event,
       SliceTracker::SetArgsCallback slice_args_callback);
   void ParseDebugAnnotationArgs(protozero::ConstBytes debug_annotation,
-                                PacketSequenceState*,
-                                size_t sequence_state_generation,
+                                PacketSequenceStateGeneration*,
                                 ArgsTracker::BoundInserter* inserter);
   void ParseNestedValueArgs(protozero::ConstBytes nested_value,
                             base::StringView flat_key,
                             base::StringView key,
                             ArgsTracker::BoundInserter* inserter);
   void ParseTaskExecutionArgs(protozero::ConstBytes task_execution,
-                              PacketSequenceState*,
-                              size_t sequence_state_generation,
+                              PacketSequenceStateGeneration*,
                               ArgsTracker::BoundInserter* inserter);
   void ParseLogMessage(protozero::ConstBytes,
-                       PacketSequenceState*,
-                       size_t sequence_state_generation,
+                       PacketSequenceStateGeneration*,
                        int64_t,
                        base::Optional<UniqueTid>,
                        ArgsTracker::BoundInserter* inserter);
   void ParseCcScheduler(protozero::ConstBytes cc_scheduler,
-                        PacketSequenceState*,
-                        size_t sequence_state_generation,
+                        PacketSequenceStateGeneration*,
                         ArgsTracker::BoundInserter* inserter);
   void ParseChromeUserEvent(protozero::ConstBytes chrome_user_event,
                             ArgsTracker::BoundInserter* inserter);
diff --git a/src/trace_processor/importers/proto/vulkan_memory_tracker.h b/src/trace_processor/importers/proto/vulkan_memory_tracker.h
index f3b8e16..e437717 100644
--- a/src/trace_processor/importers/proto/vulkan_memory_tracker.h
+++ b/src/trace_processor/importers/proto/vulkan_memory_tracker.h
@@ -40,12 +40,11 @@
   ~VulkanMemoryTracker() = default;
 
   template <int32_t FieldId>
-  StringId GetInternedString(PacketSequenceState* state,
-                             size_t generation,
+  StringId GetInternedString(PacketSequenceStateGeneration* state,
                              uint64_t iid) {
     auto* decoder =
         state->LookupInternedMessage<FieldId, protos::pbzero::InternedString>(
-            generation, iid);
+            iid);
     if (!decoder)
       return kNullStringId;
     return context_->storage->InternString(
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
index 464864c..2414f89 100644
--- a/src/trace_processor/timestamped_trace_piece.h
+++ b/src/trace_processor/timestamped_trace_piece.h
@@ -63,18 +63,15 @@
 struct TracePacketData {
   TraceBlobView packet;
 
-  // TODO(eseckler): Refactor this into a single pointer to a PSSGeneration.
-  PacketSequenceState* packet_sequence_state;
-  size_t packet_sequence_state_generation;
+  PacketSequenceStateGeneration* sequence_state;
 };
 
 struct TrackEventData : public TracePacketData {
   TrackEventData(TraceBlobView pv,
-                 PacketSequenceState* pss,
-                 size_t pss_generation,
+                 PacketSequenceStateGeneration* generation,
                  int64_t thread_ts,
                  int64_t thread_ic)
-      : TracePacketData{std::move(pv), pss, pss_generation},
+      : TracePacketData{std::move(pv), generation},
         thread_timestamp(thread_ts),
         thread_instruction_count(thread_ic) {}
 
@@ -99,9 +96,8 @@
   TimestampedTracePiece(int64_t ts,
                         uint64_t idx,
                         TraceBlobView tbv,
-                        PacketSequenceState* sequence_state)
-      : packet_data{std::move(tbv), sequence_state,
-                    sequence_state ? sequence_state->current_generation() : 0},
+                        PacketSequenceStateGeneration* sequence_state)
+      : packet_data{std::move(tbv), sequence_state},
         timestamp(ts),
         packet_idx(idx),
         type(Type::kTracePacket) {}
diff --git a/src/trace_processor/trace_sorter.h b/src/trace_processor/trace_sorter.h
index 0b8502a..b1ccf56 100644
--- a/src/trace_processor/trace_sorter.h
+++ b/src/trace_processor/trace_sorter.h
@@ -73,7 +73,8 @@
     DCHECK_ftrace_batch_cpu(kNoBatch);
     auto* queue = GetQueue(0);
     queue->Append(TimestampedTracePiece(timestamp, packet_idx_++,
-                                        std::move(packet), state));
+                                        std::move(packet),
+                                        state->current_generation()));
     MaybeExtractEvents(queue);
   }
 
@@ -135,9 +136,9 @@
                                    PacketSequenceState* state,
                                    TraceBlobView packet) {
     auto* queue = GetQueue(0);
-    std::unique_ptr<TrackEventData> data(new TrackEventData{
-        std::move(packet), state, state->current_generation(), thread_time,
-        thread_instruction_count});
+    std::unique_ptr<TrackEventData> data(
+        new TrackEventData{std::move(packet), state->current_generation(),
+                           thread_time, thread_instruction_count});
     queue->Append(
         TimestampedTracePiece(timestamp, packet_idx_++, std::move(data)));
     MaybeExtractEvents(queue);
diff --git a/src/trace_processor/trace_sorter_unittest.cc b/src/trace_processor/trace_sorter_unittest.cc
index 8f4ea0b..5db7c0f 100644
--- a/src/trace_processor/trace_sorter_unittest.cc
+++ b/src/trace_processor/trace_sorter_unittest.cc
@@ -99,14 +99,16 @@
 }
 
 TEST_F(TraceSorterTest, TestTracePacket) {
+  PacketSequenceState state(&context_);
   TraceBlobView view = test_buffer_.slice(0, 1);
   EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1000, view.data(), 1));
-  context_.sorter->PushTracePacket(1000, nullptr, std::move(view));
+  context_.sorter->PushTracePacket(1000, &state, std::move(view));
   context_.sorter->FinalizeFtraceEventBatch(1000);
   context_.sorter->ExtractEventsForced();
 }
 
 TEST_F(TraceSorterTest, Ordering) {
+  PacketSequenceState state(&context_);
   TraceBlobView view_1 = test_buffer_.slice(0, 1);
   TraceBlobView view_2 = test_buffer_.slice(0, 2);
   TraceBlobView view_3 = test_buffer_.slice(0, 3);
@@ -123,8 +125,8 @@
   context_.sorter->PushFtraceEvent(2 /*cpu*/, 1200 /*timestamp*/,
                                    std::move(view_4));
   context_.sorter->FinalizeFtraceEventBatch(2);
-  context_.sorter->PushTracePacket(1001, nullptr, std::move(view_2));
-  context_.sorter->PushTracePacket(1100, nullptr, std::move(view_3));
+  context_.sorter->PushTracePacket(1001, &state, std::move(view_2));
+  context_.sorter->PushTracePacket(1100, &state, std::move(view_3));
   context_.sorter->PushFtraceEvent(0 /*cpu*/, 1000 /*timestamp*/,
                                    std::move(view_1));
 
@@ -133,6 +135,7 @@
 }
 
 TEST_F(TraceSorterTest, SetWindowSize) {
+  PacketSequenceState state(&context_);
   TraceBlobView view_1 = test_buffer_.slice(0, 1);
   TraceBlobView view_2 = test_buffer_.slice(0, 2);
   TraceBlobView view_3 = test_buffer_.slice(0, 3);
@@ -155,8 +158,8 @@
   context_.sorter->PushFtraceEvent(2 /*cpu*/, 1200 /*timestamp*/,
                                    std::move(view_4));
   context_.sorter->FinalizeFtraceEventBatch(2);
-  context_.sorter->PushTracePacket(1001, nullptr, std::move(view_2));
-  context_.sorter->PushTracePacket(1100, nullptr, std::move(view_3));
+  context_.sorter->PushTracePacket(1001, &state, std::move(view_2));
+  context_.sorter->PushTracePacket(1100, &state, std::move(view_3));
 
   context_.sorter->PushFtraceEvent(0 /*cpu*/, 1000 /*timestamp*/,
                                    std::move(view_1));