TraceProcesor: unique_ptr<uint8_t> -> TraceBlob

A non-functional refactoring to TraceProcessor.
This CL mainly replaces all the places where we pass around
a pair of (unique_ptr<uint8_t>, size_t) with an explicit
TraceBlob.
It also simplifies a bit the TraceBlobView code, reducing
the indirection layers required to handle refcounting
(the overall refcounting principle still holds though).
The main benefit of that is removing one layer of pointer
chasing when accessing TraceBlobView.data().
The main reason of this CL is to introduce support for mmaping
the trace file on desktop (but still retain the owned-buf
semantic for wasm and platforms where mmap is not supported).

Bug: 205302474
Change-Id: I4f943bfcf0032cda910277b6d47851c73cb16316
diff --git a/Android.bp b/Android.bp
index f5484c8..780930a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1765,7 +1765,6 @@
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
-        ":perfetto_src_trace_processor_util_trace_blob_view",
         ":perfetto_src_trace_processor_util_util",
         ":perfetto_src_traced_probes_android_log_android_log",
         ":perfetto_src_traced_probes_common_common",
@@ -8232,6 +8231,7 @@
         "src/trace_processor/importers/proto/track_event_parser.cc",
         "src/trace_processor/importers/proto/track_event_tokenizer.cc",
         "src/trace_processor/importers/proto/track_event_tracker.cc",
+        "src/trace_processor/trace_blob.cc",
         "src/trace_processor/trace_processor_context.cc",
         "src/trace_processor/trace_processor_storage.cc",
         "src/trace_processor/trace_processor_storage_impl.cc",
@@ -8346,11 +8346,6 @@
     ],
 }
 
-// GN: //src/trace_processor/util:trace_blob_view
-filegroup {
-    name: "perfetto_src_trace_processor_util_trace_blob_view",
-}
-
 // GN: //src/trace_processor/util:unittests
 filegroup {
     name: "perfetto_src_trace_processor_util_unittests",
@@ -9366,7 +9361,6 @@
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
-        ":perfetto_src_trace_processor_util_trace_blob_view",
         ":perfetto_src_trace_processor_util_unittests",
         ":perfetto_src_trace_processor_util_util",
         ":perfetto_src_traced_probes_android_log_android_log",
@@ -9662,7 +9656,6 @@
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
-        ":perfetto_src_trace_processor_util_trace_blob_view",
         ":perfetto_src_trace_processor_util_util",
         "src/trace_processor/trace_processor_shell.cc",
         "src/trace_processor/util/proto_to_json.cc",
@@ -9815,7 +9808,6 @@
         ":perfetto_src_trace_processor_util_interned_message_view",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
-        ":perfetto_src_trace_processor_util_trace_blob_view",
         ":perfetto_src_trace_processor_util_util",
         ":perfetto_tools_trace_to_text_common",
         ":perfetto_tools_trace_to_text_full",
diff --git a/BUILD b/BUILD
index 6422ad6..6d764f6 100644
--- a/BUILD
+++ b/BUILD
@@ -515,6 +515,8 @@
 perfetto_filegroup(
     name = "include_perfetto_trace_processor_storage",
     srcs = [
+        "include/perfetto/trace_processor/trace_blob.h",
+        "include/perfetto/trace_processor/trace_blob_view.h",
         "include/perfetto/trace_processor/trace_processor_storage.h",
     ],
 )
@@ -1268,14 +1270,6 @@
     ],
 )
 
-# GN target: //src/trace_processor/util:trace_blob_view
-perfetto_filegroup(
-    name = "src_trace_processor_util_trace_blob_view",
-    srcs = [
-        "src/trace_processor/util/trace_blob_view.h",
-    ],
-)
-
 # GN target: //src/trace_processor/util:util
 perfetto_filegroup(
     name = "src_trace_processor_util_util",
@@ -1487,6 +1481,7 @@
         "src/trace_processor/importers/syscalls/syscall_tracker.h",
         "src/trace_processor/importers/systrace/systrace_line.h",
         "src/trace_processor/timestamped_trace_piece.h",
+        "src/trace_processor/trace_blob.cc",
         "src/trace_processor/trace_processor_context.cc",
         "src/trace_processor/trace_processor_storage.cc",
         "src/trace_processor/trace_processor_storage_impl.cc",
@@ -3697,7 +3692,6 @@
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
-        ":src_trace_processor_util_trace_blob_view",
         ":src_trace_processor_util_util",
     ],
     hdrs = [
@@ -3799,7 +3793,6 @@
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
-        ":src_trace_processor_util_trace_blob_view",
         ":src_trace_processor_util_util",
         "src/trace_processor/trace_processor_shell.cc",
         "src/trace_processor/util/proto_to_json.cc",
@@ -3982,7 +3975,6 @@
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
-        ":src_trace_processor_util_trace_blob_view",
         ":src_trace_processor_util_util",
         ":tools_trace_to_text_common",
         ":tools_trace_to_text_full",
diff --git a/include/perfetto/trace_processor/BUILD.gn b/include/perfetto/trace_processor/BUILD.gn
index ed1f3a9..cdd8b6c 100644
--- a/include/perfetto/trace_processor/BUILD.gn
+++ b/include/perfetto/trace_processor/BUILD.gn
@@ -25,7 +25,11 @@
 }
 
 source_set("storage") {
-  sources = [ "trace_processor_storage.h" ]
+  sources = [
+    "trace_blob.h",
+    "trace_blob_view.h",
+    "trace_processor_storage.h",
+  ]
   public_deps = [ ":basic_types" ]
 }
 
diff --git a/include/perfetto/trace_processor/trace_blob.h b/include/perfetto/trace_processor/trace_blob.h
new file mode 100644
index 0000000..4999926
--- /dev/null
+++ b/include/perfetto/trace_processor/trace_blob.h
@@ -0,0 +1,96 @@
+/*
+ * 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 INCLUDE_PERFETTO_TRACE_PROCESSOR_TRACE_BLOB_H_
+#define INCLUDE_PERFETTO_TRACE_PROCESSOR_TRACE_BLOB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "perfetto/base/export.h"
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// TraceBlob is a move-only buffer that owns a portion of memory containing
+// trace data (not necessarily aligned at trace packet boundaries). Think of
+// this as a std::pair<std::unique_ptr<uint8_t[]>, size_t>.
+// TraceBlob can be instantiated and moved around when it's written/altered
+// by the initial ingestion stages. In this mode, no refcounting is used
+// (i.e. refcount_ is always == 0).
+// When it comes to parsing stages, the TraceBlob can be turned into a read-only
+// object wrapping it in a TraceBlobView. Once wrapped in a TraceBlobView, the
+// TraceBlob becomes refcounted (TBV handles the inc/dec of refcount).
+// TraceBlobView allows to have multiple instances pointing at (different
+// sub-offsets of) the same TraceBlob.
+// The neat thing about TraceBlob is that it deals transparently with owned
+// memory (in the case of Allocate and TakeOwnership) and memory-mapped memory.
+// TODO(primiano): introduce full mmap support in next CL.
+class PERFETTO_EXPORT TraceBlob {
+ public:
+  static TraceBlob Allocate(size_t size);
+  static TraceBlob CopyFrom(const void*, size_t size);
+  static TraceBlob TakeOwnership(std::unique_ptr<uint8_t[]>, size_t size);
+  ~TraceBlob();
+
+  // Allow move.
+  TraceBlob(TraceBlob&& other) noexcept { *this = std::move(other); }
+  TraceBlob& operator=(TraceBlob&&) noexcept;
+
+  // Disallow copy.
+  TraceBlob(const TraceBlob&) = delete;
+  TraceBlob& operator=(const TraceBlob&) = delete;
+
+  uint8_t* data() const { return data_; }
+  size_t size() const { return size_; }
+
+ protected:
+  // "protected" here is not for inheritance. It's just to document the scope of
+  // the methods exposed to TraceBlobView.
+  friend class TraceBlobView;
+
+  // Refcount inc/dec is used only by TraceBlobView.
+  void IncRefcount() {
+    PERFETTO_DCHECK(refcount_ >= 0);
+    ++refcount_;
+  }
+
+  void DecRefcountAndDeleteIfZero() {
+    PERFETTO_DCHECK(refcount_ > 0);
+    if (--refcount_ == 0)
+      delete this;
+  }
+
+ private:
+  enum class Ownership { kNull = 0, kHeapBuf };
+
+  TraceBlob(Ownership ownership, uint8_t* data, size_t size)
+      : ownership_(ownership), data_(data), size_(size) {}
+
+  int refcount_ = 0;
+  Ownership ownership_ = Ownership::kNull;
+  uint8_t* data_ = nullptr;
+  size_t size_ = 0;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACE_PROCESSOR_TRACE_BLOB_H_
diff --git a/include/perfetto/trace_processor/trace_blob_view.h b/include/perfetto/trace_processor/trace_blob_view.h
new file mode 100644
index 0000000..de09dee
--- /dev/null
+++ b/include/perfetto/trace_processor/trace_blob_view.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2018 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 INCLUDE_PERFETTO_TRACE_PROCESSOR_TRACE_BLOB_VIEW_H_
+#define INCLUDE_PERFETTO_TRACE_PROCESSOR_TRACE_BLOB_VIEW_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/trace_processor/trace_blob.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// A read-only view of a TraceBlob.
+// This class is an equivalent of std::string_view for trace binary data, with
+// a twist: it supports turning a TraceBlob into a refcounted reference. In this
+// case the TraceBlobView acts directly as a shared_ptr, without requiring extra
+// layers of indirection.
+// The underlying TraceBlob will be freed once all the TraceBlobViews that refer
+// to the same buffer have passed through the pipeline and been parsed.
+// The overall idea is that a TraceBlob is passed around until it's written.
+// When writing is done it transforms into a shared refcounted object which is
+// held onto by one or more read-only TraceBlobView instances.
+//
+// In summary:
+//  - TraceBlob: writable, move-only, single-instance.
+//  - TraceBlobView: readable, copyable, multiple-instances can hold onto
+//                   (different sub-slices of) the same refcounted TraceBlob.
+class TraceBlobView {
+ public:
+  // Takes ownership of the passed |blob|.
+  static constexpr size_t kWholeBlob = std::numeric_limits<size_t>::max();
+  explicit TraceBlobView(TraceBlob blob,
+                         size_t offset = 0,
+                         size_t length = kWholeBlob) {
+    PERFETTO_DCHECK(offset <= std::numeric_limits<uint32_t>::max());
+    data_ = blob.data() + offset;
+    if (length == kWholeBlob) {
+      length_ = static_cast<uint32_t>(blob.size() - offset);
+    } else {
+      PERFETTO_DCHECK(length <= std::numeric_limits<uint32_t>::max());
+      PERFETTO_DCHECK(offset + length_ <= blob.size());
+      length_ = static_cast<uint32_t>(length);
+    }
+    blob_refcounted_ = new TraceBlob(std::move(blob));
+    blob_refcounted_->IncRefcount();
+  }
+
+  // Trivial empty ctor.
+  TraceBlobView() : data_(nullptr), length_(0), blob_refcounted_(nullptr) {}
+
+  ~TraceBlobView() {
+    if (blob_refcounted_)
+      blob_refcounted_->DecRefcountAndDeleteIfZero();
+  }
+
+  // Allow std::move().
+  TraceBlobView(TraceBlobView&& other) noexcept { *this = std::move(other); }
+
+  TraceBlobView& operator=(TraceBlobView&& other) noexcept {
+    // TraceBlobView moving is a hotpath. Assume we never x = std::move(x).
+    PERFETTO_DCHECK(this != &other);
+    data_ = other.data_;
+    length_ = other.length_;
+    blob_refcounted_ = other.blob_refcounted_;
+    other.blob_refcounted_ = nullptr;
+    return *this;
+  }
+
+  // Disable copy operators. Use x.Copy() to get a copy.
+  TraceBlobView(const TraceBlobView&) = delete;
+  TraceBlobView& operator=(const TraceBlobView&) = delete;
+
+  // [data, data+length] must be <= the current TraceBlobView.
+  TraceBlobView slice(const uint8_t* data, size_t length) const {
+    PERFETTO_DCHECK(data >= data_);
+    PERFETTO_DCHECK(data + length <= data_ + length_);
+    return TraceBlobView(data, static_cast<uint32_t>(length), blob_refcounted_);
+  }
+
+  // Like slice() but takes an offset rather than a pointer as 1st argument.
+  TraceBlobView slice_off(size_t off, size_t length) const {
+    PERFETTO_DCHECK(off + length <= length_);
+    return TraceBlobView(data_ + off, static_cast<uint32_t>(length),
+                         blob_refcounted_);
+  }
+
+  TraceBlobView copy() const { return slice(data_, length_); }
+
+  bool operator==(const TraceBlobView& rhs) const {
+    return (data_ == rhs.data_) && (length_ == rhs.length_) &&
+           (blob_refcounted_ == rhs.blob_refcounted_);
+  }
+  bool operator!=(const TraceBlobView& rhs) const { return !(*this == rhs); }
+
+  const uint8_t* data() const { return data_; }
+  // TODO(primiano): normalize length() vs size() usage.
+  size_t length() const { return length_; }
+  size_t size() const { return length_; }
+
+ private:
+  TraceBlobView(const uint8_t* data, uint32_t length, TraceBlob* blob)
+      : data_(data), length_(length), blob_refcounted_(blob) {
+    blob_refcounted_->IncRefcount();
+  }
+
+  const uint8_t* data_ = nullptr;
+  uint32_t length_ = 0;
+  TraceBlob* blob_refcounted_ = nullptr;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACE_PROCESSOR_TRACE_BLOB_VIEW_H_
diff --git a/include/perfetto/trace_processor/trace_processor_storage.h b/include/perfetto/trace_processor/trace_processor_storage.h
index 9d27edb..82a55d0 100644
--- a/include/perfetto/trace_processor/trace_processor_storage.h
+++ b/include/perfetto/trace_processor/trace_processor_storage.h
@@ -24,6 +24,8 @@
 #include "perfetto/base/export.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/status.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -43,7 +45,11 @@
   // status if some unrecoverable error happened. If this happens, the
   // TraceProcessor will ignore the following Parse() requests, drop data on the
   // floor and return errors forever.
-  virtual util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) = 0;
+  virtual util::Status Parse(TraceBlobView) = 0;
+
+  // Shorthand for Parse(TraceBlobView(TraceBlob(TakeOwnership(buf, size))).
+  // For compatibility with older API clients.
+  util::Status Parse(std::unique_ptr<uint8_t[]> buf, size_t size);
 
   // When parsing a bounded file (as opposite to streaming from a device) this
   // function should be called when the last chunk of the file has been passed
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index c3e9935..d47e65a 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -132,6 +132,7 @@
     "importers/syscalls/syscall_tracker.h",
     "importers/systrace/systrace_line.h",
     "timestamped_trace_piece.h",
+    "trace_blob.cc",
     "trace_processor_context.cc",
     "trace_processor_storage.cc",
     "trace_processor_storage_impl.cc",
@@ -174,7 +175,6 @@
     "../../protos/perfetto/trace/sys_stats:zero",
     "../../protos/perfetto/trace/system_info:zero",
     "../../protos/perfetto/trace/track_event:zero",
-    "util:trace_blob_view",
   ]
 
   # json_utils optionally depends on jsoncpp.
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index dc2cf54..b983d7b 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -62,8 +62,7 @@
 
 ForwardingTraceParser::~ForwardingTraceParser() {}
 
-util::Status ForwardingTraceParser::Parse(std::unique_ptr<uint8_t[]> data,
-                                          size_t size) {
+util::Status ForwardingTraceParser::Parse(TraceBlobView blob) {
   // If this is the first Parse() call, guess the trace type and create the
   // appropriate parser.
   if (!reader_) {
@@ -71,7 +70,7 @@
     {
       auto scoped_trace = context_->storage->TraceExecutionTimeIntoStats(
           stats::guess_trace_type_duration_ns);
-      trace_type = GuessTraceType(data.get(), size);
+      trace_type = GuessTraceType(blob.data(), blob.size());
     }
     switch (trace_type) {
       case kJsonTraceType: {
@@ -148,7 +147,7 @@
     }
   }
 
-  return reader_->Parse(std::move(data), size);
+  return reader_->Parse(std::move(blob));
 }
 
 void ForwardingTraceParser::NotifyEndOfFile() {
diff --git a/src/trace_processor/forwarding_trace_parser.h b/src/trace_processor/forwarding_trace_parser.h
index 0da568a..a5164cf 100644
--- a/src/trace_processor/forwarding_trace_parser.h
+++ b/src/trace_processor/forwarding_trace_parser.h
@@ -43,7 +43,7 @@
   ~ForwardingTraceParser() override;
 
   // ChunkedTraceReader implementation
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
  private:
diff --git a/src/trace_processor/importers/common/BUILD.gn b/src/trace_processor/importers/common/BUILD.gn
index 1ffb40a..aa7f57c 100644
--- a/src/trace_processor/importers/common/BUILD.gn
+++ b/src/trace_processor/importers/common/BUILD.gn
@@ -43,6 +43,7 @@
   ]
   deps = [
     "../../../../gn:default_deps",
+    "../../../../include/perfetto/trace_processor",
     "../../../../include/perfetto/trace_processor:basic_types",
     "../../../../protos/perfetto/common:zero",
     "../../../../protos/perfetto/trace:zero",
diff --git a/src/trace_processor/importers/common/chunked_trace_reader.h b/src/trace_processor/importers/common/chunked_trace_reader.h
index dc90344..a86ae13 100644
--- a/src/trace_processor/importers/common/chunked_trace_reader.h
+++ b/src/trace_processor/importers/common/chunked_trace_reader.h
@@ -28,6 +28,8 @@
 namespace perfetto {
 namespace trace_processor {
 
+class TraceBlobView;
+
 // Base interface for first stage of parsing pipeline
 // (JsonTraceParser, ProtoTraceReader).
 class ChunkedTraceReader {
@@ -38,7 +40,7 @@
   // caller to match line/protos boundaries. The parser class has to deal with
   // intermediate buffering lines/protos that span across different chunks.
   // The buffer size is guaranteed to be > 0.
-  virtual util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) = 0;
+  virtual util::Status Parse(TraceBlobView) = 0;
 
   // Called after the last Parse() call.
   virtual void NotifyEndOfFile() = 0;
diff --git a/src/trace_processor/importers/ftrace/ftrace_module_impl.cc b/src/trace_processor/importers/ftrace/ftrace_module_impl.cc
index bcf6e6c..d7579ec 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module_impl.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_module_impl.cc
@@ -16,10 +16,10 @@
 
 #include "src/trace_processor/importers/ftrace/ftrace_module_impl.h"
 #include "perfetto/base/build_config.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/ftrace/ftrace_parser.h"
 #include "src/trace_processor/importers/ftrace/ftrace_tokenizer.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -42,9 +42,8 @@
     uint32_t field_id) {
   if (field_id == TracePacket::kFtraceEventsFieldNumber) {
     auto ftrace_field = decoder.ftrace_events();
-    const size_t fld_off = packet->offset_of(ftrace_field.data);
     return tokenizer_.TokenizeFtraceBundle(
-        packet->slice(fld_off, ftrace_field.size), seq_state,
+        packet->slice(ftrace_field.data, ftrace_field.size), seq_state,
         decoder.trusted_packet_sequence_id());
   }
   return ModuleResult::Ignored();
diff --git a/src/trace_processor/importers/ftrace/ftrace_module_impl.h b/src/trace_processor/importers/ftrace/ftrace_module_impl.h
index 488c2eb..da6ffd9 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module_impl.h
+++ b/src/trace_processor/importers/ftrace/ftrace_module_impl.h
@@ -23,13 +23,14 @@
 #include "src/trace_processor/importers/ftrace/ftrace_tokenizer.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
 
+class TraceBlobView;
+
 class FtraceModuleImpl : public FtraceModule {
  public:
   FtraceModuleImpl(TraceProcessorContext* context);
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 5b5f64b..bf798a3 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -24,7 +24,6 @@
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/types/trace_processor_context.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 2e79420..3ca59f7 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -96,8 +96,8 @@
   }
 
   for (auto it = decoder.event(); it; ++it) {
-    size_t off = bundle.offset_of(it->data());
-    TokenizeFtraceEvent(cpu, clock_id, bundle.slice(off, it->size()), state);
+    TokenizeFtraceEvent(cpu, clock_id, bundle.slice(it->data(), it->size()),
+                        state);
   }
   return base::OkStatus();
 }
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
index 37048ea..f92f555 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
@@ -17,10 +17,10 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
 
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_record.h b/src/trace_processor/importers/fuchsia/fuchsia_record.h
index a5063ae..f90d039 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_record.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_record.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FUCHSIA_FUCHSIA_RECORD_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FUCHSIA_FUCHSIA_RECORD_H_
 
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 #include <vector>
 
@@ -32,7 +32,7 @@
 // the binary record after arbitrary reordering.
 class FuchsiaRecord {
  public:
-  FuchsiaRecord(TraceBlobView record_view)
+  explicit FuchsiaRecord(TraceBlobView record_view)
       : record_view_(std::move(record_view)) {}
 
   struct StringTableEntry {
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
index a142916..d215b19 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
@@ -21,6 +21,7 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/trace_blob.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
@@ -70,8 +71,9 @@
 
 FuchsiaTraceTokenizer::~FuchsiaTraceTokenizer() = default;
 
-util::Status FuchsiaTraceTokenizer::Parse(std::unique_ptr<uint8_t[]> data,
-                                          size_t size) {
+util::Status FuchsiaTraceTokenizer::Parse(TraceBlobView blob) {
+  size_t size = blob.size();
+
   // The relevant internal state is |leftover_bytes_|. Each call to Parse should
   // maintain the following properties, unless a fatal error occurs in which
   // case it should return false and no assumptions should be made about the
@@ -95,8 +97,8 @@
   if (leftover_bytes_.size() + size < 8) {
     // Even with the new bytes, we can't even read the header of the next
     // record, so just add the new bytes to |leftover_bytes_| and return.
-    leftover_bytes_.insert(leftover_bytes_.end(), data.get() + byte_offset,
-                           data.get() + size);
+    leftover_bytes_.insert(leftover_bytes_.end(), blob.data() + byte_offset,
+                           blob.data() + size);
     return util::OkStatus();
   }
   if (!leftover_bytes_.empty()) {
@@ -106,8 +108,8 @@
       // Copy bytes into |leftover_bytes_| so that the whole header is present,
       // and update |byte_offset| and |size| accordingly.
       size_t needed_bytes = 8 - leftover_bytes_.size();
-      leftover_bytes_.insert(leftover_bytes_.end(), data.get() + byte_offset,
-                             data.get() + needed_bytes);
+      leftover_bytes_.insert(leftover_bytes_.end(), blob.data() + byte_offset,
+                             blob.data() + needed_bytes);
       byte_offset += needed_bytes;
       size -= needed_bytes;
     }
@@ -128,25 +130,24 @@
     if (missing_bytes <= size) {
       // We have enough bytes to complete the partial record. Create a new
       // buffer for that record.
-      std::unique_ptr<uint8_t[]> buf(new uint8_t[record_len_bytes]);
-      memcpy(&buf[0], leftover_bytes_.data(), leftover_bytes_.size());
-      memcpy(&buf[leftover_bytes_.size()], &data[byte_offset], missing_bytes);
+      TraceBlob buf = TraceBlob::Allocate(record_len_bytes);
+      memcpy(buf.data(), leftover_bytes_.data(), leftover_bytes_.size());
+      memcpy(buf.data() + leftover_bytes_.size(), blob.data() + byte_offset,
+             missing_bytes);
       byte_offset += missing_bytes;
       size -= missing_bytes;
       leftover_bytes_.clear();
-
-      TraceBlobView leftover_record(std::move(buf), 0, record_len_bytes);
-      ParseRecord(std::move(leftover_record));
+      ParseRecord(TraceBlobView(std::move(buf)));
     } else {
       // There are not enough bytes for the full record. Add all the bytes we
       // have to leftover_bytes_ and wait for more.
-      leftover_bytes_.insert(leftover_bytes_.end(), data.get() + byte_offset,
-                             data.get() + byte_offset + size);
+      leftover_bytes_.insert(leftover_bytes_.end(), blob.data() + byte_offset,
+                             blob.data() + byte_offset + size);
       return util::OkStatus();
     }
   }
 
-  TraceBlobView full_view(std::move(data), byte_offset, size);
+  TraceBlobView full_view = blob.slice_off(byte_offset, size);
 
   // |record_offset| is a number of bytes past |byte_offset| where the record
   // under consideration starts. As a result, it must always be in the range [0,
@@ -164,8 +165,7 @@
     if (record_offset + record_len_bytes > size)
       break;
 
-    TraceBlobView record =
-        full_view.slice(byte_offset + record_offset, record_len_bytes);
+    TraceBlobView record = full_view.slice_off(record_offset, record_len_bytes);
     ParseRecord(std::move(record));
 
     record_offset += record_len_bytes;
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
index 57c4718..258a922 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
@@ -20,7 +20,6 @@
 #include "src/trace_processor/importers/common/chunked_trace_reader.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -35,7 +34,7 @@
   ~FuchsiaTraceTokenizer() override;
 
   // ChunkedTraceReader implementation
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
  private:
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
index 73d4f0d..518be4b 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
@@ -22,8 +22,8 @@
 #include <functional>
 
 #include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/gzip/gzip_trace_parser.cc b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
index cf8416e..e8e9494 100644
--- a/src/trace_processor/importers/gzip/gzip_trace_parser.cc
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
@@ -21,6 +21,7 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/forwarding_trace_parser.h"
 #include "src/trace_processor/util/gzip_utils.h"
 #include "src/trace_processor/util/status_macros.h"
@@ -42,9 +43,8 @@
 
 GzipTraceParser::~GzipTraceParser() = default;
 
-util::Status GzipTraceParser::Parse(std::unique_ptr<uint8_t[]> data,
-                                    size_t size) {
-  return ParseUnowned(data.get(), size);
+util::Status GzipTraceParser::Parse(TraceBlobView blob) {
+  return ParseUnowned(blob.data(), blob.size());
 }
 
 util::Status GzipTraceParser::ParseUnowned(const uint8_t* data, size_t size) {
@@ -97,8 +97,11 @@
     }
     bytes_written_ += result.bytes_written;
 
-    if (bytes_written_ == kUncompressedBufferSize || ret == ResultCode::kEof)
-      RETURN_IF_ERROR(inner_->Parse(std::move(buffer_), bytes_written_));
+    if (bytes_written_ == kUncompressedBufferSize || ret == ResultCode::kEof) {
+      TraceBlob blob =
+          TraceBlob::TakeOwnership(std::move(buffer_), bytes_written_);
+      RETURN_IF_ERROR(inner_->Parse(TraceBlobView(std::move(blob))));
+    }
   }
   return util::OkStatus();
 }
diff --git a/src/trace_processor/importers/gzip/gzip_trace_parser.h b/src/trace_processor/importers/gzip/gzip_trace_parser.h
index 051284a..6689939 100644
--- a/src/trace_processor/importers/gzip/gzip_trace_parser.h
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.h
@@ -32,7 +32,7 @@
   ~GzipTraceParser() override;
 
   // ChunkedTraceReader implementation
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
   util::Status ParseUnowned(const uint8_t*, size_t);
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index 280725e..bcfe02e 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -21,12 +21,12 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/string_utils.h"
 
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/json/json_tracker.h"
 #include "src/trace_processor/importers/json/json_utils.h"
 #include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/trace_sorter.h"
 #include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -416,11 +416,10 @@
     : context_(ctx) {}
 JsonTraceTokenizer::~JsonTraceTokenizer() = default;
 
-util::Status JsonTraceTokenizer::Parse(std::unique_ptr<uint8_t[]> data,
-                                       size_t size) {
+util::Status JsonTraceTokenizer::Parse(TraceBlobView blob) {
   PERFETTO_DCHECK(json::IsJsonSupported());
 
-  buffer_.insert(buffer_.end(), data.get(), data.get() + size);
+  buffer_.insert(buffer_.end(), blob.data(), blob.data() + blob.size());
   const char* buf = buffer_.data();
   const char* next = buf;
   const char* end = buf + buffer_.size();
@@ -432,7 +431,7 @@
     // two passes on the file so for now we only handle displayTimeUnit
     // correctly if it is at the beginning of the file.
     base::Optional<json::TimeUnit> timeunit =
-        MaybeParseDisplayTimeUnit(base::StringView(buf, size));
+        MaybeParseDisplayTimeUnit(base::StringView(buf, blob.size()));
     if (timeunit) {
       JsonTracker::GetOrCreate(context_)->SetTimeUnit(*timeunit);
     }
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.h b/src/trace_processor/importers/json/json_trace_tokenizer.h
index 4daf661..d25954d 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.h
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.h
@@ -106,7 +106,7 @@
   ~JsonTraceTokenizer() override;
 
   // ChunkedTraceReader implementation.
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
  private:
diff --git a/src/trace_processor/importers/ninja/ninja_log_parser.cc b/src/trace_processor/importers/ninja/ninja_log_parser.cc
index 1e90419..bf186bb 100644
--- a/src/trace_processor/importers/ninja/ninja_log_parser.cc
+++ b/src/trace_processor/importers/ninja/ninja_log_parser.cc
@@ -32,13 +32,13 @@
 NinjaLogParser::NinjaLogParser(TraceProcessorContext* ctx) : ctx_(ctx) {}
 NinjaLogParser::~NinjaLogParser() = default;
 
-util::Status NinjaLogParser::Parse(std::unique_ptr<uint8_t[]> buf, size_t len) {
+util::Status NinjaLogParser::Parse(TraceBlobView blob) {
   // A trace is read in chunks of arbitrary size (for http fetch() pipeliniing),
   // not necessarily aligned on a line boundary.
   // Here we push everything into a vector and, on each call, consume only
   // the leading part until the last \n, keeping the rest for the next call.
-  const char* src = reinterpret_cast<const char*>(&buf[0]);
-  log_.insert(log_.end(), src, src + len);
+  const char* src = reinterpret_cast<const char*>(blob.data());
+  log_.insert(log_.end(), src, src + blob.size());
 
   // Find the last \n.
   size_t valid_size = log_.size();
diff --git a/src/trace_processor/importers/ninja/ninja_log_parser.h b/src/trace_processor/importers/ninja/ninja_log_parser.h
index f010e73..39a0806 100644
--- a/src/trace_processor/importers/ninja/ninja_log_parser.h
+++ b/src/trace_processor/importers/ninja/ninja_log_parser.h
@@ -50,7 +50,7 @@
   NinjaLogParser& operator=(const NinjaLogParser&) = delete;
 
   // ChunkedTraceReader implementation
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
  private:
diff --git a/src/trace_processor/importers/proto/android_probes_module.cc b/src/trace_processor/importers/proto/android_probes_module.cc
index 6047419..e03aa18 100644
--- a/src/trace_processor/importers/proto/android_probes_module.cc
+++ b/src/trace_processor/importers/proto/android_probes_module.cc
@@ -148,10 +148,9 @@
     energy->set_timestamp_ms(static_cast<uint64_t>(actual_ts / 1000000));
 
     std::vector<uint8_t> vec = data_packet.SerializeAsArray();
-    std::unique_ptr<uint8_t[]> buffer(new uint8_t[vec.size()]);
-    memcpy(buffer.get(), vec.data(), vec.size());
-    context_->sorter->PushTracePacket(
-        actual_ts, state, TraceBlobView(std::move(buffer), 0, vec.size()));
+    TraceBlob blob = TraceBlob::CopyFrom(vec.data(), vec.size());
+    context_->sorter->PushTracePacket(actual_ts, state,
+                                      TraceBlobView(std::move(blob)));
   }
 
   return ModuleResult::Handled();
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index a999f9a..ce88f72 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -24,11 +24,11 @@
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/protozero/proto_decoder.h"
+#include "perfetto/trace_processor/trace_blob_view.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"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
diff --git a/src/trace_processor/importers/proto/proto_importer_module.h b/src/trace_processor/importers/proto/proto_importer_module.h
index 9a1dd5b..c56f034 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.h
+++ b/src/trace_processor/importers/proto/proto_importer_module.h
@@ -19,7 +19,6 @@
 
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 namespace perfetto {
 
@@ -34,6 +33,7 @@
 
 class PacketSequenceState;
 struct TimestampedTracePiece;
+class TraceBlobView;
 class TraceProcessorContext;
 
 // This file contains a base class for ProtoTraceReader/Parser modules.
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.h b/src/trace_processor/importers/proto/proto_trace_parser.h
index 48ec82e..0061d49 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.h
+++ b/src/trace_processor/importers/proto/proto_trace_parser.h
@@ -25,10 +25,10 @@
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/field.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 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 60c1606..8d73050 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -19,6 +19,7 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/trace_processor/trace_blob.h"
 #include "src/trace_processor/importers/additional_modules.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
@@ -270,8 +271,8 @@
     std::unique_ptr<uint8_t[]> raw_trace(new uint8_t[trace_bytes.size()]);
     memcpy(raw_trace.get(), trace_bytes.data(), trace_bytes.size());
     context_.chunk_reader.reset(new ProtoTraceReader(&context_));
-    auto status =
-        context_.chunk_reader->Parse(std::move(raw_trace), trace_bytes.size());
+    auto status = context_.chunk_reader->Parse(TraceBlobView(
+        TraceBlob::TakeOwnership(std::move(raw_trace), trace_bytes.size())));
 
     ResetTraceBuffers();
     return status;
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index e8ca900..e6dd7c6 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -55,11 +55,10 @@
     : context_(ctx) {}
 ProtoTraceReader::~ProtoTraceReader() = default;
 
-util::Status ProtoTraceReader::Parse(std::unique_ptr<uint8_t[]> owned_buf,
-                                     size_t size) {
-  return tokenizer_.Tokenize(
-      std::move(owned_buf), size,
-      [this](TraceBlobView packet) { return ParsePacket(std::move(packet)); });
+util::Status ProtoTraceReader::Parse(TraceBlobView blob) {
+  return tokenizer_.Tokenize(std::move(blob), [this](TraceBlobView packet) {
+    return ParsePacket(std::move(packet));
+  });
 }
 
 util::Status ProtoTraceReader::ParseExtensionDescriptor(ConstBytes descriptor) {
@@ -100,14 +99,12 @@
   // the timestamp, since the defaults could affect them.
   if (decoder.has_trace_packet_defaults()) {
     auto field = decoder.trace_packet_defaults();
-    const size_t offset = packet.offset_of(field.data);
-    ParseTracePacketDefaults(decoder, packet.slice(offset, field.size));
+    ParseTracePacketDefaults(decoder, packet.slice(field.data, field.size));
   }
 
   if (decoder.has_interned_data()) {
     auto field = decoder.interned_data();
-    const size_t offset = packet.offset_of(field.data);
-    ParseInternedData(decoder, packet.slice(offset, field.size));
+    ParseInternedData(decoder, packet.slice(field.data, field.size));
   }
 
   if (decoder.has_clock_snapshot()) {
@@ -310,8 +307,7 @@
   for (protozero::Field f = decoder.ReadField(); f.valid();
        f = decoder.ReadField()) {
     auto bytes = f.as_bytes();
-    auto offset = interned_data.offset_of(bytes.data);
-    state->InternMessage(f.id(), interned_data.slice(offset, bytes.size));
+    state->InternMessage(f.id(), interned_data.slice(bytes.data, bytes.size));
   }
 }
 
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.h b/src/trace_processor/importers/proto/proto_trace_reader.h
index c144c29..ed3e43e 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.h
+++ b/src/trace_processor/importers/proto/proto_trace_reader.h
@@ -24,7 +24,6 @@
 #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/util/trace_blob_view.h"
 
 namespace protozero {
 struct ConstBytes;
@@ -58,7 +57,7 @@
   ~ProtoTraceReader() override;
 
   // ChunkedTraceReader implementation.
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t size) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
  private:
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
index 9bf23df..1a96191 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
@@ -15,6 +15,7 @@
  */
 
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
+#include "perfetto/trace_processor/trace_blob.h"
 
 #include "perfetto/ext/base/utils.h"
 
@@ -27,7 +28,7 @@
                                              TraceBlobView* output) {
   PERFETTO_DCHECK(util::IsGzipSupported());
 
-  uint8_t out[4096];
+  uint8_t zbuf[4096];
 
   std::vector<uint8_t> data;
   data.reserve(input.length());
@@ -38,7 +39,7 @@
 
   using ResultCode = util::GzipDecompressor::ResultCode;
   for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
-    auto res = decompressor_.Decompress(out, base::ArraySize(out));
+    auto res = decompressor_.Decompress(zbuf, base::ArraySize(zbuf));
     ret = res.ret;
     if (ret == ResultCode::kError || ret == ResultCode::kNoProgress ||
         ret == ResultCode::kNeedsMoreInput) {
@@ -46,12 +47,11 @@
                              static_cast<int>(ret));
     }
 
-    data.insert(data.end(), out, out + res.bytes_written);
+    data.insert(data.end(), zbuf, zbuf + res.bytes_written);
   }
 
-  std::unique_ptr<uint8_t[]> out_data(new uint8_t[data.size()]);
-  memcpy(out_data.get(), data.data(), data.size());
-  *output = TraceBlobView(std::move(out_data), 0, data.size());
+  TraceBlob out_blob = TraceBlob::CopyFrom(data.data(), data.size());
+  *output = TraceBlobView(std::move(out_blob));
   return util::OkStatus();
 }
 
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.h b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
index 350cb34..1b51e56 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.h
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
@@ -21,9 +21,10 @@
 
 #include "perfetto/protozero/proto_utils.h"
 #include "perfetto/trace_processor/status.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/util/gzip_utils.h"
 #include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -38,10 +39,9 @@
   ProtoTraceTokenizer();
 
   template <typename Callback = util::Status(TraceBlobView)>
-  util::Status Tokenize(std::unique_ptr<uint8_t[]> owned_buf,
-                        size_t size,
-                        Callback callback) {
-    uint8_t* data = &owned_buf[0];
+  util::Status Tokenize(TraceBlobView blob, Callback callback) {
+    const uint8_t* data = blob.data();
+    size_t size = blob.size();
     if (!partial_buf_.empty()) {
       // It takes ~5 bytes for a proto preamble + the varint size.
       const size_t kHeaderBytes = 5;
@@ -86,24 +86,23 @@
         //    that we might have consumed already a few bytes form |data|
         //    earlier in this function, hence we need to keep |off| into
         //    account).
-        std::unique_ptr<uint8_t[]> buf(new uint8_t[size_incl_header]);
-        memcpy(&buf[0], partial_buf_.data(), partial_buf_.size());
+        TraceBlob glued = TraceBlob::Allocate(size_incl_header);
+        memcpy(glued.data(), partial_buf_.data(), partial_buf_.size());
         // |size_missing| is the number of bytes for the rest of the TracePacket
         // in |data|.
         size_t size_missing = size_incl_header - partial_buf_.size();
-        memcpy(&buf[partial_buf_.size()], &data[0], size_missing);
+        memcpy(glued.data() + partial_buf_.size(), &data[0], size_missing);
         data += size_missing;
         size -= size_missing;
         partial_buf_.clear();
-        uint8_t* buf_start = &buf[0];  // Note that buf is std::moved below.
-        RETURN_IF_ERROR(ParseInternal(std::move(buf), buf_start,
-                                      size_incl_header, callback));
+        RETURN_IF_ERROR(
+            ParseInternal(TraceBlobView(std::move(glued)), callback));
       } else {
         partial_buf_.insert(partial_buf_.end(), data, &data[size]);
         return util::OkStatus();
       }
     }
-    return ParseInternal(std::move(owned_buf), data, size, callback);
+    return ParseInternal(blob.slice(data, size), callback);
   }
 
  private:
@@ -112,28 +111,20 @@
           protos::pbzero::Trace::kPacketFieldNumber);
 
   template <typename Callback = util::Status(TraceBlobView)>
-  util::Status ParseInternal(std::unique_ptr<uint8_t[]> owned_buf,
-                             uint8_t* data,
-                             size_t size,
-                             Callback callback) {
-    PERFETTO_DCHECK(data >= &owned_buf[0]);
-    const uint8_t* start = &owned_buf[0];
-    const size_t data_off = static_cast<size_t>(data - start);
-    TraceBlobView whole_buf(std::move(owned_buf), data_off, size);
-
-    protos::pbzero::Trace::Decoder decoder(data, size);
+  util::Status ParseInternal(TraceBlobView whole_buf, Callback callback) {
+    const uint8_t* const start = whole_buf.data();
+    protos::pbzero::Trace::Decoder decoder(whole_buf.data(), whole_buf.size());
     for (auto it = decoder.packet(); it; ++it) {
       protozero::ConstBytes packet = *it;
-      size_t field_offset = whole_buf.offset_of(packet.data);
-      TraceBlobView sliced = whole_buf.slice(field_offset, packet.size);
+      TraceBlobView sliced = whole_buf.slice(packet.data, packet.size);
       RETURN_IF_ERROR(ParsePacket(std::move(sliced), callback));
     }
 
     const size_t bytes_left = decoder.bytes_left();
     if (bytes_left > 0) {
       PERFETTO_DCHECK(partial_buf_.empty());
-      partial_buf_.insert(partial_buf_.end(), &data[decoder.read_offset()],
-                          &data[decoder.read_offset() + bytes_left]);
+      partial_buf_.insert(partial_buf_.end(), &start[decoder.read_offset()],
+                          &start[decoder.read_offset() + bytes_left]);
     }
     return util::OkStatus();
   }
@@ -149,9 +140,8 @@
       }
 
       protozero::ConstBytes field = decoder.compressed_packets();
-      const size_t field_off = packet.offset_of(field.data);
-      TraceBlobView compressed_packets = packet.slice(field_off, field.size);
-      TraceBlobView packets(nullptr, 0, 0);
+      TraceBlobView compressed_packets = packet.slice(field.data, field.size);
+      TraceBlobView packets;
 
       RETURN_IF_ERROR(Decompress(std::move(compressed_packets), &packets));
 
@@ -159,18 +149,18 @@
       const uint8_t* end = packets.data() + packets.length();
       const uint8_t* ptr = start;
       while ((end - ptr) > 2) {
-        const uint8_t* packet_start = ptr;
+        const uint8_t* packet_outer = ptr;
         if (PERFETTO_UNLIKELY(*ptr != kTracePacketTag))
           return util::ErrStatus("Expected TracePacket tag");
         uint64_t packet_size = 0;
         ptr = protozero::proto_utils::ParseVarInt(++ptr, end, &packet_size);
-        size_t packet_offset = static_cast<size_t>(ptr - start);
+        const uint8_t* packet_start = ptr;
         ptr += packet_size;
-        if (PERFETTO_UNLIKELY((ptr - packet_start) < 2 || ptr > end))
+        if (PERFETTO_UNLIKELY((ptr - packet_outer) < 2 || ptr > end))
           return util::ErrStatus("Invalid packet size");
 
         TraceBlobView sliced =
-            packets.slice(packet_offset, static_cast<size_t>(packet_size));
+            packets.slice(packet_start, static_cast<size_t>(packet_size));
         RETURN_IF_ERROR(ParsePacket(std::move(sliced), callback));
       }
       return util::OkStatus();
diff --git a/src/trace_processor/importers/proto/track_event_tokenizer.cc b/src/trace_processor/importers/proto/track_event_tokenizer.cc
index 056d342..2601ff0 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.cc
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.cc
@@ -17,6 +17,7 @@
 #include "src/trace_processor/importers/proto/track_event_tokenizer.h"
 
 #include "perfetto/base/logging.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
@@ -26,7 +27,6 @@
 #include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 #include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.cc b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
index 2b797db..445dda1 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
@@ -64,11 +64,11 @@
     : line_parser_(ctx), ctx_(ctx) {}
 SystraceTraceParser::~SystraceTraceParser() = default;
 
-util::Status SystraceTraceParser::Parse(std::unique_ptr<uint8_t[]> owned_buf,
-                                        size_t size) {
+util::Status SystraceTraceParser::Parse(TraceBlobView blob) {
   if (state_ == ParseState::kEndOfSystrace)
     return util::OkStatus();
-  partial_buf_.insert(partial_buf_.end(), &owned_buf[0], &owned_buf[size]);
+  partial_buf_.insert(partial_buf_.end(), blob.data(),
+                      blob.data() + blob.size());
 
   if (state_ == ParseState::kBeforeParse) {
     state_ = partial_buf_[0] == '<' ? ParseState::kHtmlBeforeSystrace
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.h b/src/trace_processor/importers/systrace/systrace_trace_parser.h
index bcb5ea8..9721879 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.h
@@ -35,7 +35,7 @@
   ~SystraceTraceParser() override;
 
   // ChunkedTraceReader implementation.
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t size) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
  private:
diff --git a/src/trace_processor/read_trace.cc b/src/trace_processor/read_trace.cc
index 4a9d8fd..fb2938b 100644
--- a/src/trace_processor/read_trace.cc
+++ b/src/trace_processor/read_trace.cc
@@ -23,6 +23,8 @@
 #include "perfetto/protozero/proto_utils.h"
 #include "perfetto/trace_processor/trace_processor.h"
 
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/forwarding_trace_parser.h"
 #include "src/trace_processor/importers/gzip/gzip_trace_parser.h"
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
@@ -60,8 +62,8 @@
     if (progress_callback && i % 128 == 0)
       progress_callback(*file_size);
 
-    std::unique_ptr<uint8_t[]> buf(new uint8_t[kChunkSize]);
-    auto rsize = base::Read(fd, buf.get(), kChunkSize);
+    TraceBlob blob = TraceBlob::Allocate(kChunkSize);
+    auto rsize = base::Read(fd, blob.data(), blob.size());
     if (rsize == 0)
       break;
 
@@ -71,30 +73,30 @@
     }
 
     *file_size += static_cast<uint64_t>(rsize);
-
-    RETURN_IF_ERROR(tp->Parse(std::move(buf), static_cast<size_t>(rsize)));
+    TraceBlobView blob_view(std::move(blob), 0, static_cast<size_t>(rsize));
+    RETURN_IF_ERROR(tp->Parse(std::move(blob_view)));
   }
   return util::OkStatus();
 }
 
 class SerializingProtoTraceReader : public ChunkedTraceReader {
  public:
-  SerializingProtoTraceReader(std::vector<uint8_t>* output) : output_(output) {}
+  explicit SerializingProtoTraceReader(std::vector<uint8_t>* output)
+      : output_(output) {}
 
-  util::Status Parse(std::unique_ptr<uint8_t[]> data, size_t size) override {
-    return tokenizer_.Tokenize(
-        std::move(data), size, [this](TraceBlobView packet) {
-          uint8_t buffer[protozero::proto_utils::kMaxSimpleFieldEncodedSize];
+  util::Status Parse(TraceBlobView blob) override {
+    return tokenizer_.Tokenize(std::move(blob), [this](TraceBlobView packet) {
+      uint8_t buffer[protozero::proto_utils::kMaxSimpleFieldEncodedSize];
 
-          uint8_t* pos = buffer;
-          pos = protozero::proto_utils::WriteVarInt(kTracePacketTag, pos);
-          pos = protozero::proto_utils::WriteVarInt(packet.length(), pos);
-          output_->insert(output_->end(), buffer, pos);
+      uint8_t* pos = buffer;
+      pos = protozero::proto_utils::WriteVarInt(kTracePacketTag, pos);
+      pos = protozero::proto_utils::WriteVarInt(packet.length(), pos);
+      output_->insert(output_->end(), buffer, pos);
 
-          output_->insert(output_->end(), packet.data(),
-                          packet.data() + packet.length());
-          return util::OkStatus();
-        });
+      output_->insert(output_->end(), packet.data(),
+                      packet.data() + packet.length());
+      return util::OkStatus();
+    });
   }
 
   void NotifyEndOfFile() override {}
@@ -164,7 +166,9 @@
     PERFETTO_CHECK(aio_read(&cb) == 0);
 
     // Parse the completed buffer while the async read is in-flight.
-    RETURN_IF_ERROR(tp->Parse(std::move(buf), static_cast<size_t>(rsize)));
+    TraceBlob blob =
+        TraceBlob::TakeOwnership(std::move(buf), static_cast<size_t>(rsize));
+    RETURN_IF_ERROR(tp->Parse(TraceBlobView(std::move(blob))));
   }
 
   if (file_size == 0) {
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
index 303e234..3cd27cd 100644
--- a/src/trace_processor/timestamped_trace_piece.h
+++ b/src/trace_processor/timestamped_trace_piece.h
@@ -19,13 +19,13 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
 #include "src/trace_processor/importers/json/json_utils.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/systrace/systrace_line.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 // GCC can't figure out the relationship between TimestampedTracePiece's type
 // and the union, and thus thinks that we may be moving or destroying
diff --git a/src/trace_processor/trace_blob.cc b/src/trace_processor/trace_blob.cc
new file mode 100644
index 0000000..50e4751
--- /dev/null
+++ b/src/trace_processor/trace_blob.cc
@@ -0,0 +1,84 @@
+/*
+ * 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 "perfetto/trace_processor/trace_blob.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/trace_processor/basic_types.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// static
+TraceBlob TraceBlob::Allocate(size_t size) {
+  TraceBlob blob(Ownership::kHeapBuf, new uint8_t[size], size);
+  PERFETTO_CHECK(blob.data_);
+  return blob;
+}
+
+// static
+TraceBlob TraceBlob::CopyFrom(const void* src, size_t size) {
+  TraceBlob blob = Allocate(size);
+  memcpy(blob.data_, src, size);
+  return blob;
+}
+
+// static
+TraceBlob TraceBlob::TakeOwnership(std::unique_ptr<uint8_t[]> buf,
+                                   size_t size) {
+  PERFETTO_CHECK(buf);
+  return TraceBlob(Ownership::kHeapBuf, buf.release(), size);
+}
+
+TraceBlob::~TraceBlob() {
+  PERFETTO_CHECK(refcount_ == 0);
+  switch (ownership_) {
+    case Ownership::kHeapBuf:
+      delete[] data_;
+      break;
+
+    case Ownership::kNull:
+      // Nothing to do.
+      break;
+  }
+  data_ = nullptr;
+  size_ = 0;
+}
+
+TraceBlob& TraceBlob::operator=(TraceBlob&& other) noexcept {
+  if (this == &other)
+    return *this;
+  static_assert(sizeof(*this) == base::AlignUp<sizeof(void*)>(
+                                     sizeof(data_) + sizeof(size_) +
+                                     sizeof(ownership_) + sizeof(refcount_)),
+                "TraceBlob move operator needs updating");
+  data_ = other.data_;
+  size_ = other.size_;
+  ownership_ = other.ownership_;
+  refcount_ = other.refcount_;
+  other.refcount_ = 0;
+  other.data_ = nullptr;
+  other.size_ = 0;
+  other.ownership_ = Ownership::kNull;
+  return *this;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 97cc591..f942406 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -873,10 +873,9 @@
 
 TraceProcessorImpl::~TraceProcessorImpl() = default;
 
-util::Status TraceProcessorImpl::Parse(std::unique_ptr<uint8_t[]> data,
-                                       size_t size) {
-  bytes_parsed_ += size;
-  return TraceProcessorStorageImpl::Parse(std::move(data), size);
+util::Status TraceProcessorImpl::Parse(TraceBlobView blob) {
+  bytes_parsed_ += blob.size();
+  return TraceProcessorStorageImpl::Parse(std::move(blob));
 }
 
 std::string TraceProcessorImpl::GetCurrentTraceName() {
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 5db351c..f94e6d5 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -49,7 +49,7 @@
   ~TraceProcessorImpl() override;
 
   // TraceProcessorStorage implementation:
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
   // TraceProcessor implementation:
diff --git a/src/trace_processor/trace_processor_storage.cc b/src/trace_processor/trace_processor_storage.cc
index f72c2a6..36e6579 100644
--- a/src/trace_processor/trace_processor_storage.cc
+++ b/src/trace_processor/trace_processor_storage.cc
@@ -16,6 +16,7 @@
 
 #include "perfetto/trace_processor/trace_processor_storage.h"
 
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_processor_storage_impl.h"
 
 namespace perfetto {
@@ -30,5 +31,10 @@
 
 TraceProcessorStorage::~TraceProcessorStorage() = default;
 
+util::Status TraceProcessorStorage::Parse(std::unique_ptr<uint8_t[]> buf,
+                                          size_t size) {
+  return Parse(TraceBlobView(TraceBlob::TakeOwnership(std::move(buf), size)));
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index b71eac7..53bc802 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -82,9 +82,8 @@
 
 TraceProcessorStorageImpl::~TraceProcessorStorageImpl() {}
 
-util::Status TraceProcessorStorageImpl::Parse(std::unique_ptr<uint8_t[]> data,
-                                              size_t size) {
-  if (size == 0)
+util::Status TraceProcessorStorageImpl::Parse(TraceBlobView blob) {
+  if (blob.size() == 0)
     return util::OkStatus();
   if (unrecoverable_parse_error_)
     return util::ErrStatus(
@@ -96,10 +95,10 @@
       stats::parse_trace_duration_ns);
 
   if (hash_input_size_remaining_ > 0 && !context_.uuid_found_in_trace) {
-    const size_t hash_size = std::min(hash_input_size_remaining_, size);
+    const size_t hash_size = std::min(hash_input_size_remaining_, blob.size());
     hash_input_size_remaining_ -= hash_size;
 
-    trace_hash_.Update(reinterpret_cast<const char*>(data.get()), hash_size);
+    trace_hash_.Update(reinterpret_cast<const char*>(blob.data()), hash_size);
     base::Uuid uuid(static_cast<int64_t>(trace_hash_.digest()), 0);
     const StringId id_for_uuid =
         context_.storage->InternString(base::StringView(uuid.ToPrettyString()));
@@ -107,7 +106,7 @@
                                            Variadic::String(id_for_uuid));
   }
 
-  util::Status status = context_.chunk_reader->Parse(std::move(data), size);
+  util::Status status = context_.chunk_reader->Parse(std::move(blob));
   unrecoverable_parse_error_ |= !status.ok();
   return status;
 }
diff --git a/src/trace_processor/trace_processor_storage_impl.h b/src/trace_processor/trace_processor_storage_impl.h
index 4b225d0..901256b 100644
--- a/src/trace_processor/trace_processor_storage_impl.h
+++ b/src/trace_processor/trace_processor_storage_impl.h
@@ -33,7 +33,7 @@
   explicit TraceProcessorStorageImpl(const Config&);
   ~TraceProcessorStorageImpl() override;
 
-  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+  util::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
   TraceProcessorContext* context() { return &context_; }
diff --git a/src/trace_processor/trace_sorter.h b/src/trace_processor/trace_sorter.h
index 50bec23..6625373 100644
--- a/src/trace_processor/trace_sorter.h
+++ b/src/trace_processor/trace_sorter.h
@@ -21,9 +21,9 @@
 
 #include "perfetto/ext/base/circular_queue.h"
 #include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 
 namespace Json {
 class Value;
diff --git a/src/trace_processor/trace_sorter_unittest.cc b/src/trace_processor/trace_sorter_unittest.cc
index 18c6d1e..342cde9 100644
--- a/src/trace_processor/trace_sorter_unittest.cc
+++ b/src/trace_processor/trace_sorter_unittest.cc
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/trace_blob.h"
 #include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/trace_sorter.h"
 #include "src/trace_processor/types/trace_processor_context.h"
@@ -72,8 +73,7 @@
 
 class TraceSorterTest : public ::testing::Test {
  public:
-  TraceSorterTest()
-      : test_buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[8]), 0, 8) {
+  TraceSorterTest() : test_buffer_(TraceBlob::Allocate(8)) {
     storage_ = new NiceMock<MockTraceStorage>();
     context_.storage.reset(storage_);
     CreateSorter();
@@ -97,7 +97,7 @@
 
 TEST_F(TraceSorterTest, TestFtrace) {
   PacketSequenceState state(&context_);
-  TraceBlobView view = test_buffer_.slice(0, 1);
+  TraceBlobView view = test_buffer_.slice_off(0, 1);
   EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(0, 1000, view.data(), 1));
   context_.sorter->PushFtraceEvent(0 /*cpu*/, 1000 /*timestamp*/,
                                    std::move(view), &state);
@@ -106,7 +106,7 @@
 
 TEST_F(TraceSorterTest, TestTracePacket) {
   PacketSequenceState state(&context_);
-  TraceBlobView view = test_buffer_.slice(0, 1);
+  TraceBlobView view = test_buffer_.slice_off(0, 1);
   EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1000, view.data(), 1));
   context_.sorter->PushTracePacket(1000, &state, std::move(view));
   context_.sorter->ExtractEventsForced();
@@ -114,10 +114,10 @@
 
 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);
-  TraceBlobView view_4 = test_buffer_.slice(0, 4);
+  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
+  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
+  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
+  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
 
   InSequence s;
 
@@ -140,11 +140,11 @@
 
   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);
-  TraceBlobView view_4 = test_buffer_.slice(0, 4);
-  TraceBlobView view_5 = test_buffer_.slice(0, 5);
+  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
+  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
+  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
+  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
+  TraceBlobView view_5 = test_buffer_.slice_off(0, 5);
 
   // Flush at the start of packet sequence to match behavior of the
   // service.
@@ -202,10 +202,10 @@
 
   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);
-  TraceBlobView view_4 = test_buffer_.slice(0, 4);
+  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
+  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
+  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
+  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
 
   context_.sorter->NotifyFlushEvent();
   context_.sorter->NotifyFlushEvent();
@@ -288,8 +288,7 @@
     for (int j = 0; j < num_cpus; j++) {
       uint32_t cpu = static_cast<uint32_t>(rnd_engine() % 32);
       expectations[ts].push_back(cpu);
-      context_.sorter->PushFtraceEvent(cpu, ts, TraceBlobView(nullptr, 0, 0),
-                                       &state);
+      context_.sorter->PushFtraceEvent(cpu, ts, TraceBlobView(), &state);
     }
   }
 
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 055cc81..78ee878 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -61,21 +61,13 @@
   ]
 }
 
-source_set("trace_blob_view") {
-  sources = [ "trace_blob_view.h" ]
-  deps = [
-    "../../../gn:default_deps",
-    "../../base",
-  ]
-}
-
 source_set("interned_message_view") {
   sources = [ "interned_message_view.h" ]
+  public_deps = [ "../../../include/perfetto/trace_processor" ]
   deps = [
     "../../../gn:default_deps",
     "../../base",
   ]
-  public_deps = [ ":trace_blob_view" ]
 }
 
 source_set("descriptors") {
diff --git a/src/trace_processor/util/debug_annotation_parser_unittest.cc b/src/trace_processor/util/debug_annotation_parser_unittest.cc
index b23518a..d074e79 100644
--- a/src/trace_processor/util/debug_annotation_parser_unittest.cc
+++ b/src/trace_processor/util/debug_annotation_parser_unittest.cc
@@ -18,6 +18,7 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
@@ -25,7 +26,6 @@
 #include "src/trace_processor/test_messages.descriptor.h"
 #include "src/trace_processor/util/interned_message_view.h"
 #include "src/trace_processor/util/proto_to_args_parser.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 #include "test/gtest_and_gmock.h"
 
 #include <sstream>
diff --git a/src/trace_processor/util/interned_message_view.h b/src/trace_processor/util/interned_message_view.h
index 7341aea..4f5b993 100644
--- a/src/trace_processor/util/interned_message_view.h
+++ b/src/trace_processor/util/interned_message_view.h
@@ -17,7 +17,7 @@
 #ifndef SRC_TRACE_PROCESSOR_UTIL_INTERNED_MESSAGE_VIEW_H_
 #define SRC_TRACE_PROCESSOR_UTIL_INTERNED_MESSAGE_VIEW_H_
 
-#include "src/trace_processor/util/trace_blob_view.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 
 #include <unordered_map>
 
@@ -35,7 +35,7 @@
 // Entry in an interning index, refers to the interned message.
 class InternedMessageView {
  public:
-  InternedMessageView(TraceBlobView msg) : message_(std::move(msg)) {}
+  explicit InternedMessageView(TraceBlobView msg) : message_(std::move(msg)) {}
 
   InternedMessageView(InternedMessageView&&) = default;
   InternedMessageView& operator=(InternedMessageView&&) = default;
@@ -43,9 +43,10 @@
   // Allow copy by cloning the TraceBlobView. This is required for
   // UpdateTracePacketDefaults().
   InternedMessageView(const InternedMessageView& view)
-      : message_(view.message_.slice(0, view.message_.length())) {}
+      : message_(view.message_.copy()) {}
+
   InternedMessageView& operator=(const InternedMessageView& view) {
-    this->message_ = view.message_.slice(0, view.message_.length());
+    this->message_ = view.message_.copy();
     this->decoder_ = nullptr;
     this->decoder_type_ = nullptr;
     this->submessages_.clear();
@@ -94,8 +95,7 @@
     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);
+    TraceBlobView submessage = message_.slice(field.data, field.size);
     InternedMessageView* submessage_view =
         new InternedMessageView(std::move(submessage));
     submessages_.emplace_hint(
diff --git a/src/trace_processor/util/proto_to_args_parser_unittest.cc b/src/trace_processor/util/proto_to_args_parser_unittest.cc
index 47d2d27..bc4b24f 100644
--- a/src/trace_processor/util/proto_to_args_parser_unittest.cc
+++ b/src/trace_processor/util/proto_to_args_parser_unittest.cc
@@ -18,12 +18,13 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
 #include "src/trace_processor/test_messages.descriptor.h"
 #include "src/trace_processor/util/interned_message_view.h"
-#include "src/trace_processor/util/trace_blob_view.h"
 #include "test/gtest_and_gmock.h"
 
 #include <sstream>
@@ -334,8 +335,9 @@
   for (size_t i = 0; i < binary_data.size(); ++i) {
     buffer.get()[i] = binary_data[i];
   }
-  TraceBlobView blob(std::move(buffer), 0, binary_data.size());
-  AddInternedSourceLocation(kIid, std::move(blob));
+  TraceBlob blob =
+      TraceBlob::TakeOwnership(std::move(buffer), binary_data.size());
+  AddInternedSourceLocation(kIid, TraceBlobView(std::move(blob)));
 
   DescriptorPool pool;
   auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
diff --git a/src/trace_processor/util/trace_blob_view.h b/src/trace_processor/util/trace_blob_view.h
deleted file mode 100644
index 7d9c1a4..0000000
--- a/src/trace_processor/util/trace_blob_view.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2018 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_UTIL_TRACE_BLOB_VIEW_H_
-#define SRC_TRACE_PROCESSOR_UTIL_TRACE_BLOB_VIEW_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <limits>
-#include <memory>
-
-#include "perfetto/base/logging.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// This class is an equivalent of std::string_view for trace binary data.
-// The main difference is that this class has also shared ownership of a portion
-// of the raw trace.
-// The underlying buffer will be freed once all the TraceBlobViews that refer
-// to the same buffer have passed through the pipeline and been parsed.
-class TraceBlobView {
- public:
-  TraceBlobView(std::unique_ptr<uint8_t[]> buffer, size_t offset, size_t length)
-      : shbuf_(SharedBuf(std::move(buffer))),
-        offset_(static_cast<uint32_t>(offset)),
-        length_(static_cast<uint32_t>(length)) {
-    PERFETTO_DCHECK(offset <= std::numeric_limits<uint32_t>::max());
-    PERFETTO_DCHECK(length <= std::numeric_limits<uint32_t>::max());
-  }
-
-  // Allow std::move().
-  TraceBlobView(TraceBlobView&&) noexcept = default;
-  TraceBlobView& operator=(TraceBlobView&&) = default;
-
-  // Disable implicit copy.
-  TraceBlobView(const TraceBlobView&) = delete;
-  TraceBlobView& operator=(const TraceBlobView&) = delete;
-
-  TraceBlobView slice(size_t offset, size_t length) const {
-    PERFETTO_DCHECK(offset + length <= offset_ + length_);
-    return TraceBlobView(shbuf_, offset, length);
-  }
-
-  bool operator==(const TraceBlobView& rhs) const {
-    return (shbuf_ == rhs.shbuf_) && (offset_ == rhs.offset_) &&
-           (length_ == rhs.length_);
-  }
-  bool operator!=(const TraceBlobView& rhs) const { return !(*this == rhs); }
-
-  inline const uint8_t* data() const { return start() + offset_; }
-
-  size_t offset_of(const uint8_t* data) const {
-    // When a field is size 0, data can be equal to start() + offset_ + length_.
-    PERFETTO_DCHECK(data >= start() && data <= (start() + offset_ + length_));
-    return static_cast<size_t>(data - start());
-  }
-
-  size_t length() const { return length_; }
-  size_t offset() const { return offset_; }
-
- private:
-  // An equivalent to std::shared_ptr<uint8_t>, with the differnce that:
-  // - Supports array types, available for shared_ptr only in C++17.
-  // - Is not thread safe, which is not needed for our purposes.
-  class SharedBuf {
-   public:
-    explicit SharedBuf(std::unique_ptr<uint8_t[]> mem) {
-      rcbuf_ = new RefCountedBuf(std::move(mem));
-    }
-
-    SharedBuf(const SharedBuf& copy) : rcbuf_(copy.rcbuf_) {
-      PERFETTO_DCHECK(rcbuf_->refcount > 0);
-      rcbuf_->refcount++;
-    }
-
-    ~SharedBuf() {
-      if (!rcbuf_)
-        return;
-      PERFETTO_DCHECK(rcbuf_->refcount > 0);
-      if (--rcbuf_->refcount == 0) {
-        RefCountedBuf* rcbuf = rcbuf_;
-        rcbuf_ = nullptr;
-        delete rcbuf;
-      }
-    }
-
-    SharedBuf(SharedBuf&& other) noexcept {
-      rcbuf_ = other.rcbuf_;
-      other.rcbuf_ = nullptr;
-    }
-
-    SharedBuf& operator=(SharedBuf&& other) {
-      if (this != &other) {
-        // A bit of a ugly but pragmatic pattern to implement move assignment.
-        // First invoke the distructor and then invoke the move constructor
-        // inline via placement-new.
-        this->~SharedBuf();
-        new (this) SharedBuf(std::move(other));
-      }
-      return *this;
-    }
-
-    bool operator==(const SharedBuf& x) const { return x.rcbuf_ == rcbuf_; }
-    bool operator!=(const SharedBuf& x) const { return !(x == *this); }
-    const uint8_t* data() const { return rcbuf_->mem.get(); }
-
-   private:
-    struct RefCountedBuf {
-      explicit RefCountedBuf(std::unique_ptr<uint8_t[]> buf)
-          : refcount(1), mem(std::move(buf)) {}
-      int refcount;
-      std::unique_ptr<uint8_t[]> mem;
-    };
-
-    RefCountedBuf* rcbuf_ = nullptr;
-  };
-
-  inline const uint8_t* start() const { return shbuf_.data(); }
-
-  TraceBlobView(SharedBuf b, size_t o, size_t l)
-      : shbuf_(b),
-        offset_(static_cast<uint32_t>(o)),
-        length_(static_cast<uint32_t>(l)) {}
-
-  SharedBuf shbuf_;
-  uint32_t offset_;
-  uint32_t length_;  // Measured from |offset_|, not from |data()|.
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_UTIL_TRACE_BLOB_VIEW_H_