tp: FileBuffer -> TraceBlobViewReader and use it in ProtoTokenizer
Change-Id: Ie8e8dc6968a956f020f2d6b8b07d043a55949278
diff --git a/Android.bp b/Android.bp
index daef6ec..e8eb8a3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2447,7 +2447,6 @@
":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
- ":perfetto_src_trace_processor_util_file_buffer",
":perfetto_src_trace_processor_util_glob",
":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
@@ -2460,6 +2459,7 @@
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stdlib",
+ ":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_util",
":perfetto_src_trace_processor_util_zip_reader",
@@ -13498,14 +13498,6 @@
],
}
-// GN: //src/trace_processor/util:file_buffer
-filegroup {
- name: "perfetto_src_trace_processor_util_file_buffer",
- srcs: [
- "src/trace_processor/util/file_buffer.cc",
- ],
-}
-
// GN: //src/trace_processor/util:glob
filegroup {
name: "perfetto_src_trace_processor_util_glob",
@@ -13595,6 +13587,14 @@
name: "perfetto_src_trace_processor_util_stdlib",
}
+// GN: //src/trace_processor/util:trace_blob_view_reader
+filegroup {
+ name: "perfetto_src_trace_processor_util_trace_blob_view_reader",
+ srcs: [
+ "src/trace_processor/util/trace_blob_view_reader.cc",
+ ],
+}
+
// GN: //src/trace_processor/util:trace_type
filegroup {
name: "perfetto_src_trace_processor_util_trace_type",
@@ -13609,7 +13609,6 @@
srcs: [
"src/trace_processor/util/bump_allocator_unittest.cc",
"src/trace_processor/util/debug_annotation_parser_unittest.cc",
- "src/trace_processor/util/file_buffer_unittest.cc",
"src/trace_processor/util/glob_unittest.cc",
"src/trace_processor/util/gzip_utils_unittest.cc",
"src/trace_processor/util/proto_profiler_unittest.cc",
@@ -13618,6 +13617,7 @@
"src/trace_processor/util/protozero_to_text_unittests.cc",
"src/trace_processor/util/sql_argument_unittest.cc",
"src/trace_processor/util/streaming_line_reader_unittest.cc",
+ "src/trace_processor/util/trace_blob_view_reader_unittest.cc",
"src/trace_processor/util/zip_reader_unittest.cc",
],
}
@@ -15197,7 +15197,6 @@
":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
- ":perfetto_src_trace_processor_util_file_buffer",
":perfetto_src_trace_processor_util_glob",
":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
@@ -15210,6 +15209,7 @@
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stdlib",
+ ":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_unittests",
":perfetto_src_trace_processor_util_util",
@@ -16214,7 +16214,6 @@
":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
- ":perfetto_src_trace_processor_util_file_buffer",
":perfetto_src_trace_processor_util_glob",
":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
@@ -16227,6 +16226,7 @@
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stdlib",
+ ":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_util",
":perfetto_src_trace_processor_util_zip_reader",
@@ -16427,6 +16427,7 @@
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
":perfetto_src_trace_processor_util_regex",
+ ":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_util",
":perfetto_src_trace_redaction_trace_redaction",
@@ -16610,7 +16611,6 @@
":perfetto_src_trace_processor_util_build_id",
":perfetto_src_trace_processor_util_bump_allocator",
":perfetto_src_trace_processor_util_descriptors",
- ":perfetto_src_trace_processor_util_file_buffer",
":perfetto_src_trace_processor_util_glob",
":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
@@ -16623,6 +16623,7 @@
":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stdlib",
+ ":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_util",
":perfetto_src_trace_processor_util_zip_reader",
diff --git a/BUILD b/BUILD
index bf3ce33..b0337fa 100644
--- a/BUILD
+++ b/BUILD
@@ -276,7 +276,6 @@
":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
- ":src_trace_processor_util_file_buffer",
":src_trace_processor_util_glob",
":src_trace_processor_util_gzip",
":src_trace_processor_util_interned_message_view",
@@ -289,6 +288,7 @@
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stdlib",
+ ":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
@@ -3063,15 +3063,6 @@
],
)
-# GN target: //src/trace_processor/util:file_buffer
-perfetto_filegroup(
- name = "src_trace_processor_util_file_buffer",
- srcs = [
- "src/trace_processor/util/file_buffer.cc",
- "src/trace_processor/util/file_buffer.h",
- ],
-)
-
# GN target: //src/trace_processor/util:glob
perfetto_filegroup(
name = "src_trace_processor_util_glob",
@@ -3181,6 +3172,15 @@
],
)
+# GN target: //src/trace_processor/util:trace_blob_view_reader
+perfetto_filegroup(
+ name = "src_trace_processor_util_trace_blob_view_reader",
+ srcs = [
+ "src/trace_processor/util/trace_blob_view_reader.cc",
+ "src/trace_processor/util/trace_blob_view_reader.h",
+ ],
+)
+
# GN target: //src/trace_processor/util:trace_type
perfetto_filegroup(
name = "src_trace_processor_util_trace_type",
@@ -6157,7 +6157,6 @@
":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
- ":src_trace_processor_util_file_buffer",
":src_trace_processor_util_glob",
":src_trace_processor_util_gzip",
":src_trace_processor_util_interned_message_view",
@@ -6170,6 +6169,7 @@
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stdlib",
+ ":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
@@ -6341,7 +6341,6 @@
":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
- ":src_trace_processor_util_file_buffer",
":src_trace_processor_util_glob",
":src_trace_processor_util_gzip",
":src_trace_processor_util_interned_message_view",
@@ -6354,6 +6353,7 @@
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stdlib",
+ ":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
@@ -6579,7 +6579,6 @@
":src_trace_processor_util_build_id",
":src_trace_processor_util_bump_allocator",
":src_trace_processor_util_descriptors",
- ":src_trace_processor_util_file_buffer",
":src_trace_processor_util_glob",
":src_trace_processor_util_gzip",
":src_trace_processor_util_interned_message_view",
@@ -6592,6 +6591,7 @@
":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stdlib",
+ ":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
":src_trace_processor_util_zip_reader",
diff --git a/include/perfetto/base/status.h b/include/perfetto/base/status.h
index 506bb6e..ee39593 100644
--- a/include/perfetto/base/status.h
+++ b/include/perfetto/base/status.h
@@ -109,7 +109,7 @@
return Status();
}
-PERFETTO_PRINTF_FORMAT(1, 2) Status ErrStatus(const char* format, ...);
+Status ErrStatus(const char* format, ...) PERFETTO_PRINTF_FORMAT(1, 2);
} // namespace base
} // namespace perfetto
diff --git a/src/trace_processor/importers/perf/BUILD.gn b/src/trace_processor/importers/perf/BUILD.gn
index da4153a..5fd5d25 100644
--- a/src/trace_processor/importers/perf/BUILD.gn
+++ b/src/trace_processor/importers/perf/BUILD.gn
@@ -83,7 +83,7 @@
"../../tables:tables_python",
"../../types",
"../../util:build_id",
- "../../util:file_buffer",
+ "../../util:trace_blob_view_reader",
"../../util:util",
"../common:common",
"../proto:minimal",
diff --git a/src/trace_processor/importers/perf/perf_data_tokenizer.cc b/src/trace_processor/importers/perf/perf_data_tokenizer.cc
index 75e4b50..1c48a4a 100644
--- a/src/trace_processor/importers/perf/perf_data_tokenizer.cc
+++ b/src/trace_processor/importers/perf/perf_data_tokenizer.cc
@@ -239,7 +239,7 @@
base::StatusOr<PerfDataTokenizer::ParsingResult>
PerfDataTokenizer::ParseRecords() {
- while (buffer_.file_offset() < header_.data.end()) {
+ while (buffer_.start_offset() < header_.data.end()) {
Record record;
if (auto res = ParseRecord(record);
@@ -260,7 +260,7 @@
Record& record) {
record.session = perf_session_;
std::optional<TraceBlobView> tbv =
- buffer_.SliceOff(buffer_.file_offset(), sizeof(record.header));
+ buffer_.SliceOff(buffer_.start_offset(), sizeof(record.header));
if (!tbv) {
return ParsingResult::kMoreDataNeeded;
}
@@ -270,7 +270,7 @@
return base::ErrStatus("Invalid record size: %" PRIu16, record.header.size);
}
- tbv = buffer_.SliceOff(buffer_.file_offset() + sizeof(record.header),
+ tbv = buffer_.SliceOff(buffer_.start_offset() + sizeof(record.header),
record.header.size - sizeof(record.header));
if (!tbv) {
return ParsingResult::kMoreDataNeeded;
@@ -332,7 +332,7 @@
base::StatusOr<PerfDataTokenizer::ParsingResult>
PerfDataTokenizer::ParseFeatureSections() {
- PERFETTO_CHECK(buffer_.file_offset() == header_.data.end());
+ PERFETTO_CHECK(buffer_.start_offset() == header_.data.end());
auto tbv = buffer_.SliceOff(feature_headers_section_.offset,
feature_headers_section_.size);
if (!tbv) {
diff --git a/src/trace_processor/importers/perf/perf_data_tokenizer.h b/src/trace_processor/importers/perf/perf_data_tokenizer.h
index 61c1308..f02efba 100644
--- a/src/trace_processor/importers/perf/perf_data_tokenizer.h
+++ b/src/trace_processor/importers/perf/perf_data_tokenizer.h
@@ -30,7 +30,7 @@
#include "src/trace_processor/importers/common/chunked_trace_reader.h"
#include "src/trace_processor/importers/perf/perf_file.h"
#include "src/trace_processor/importers/perf/perf_session.h"
-#include "src/trace_processor/util/file_buffer.h"
+#include "src/trace_processor/util/trace_blob_view_reader.h"
namespace perfetto {
namespace trace_processor {
@@ -90,7 +90,7 @@
RefPtr<PerfSession> perf_session_;
- util::FileBuffer buffer_;
+ util::TraceBlobViewReader buffer_;
int64_t latest_timestamp_ = 0;
};
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index 937fd1d..68d8963 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -100,6 +100,7 @@
"../../util:build_id",
"../../util:gzip",
"../../util:profiler_util",
+ "../../util:trace_blob_view_reader",
"../common",
"../common:parser_types",
"../etw:minimal",
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.h b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
index 2526900..1d37791 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.h
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
@@ -17,23 +17,26 @@
#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_TRACE_TOKENIZER_H_
#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_TRACE_TOKENIZER_H_
+#include <algorithm>
+#include <cstddef>
#include <cstdint>
-#include <vector>
+#include <optional>
+#include <utility>
+#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
+#include "perfetto/protozero/field.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/public/compiler.h"
-#include "perfetto/trace_processor/status.h"
-#include "perfetto/trace_processor/trace_blob.h"
#include "perfetto/trace_processor/trace_blob_view.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "src/trace_processor/util/gzip_utils.h"
-#include "src/trace_processor/util/status_macros.h"
#include "protos/perfetto/trace/trace.pbzero.h"
-#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/trace_blob_view_reader.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
// Reads a protobuf trace in chunks and extracts boundaries of trace packets
// (or subfields, for the case of ftrace) with their timestamps.
@@ -41,116 +44,79 @@
public:
ProtoTraceTokenizer();
- template <typename Callback = util::Status(TraceBlobView)>
- 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;
- if (PERFETTO_UNLIKELY(partial_buf_.size() < kHeaderBytes)) {
- size_t missing_len = std::min(kHeaderBytes - partial_buf_.size(), size);
- partial_buf_.insert(partial_buf_.end(), &data[0], &data[missing_len]);
- if (partial_buf_.size() < kHeaderBytes)
- return util::OkStatus();
- data += missing_len;
- size -= missing_len;
+ template <typename Callback = base::Status(TraceBlobView)>
+ base::Status Tokenize(TraceBlobView tbv, Callback callback) {
+ reader_.PushBack(std::move(tbv));
+
+ for (;;) {
+ size_t start_offset = reader_.start_offset();
+ size_t avail = reader_.avail();
+
+ // The header must be at least 2 bytes (1 byte for tag, 1 byte for
+ // size) and can be at most 6 bytes (1 byte for tag + 5 bytes for
+ // size).
+ const size_t kMinHeaderBytes = 2;
+ const size_t kMaxHeaderBytes = 6;
+ std::optional<TraceBlobView> header = reader_.SliceOff(
+ start_offset,
+ std::min(std::max(avail, kMinHeaderBytes), kMaxHeaderBytes));
+
+ // This means that kMinHeaderBytes was not available. Just wait for the
+ // next round.
+ if (PERFETTO_UNLIKELY(!header)) {
+ return base::OkStatus();
}
- // At this point we have enough data in |partial_buf_| to read at least
- // the field header and know the size of the next TracePacket.
- const uint8_t* pos = &partial_buf_[0];
- uint8_t proto_field_tag = *pos;
- uint64_t field_size = 0;
- // We cannot do &partial_buf_[partial_buf_.size()] because that crashes
- // on MSVC STL debug builds, so does &*partial_buf_.end().
- const uint8_t* next = protozero::proto_utils::ParseVarInt(
- ++pos, &partial_buf_.front() + partial_buf_.size(), &field_size);
- bool parse_failed = next == pos;
- pos = next;
- if (proto_field_tag != kTracePacketTag || field_size == 0 ||
- parse_failed) {
- return util::ErrStatus(
- "Failed parsing a TracePacket from the partial buffer");
+ uint64_t field_size;
+ const uint8_t* size_start = header->data() + 1;
+ const uint8_t* size_end = protozero::proto_utils::ParseVarInt(
+ size_start, header->data() + header->size(), &field_size);
+
+ // If we had less than the maximum number of header bytes, it's possible
+ // that we just need more to actually parse. Otherwise, this is an error.
+ if (PERFETTO_UNLIKELY(size_start == size_end)) {
+ return header->size() < kMaxHeaderBytes
+ ? base::OkStatus()
+ : base::ErrStatus("Failed to parse TracePacket size");
}
- // At this point we know how big the TracePacket is.
- size_t hdr_size = static_cast<size_t>(pos - &partial_buf_[0]);
- size_t size_incl_header = static_cast<size_t>(field_size + hdr_size);
- PERFETTO_DCHECK(size_incl_header > partial_buf_.size());
-
- // There is a good chance that between the |partial_buf_| and the new
- // |data| of the current call we have enough bytes to parse a TracePacket.
- if (partial_buf_.size() + size >= size_incl_header) {
- // Create a new buffer for the whole TracePacket and copy into that:
- // 1) The beginning of the TracePacket (including the proto header) from
- // the partial buffer.
- // 2) The rest of the TracePacket from the current |data| buffer (note
- // that we might have consumed already a few bytes form |data|
- // earlier in this function, hence we need to keep |off| into
- // account).
- 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(glued.data() + partial_buf_.size(), &data[0], size_missing);
- data += size_missing;
- size -= size_missing;
- partial_buf_.clear();
- RETURN_IF_ERROR(
- ParseInternal(TraceBlobView(std::move(glued)), callback));
- } else {
- partial_buf_.insert(partial_buf_.end(), data, &data[size]);
- return util::OkStatus();
+ // Empty packets can legitimately happen if the producer ends up emitting
+ // no data: just ignore them.
+ auto hdr_size = static_cast<size_t>(size_end - header->data());
+ if (PERFETTO_UNLIKELY(field_size == 0)) {
+ PERFETTO_CHECK(reader_.PopFrontBytes(hdr_size));
+ continue;
}
- }
- return ParseInternal(blob.slice(data, size), callback);
- }
- private:
- static constexpr uint8_t kTracePacketTag =
- protozero::proto_utils::MakeTagLengthDelimited(
- protos::pbzero::Trace::kPacketFieldNumber);
-
- template <typename Callback = util::Status(TraceBlobView)>
- util::Status ParseInternal(TraceBlobView whole_buf, Callback callback) {
- static constexpr auto kLengthDelimited =
- protozero::proto_utils::ProtoWireType::kLengthDelimited;
- 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) {
- if (PERFETTO_UNLIKELY(it->type() != kLengthDelimited)) {
- return base::ErrStatus("Failed to parse TracePacket bounds");
+ // If there's no enough bytes in the reader, then we cannot do anymore.
+ size_t size_incl_header = hdr_size + field_size;
+ if (size_incl_header > avail) {
+ return base::OkStatus();
}
- protozero::ConstBytes packet = *it;
- 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(), &start[decoder.read_offset()],
- &start[decoder.read_offset() + bytes_left]);
- }
- return util::OkStatus();
- }
+ uint8_t proto_field_tag = *header->data();
+ if (PERFETTO_UNLIKELY(proto_field_tag != kTracePacketTag)) {
+ return base::ErrStatus("Invalid TracePacket tag or size");
+ }
- template <typename Callback = util::Status(TraceBlobView)>
- util::Status ParsePacket(TraceBlobView packet, Callback callback) {
- protos::pbzero::TracePacket::Decoder decoder(packet.data(),
- packet.length());
- if (decoder.has_compressed_packets()) {
+ auto packet = reader_.SliceOff(start_offset + hdr_size, field_size);
+ PERFETTO_CHECK(packet);
+ PERFETTO_CHECK(reader_.PopFrontBytes(hdr_size + field_size));
+ protos::pbzero::TracePacket::Decoder decoder(packet->data(),
+ packet->length());
+ if (!decoder.has_compressed_packets()) {
+ RETURN_IF_ERROR(callback(std::move(*packet)));
+ continue;
+ }
+
if (!util::IsGzipSupported()) {
- return util::Status(
+ return base::ErrStatus(
"Cannot decode compressed packets. Zlib not enabled");
}
protozero::ConstBytes field = decoder.compressed_packets();
- TraceBlobView compressed_packets = packet.slice(field.data, field.size);
+ TraceBlobView compressed_packets = packet->slice(field.data, field.size);
TraceBlobView packets;
-
RETURN_IF_ERROR(Decompress(std::move(compressed_packets), &packets));
const uint8_t* start = packets.data();
@@ -158,35 +124,41 @@
const uint8_t* ptr = start;
while ((end - ptr) > 2) {
const uint8_t* packet_outer = ptr;
- if (PERFETTO_UNLIKELY(*ptr != kTracePacketTag))
- return util::ErrStatus("Expected TracePacket tag");
+ if (PERFETTO_UNLIKELY(*ptr != kTracePacketTag)) {
+ return base::ErrStatus("Expected TracePacket tag");
+ }
uint64_t packet_size = 0;
ptr = protozero::proto_utils::ParseVarInt(++ptr, end, &packet_size);
const uint8_t* packet_start = ptr;
ptr += packet_size;
- if (PERFETTO_UNLIKELY((ptr - packet_outer) < 2 || ptr > end))
- return util::ErrStatus("Invalid packet size");
-
+ if (PERFETTO_UNLIKELY((ptr - packet_outer) < 2 || ptr > end)) {
+ return base::ErrStatus("Invalid packet size");
+ }
TraceBlobView sliced =
packets.slice(packet_start, static_cast<size_t>(packet_size));
- RETURN_IF_ERROR(ParsePacket(std::move(sliced), callback));
+ RETURN_IF_ERROR(callback(std::move(sliced)));
}
- return util::OkStatus();
}
- return callback(std::move(packet));
}
- util::Status Decompress(TraceBlobView input, TraceBlobView* output);
+ private:
+ static constexpr uint8_t kTracePacketTag =
+ protozero::proto_utils::MakeTagLengthDelimited(
+ protos::pbzero::Trace::kPacketFieldNumber);
+ static constexpr uint16_t kCompresedPacketsTag =
+ protozero::proto_utils::MakeTagLengthDelimited(
+ protos::pbzero::TracePacket::kCompressedPacketsFieldNumber);
+
+ base::Status Decompress(TraceBlobView input, TraceBlobView* output);
// Used to glue together trace packets that span across two (or more)
// Parse() boundaries.
- std::vector<uint8_t> partial_buf_;
+ util::TraceBlobViewReader reader_;
// Allows support for compressed trace packets.
util::GzipDecompressor decompressor_;
};
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_TRACE_TOKENIZER_H_
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 6c40069..ee4ee7c 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -254,10 +254,10 @@
]
}
-source_set("file_buffer") {
+source_set("trace_blob_view_reader") {
sources = [
- "file_buffer.cc",
- "file_buffer.h",
+ "trace_blob_view_reader.cc",
+ "trace_blob_view_reader.h",
]
deps = [
"../../../gn:default_deps",
@@ -281,7 +281,6 @@
sources = [
"bump_allocator_unittest.cc",
"debug_annotation_parser_unittest.cc",
- "file_buffer_unittest.cc",
"glob_unittest.cc",
"proto_profiler_unittest.cc",
"proto_to_args_parser_unittest.cc",
@@ -289,13 +288,13 @@
"protozero_to_text_unittests.cc",
"sql_argument_unittest.cc",
"streaming_line_reader_unittest.cc",
+ "trace_blob_view_reader_unittest.cc",
"zip_reader_unittest.cc",
]
testonly = true
deps = [
":bump_allocator",
":descriptors",
- ":file_buffer",
":glob",
":gzip",
":proto_profiler",
@@ -303,6 +302,7 @@
":protozero_to_json",
":protozero_to_text",
":sql_argument",
+ ":trace_blob_view_reader",
":zip_reader",
"..:gen_cc_test_messages_descriptor",
"../../../gn:default_deps",
diff --git a/src/trace_processor/util/file_buffer.cc b/src/trace_processor/util/file_buffer.cc
deleted file mode 100644
index 1dbd10b..0000000
--- a/src/trace_processor/util/file_buffer.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-
-/*
- * Copyright (C) 2024 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/util/file_buffer.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <iterator>
-#include <optional>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/trace_processor/trace_blob.h"
-#include "perfetto/trace_processor/trace_blob_view.h"
-
-namespace perfetto::trace_processor::util {
-
-void FileBuffer::PushBack(TraceBlobView data) {
- if (data.size() == 0) {
- return;
- }
- const size_t size = data.size();
- data_.emplace_back(Entry{end_offset_, std::move(data)});
- end_offset_ += size;
-}
-
-bool FileBuffer::PopFrontUntil(const size_t target_offset) {
- PERFETTO_CHECK(file_offset() <= target_offset);
- while (!data_.empty()) {
- Entry& entry = data_.front();
- if (target_offset == entry.file_offset) {
- return true;
- }
- const size_t bytes_to_pop = target_offset - entry.file_offset;
- if (entry.data.size() > bytes_to_pop) {
- entry.data =
- entry.data.slice_off(bytes_to_pop, entry.data.size() - bytes_to_pop);
- entry.file_offset += bytes_to_pop;
- return true;
- }
- data_.pop_front();
- }
-
- return target_offset == end_offset_;
-}
-
-std::optional<TraceBlobView> FileBuffer::SliceOff(size_t start_offset,
- size_t length) const {
- if (length == 0) {
- return TraceBlobView();
- }
-
- if (start_offset + length > end_offset_) {
- return std::nullopt;
- }
-
- Iterator it = FindEntryWithOffset(start_offset);
- if (it == end()) {
- return std::nullopt;
- }
-
- const size_t offset_from_entry_start = start_offset - it->file_offset;
- const size_t bytes_in_entry = it->data.size() - offset_from_entry_start;
- TraceBlobView first_blob = it->data.slice_off(
- offset_from_entry_start, std::min(bytes_in_entry, length));
-
- if (first_blob.size() == length) {
- return std::move(first_blob);
- }
-
- auto buffer = TraceBlob::Allocate(length);
- uint8_t* ptr = buffer.data();
-
- memcpy(ptr, first_blob.data(), first_blob.size());
- ptr += first_blob.size();
- length -= first_blob.size();
- ++it;
-
- while (length != 0) {
- PERFETTO_DCHECK(it != end());
- const size_t bytes_to_copy = std::min(length, it->data.size());
- memcpy(ptr, it->data.data(), bytes_to_copy);
- ptr += bytes_to_copy;
- length -= bytes_to_copy;
- ++it;
- }
-
- return TraceBlobView(std::move(buffer));
-}
-
-FileBuffer::Iterator FileBuffer::FindEntryWithOffset(size_t offset) const {
- if (offset >= end_offset_) {
- return end();
- }
-
- auto it = std::upper_bound(
- data_.begin(), data_.end(), offset,
- [](size_t offset, const Entry& rhs) { return offset < rhs.file_offset; });
- // This can only happen if too much data was popped.
- PERFETTO_CHECK(it != data_.begin());
- return std::prev(it);
-}
-
-} // namespace perfetto::trace_processor::util
diff --git a/src/trace_processor/util/file_buffer.h b/src/trace_processor/util/file_buffer.h
deleted file mode 100644
index 35e7384..0000000
--- a/src/trace_processor/util/file_buffer.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2024 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_FILE_BUFFER_H_
-#define SRC_TRACE_PROCESSOR_UTIL_FILE_BUFFER_H_
-
-#include <cstddef>
-#include <optional>
-
-#include "perfetto/ext/base/circular_queue.h"
-#include "perfetto/trace_processor/trace_blob_view.h"
-
-namespace perfetto::trace_processor::util {
-
-// Helper class that exposes a window into the contents of a file. Data can be
-// appended to the end of the buffer (increasing the size of the window) or
-// removed from the front (decreasing the size of the window).
-//
-// TraceProcessor reads trace files in chunks and streams those to the
-// `ChunkedTraceReader` instance. But sometimes the reader needs to look into
-// the future (i.e. some data that has not yet arrived) before being able to
-// process the current data. In such a case the reader would have to buffer data
-// until the "future" data arrives. This class encapsulates that functionality.
-class FileBuffer {
- public:
- // Trivial empty ctor.
- FileBuffer() = default;
-
- bool empty() const { return data_.empty(); }
-
- // Returns the offset to the start of the buffered window of data.
- size_t file_offset() const {
- return data_.empty() ? end_offset_ : data_.front().file_offset;
- }
-
- // Adds a `TraceBlobView` at the back.
- void PushBack(TraceBlobView view);
-
- // Shrinks the buffer by dropping data from the front of the buffer until the
- // given offset is reached. If not enough data is present as much data as
- // possible will be dropped and `false` will be returned.
- // ATTENTION: If `offset` < 'file_offset()' (i.e. you try to access data
- // previously popped) this method will CHECK fail.
- bool PopFrontUntil(size_t offset);
-
- // Shrinks the buffer by dropping `bytes` from the front of the buffer. If not
- // enough data is present as much data as possible will be dropped and `false`
- // will be returned.
- bool PopFrontBytes(size_t bytes) {
- return PopFrontUntil(file_offset() + bytes);
- }
-
- // Similar to `TraceBlobView::slice_off`, creates a slice with data starting
- // at `offset` and of the given `length`. This method might need to allocate a
- // new buffer and copy data into it (if the requested data spans multiple
- // TraceBlobView instances). If not enough data is present `std::nullopt` is
- // returned.
- //
- // ATTENTION: If `offset` < 'file_offset()' (i.e. you try to access data
- // previously popped) this method will CHECK fail.
- std::optional<TraceBlobView> SliceOff(size_t offset, size_t length) const;
-
- private:
- struct Entry {
- // File offset of the first byte in `data`.
- size_t file_offset;
- TraceBlobView data;
- };
- using Iterator = base::CircularQueue<Entry>::Iterator;
- // Finds the `TraceBlobView` at `offset` and returns a slice starting at that
- // offset and spanning the rest of the `TraceBlobView`. It also returns an
- // iterator to the next `TraceBlobView` instance (which might be `end()`).
- Iterator FindEntryWithOffset(size_t offset) const;
-
- Iterator end() const { return data_.end(); }
-
- // CircularQueue has no const_iterator, so mutable is needed to access it from
- // const methods.
- // CircularQueue has no const_iterator, so mutable is needed to access it from
- // const methods.
- mutable base::CircularQueue<Entry> data_;
- size_t end_offset_ = 0;
-};
-
-} // namespace perfetto::trace_processor::util
-
-#endif // SRC_TRACE_PROCESSOR_UTIL_FILE_BUFFER_H_
diff --git a/src/trace_processor/util/trace_blob_view_reader.cc b/src/trace_processor/util/trace_blob_view_reader.cc
new file mode 100644
index 0000000..4ba5b13
--- /dev/null
+++ b/src/trace_processor/util/trace_blob_view_reader.cc
@@ -0,0 +1,122 @@
+
+/*
+ * Copyright (C) 2024 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/util/trace_blob_view_reader.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <optional>
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/public/compiler.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+
+namespace perfetto::trace_processor::util {
+
+void TraceBlobViewReader::PushBack(TraceBlobView data) {
+ if (data.size() == 0) {
+ return;
+ }
+ const size_t size = data.size();
+ data_.emplace_back(Entry{end_offset_, std::move(data)});
+ end_offset_ += size;
+}
+
+bool TraceBlobViewReader::PopFrontUntil(const size_t target_offset) {
+ PERFETTO_CHECK(start_offset() <= target_offset);
+ while (!data_.empty()) {
+ Entry& entry = data_.front();
+ if (target_offset == entry.start_offset) {
+ return true;
+ }
+ const size_t bytes_to_pop = target_offset - entry.start_offset;
+ if (entry.data.size() > bytes_to_pop) {
+ entry.data =
+ entry.data.slice_off(bytes_to_pop, entry.data.size() - bytes_to_pop);
+ entry.start_offset += bytes_to_pop;
+ return true;
+ }
+ data_.pop_front();
+ }
+ return target_offset == end_offset_;
+}
+
+std::optional<TraceBlobView> TraceBlobViewReader::SliceOff(
+ size_t offset,
+ size_t length) const {
+ PERFETTO_DCHECK(offset >= start_offset());
+
+ // Fast path: the slice fits entirely inside the first TBV, we can just slice
+ // that directly without doing any searching. This will happen most of the
+ // time when this class is used so optimize for it.
+ bool is_fast_path =
+ !data_.empty() &&
+ offset + length <= data_.front().start_offset + data_.front().data.size();
+ if (PERFETTO_LIKELY(is_fast_path)) {
+ return data_.front().data.slice_off(offset - data_.front().start_offset,
+ length);
+ }
+
+ // If the length is zero, then a zero-sized blob view is always approrpriate.
+ if (PERFETTO_UNLIKELY(length == 0)) {
+ return TraceBlobView();
+ }
+
+ // If we don't have any TBVs or the end of the slice does not fit, then we
+ // cannot possibly return a full slice.
+ if (PERFETTO_UNLIKELY(data_.empty() || offset + length > end_offset_)) {
+ return std::nullopt;
+ }
+
+ // Find the first block finishes *after* start_offset i.e. there is at least
+ // one byte in that block which will end up in the slice. We know this *must*
+ // exist because of the above check.
+ auto rit = std::upper_bound(
+ data_.begin(), data_.end(), offset, [](size_t offset, const Entry& rhs) {
+ return offset < rhs.start_offset + rhs.data.size();
+ });
+ PERFETTO_CHECK(rit != data_.end());
+
+ // If the slice fits entirely in the block we found, then just slice that
+ // block avoiding any copies.
+ size_t rel_off = offset - rit->start_offset;
+ if (rel_off + length <= rit->data.size()) {
+ return rit->data.slice_off(rel_off, length);
+ }
+
+ // Otherwise, allocate some memory and make a copy.
+ auto buffer = TraceBlob::Allocate(length);
+ uint8_t* ptr = buffer.data();
+ uint8_t* end = buffer.data() + buffer.size();
+
+ // Copy all bytes in this block which overlap with the slice.
+ memcpy(ptr, rit->data.data() + rel_off, rit->data.length() - rel_off);
+ ptr += rit->data.length() - rel_off;
+
+ for (auto it = rit + 1; ptr != end; ++it) {
+ auto len = std::min(static_cast<size_t>(end - ptr), it->data.size());
+ memcpy(ptr, it->data.data(), len);
+ ptr += len;
+ }
+ return TraceBlobView(std::move(buffer));
+}
+
+} // namespace perfetto::trace_processor::util
diff --git a/src/trace_processor/util/trace_blob_view_reader.h b/src/trace_processor/util/trace_blob_view_reader.h
new file mode 100644
index 0000000..c39ffab
--- /dev/null
+++ b/src/trace_processor/util/trace_blob_view_reader.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 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_READER_H_
+#define SRC_TRACE_PROCESSOR_UTIL_TRACE_BLOB_VIEW_READER_H_
+
+#include <cstddef>
+#include <optional>
+
+#include "perfetto/ext/base/circular_queue.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+
+namespace perfetto::trace_processor::util {
+
+// Helper class which handles all the complexity of reading pieces of data which
+// span across multiple TraceBlobView chunks. It takes care of:
+// 1) Buffering data until it can be read.
+// 2) Stitching together the cross-chunk spanning pieces.
+// 3) Dropping data when it is no longer necessary to be buffered.
+class TraceBlobViewReader {
+ public:
+ // Adds a `TraceBlobView` at the back.
+ void PushBack(TraceBlobView);
+
+ // Shrinks the buffer by dropping data from the front of the buffer until the
+ // given offset is reached. If not enough data is present as much data as
+ // possible will be dropped and `false` will be returned.
+ //
+ // NOTE: If `offset` < 'file_offset()' this method will CHECK fail.
+ bool PopFrontUntil(size_t offset);
+
+ // Shrinks the buffer by dropping `bytes` from the front of the buffer. If not
+ // enough data is present as much data as possible will be dropped and `false`
+ // will be returned.
+ bool PopFrontBytes(size_t bytes) {
+ return PopFrontUntil(start_offset() + bytes);
+ }
+
+ // Creates a TraceBlobView by slicing this reader starting at |offset| and
+ // spanning |length| bytes.
+ //
+ // If possible, this method will try to avoid copies and simply slice an
+ // input TraceBlobView. However, that may not be possible and it so, it will
+ // allocate a new chunk of memory and copy over the data instead.
+ //
+ // NOTE: If `offset` < 'file_offset()' this method will CHECK fail.
+ std::optional<TraceBlobView> SliceOff(size_t offset, size_t length) const;
+
+ // Returns the offset to the start of the available data.
+ size_t start_offset() const {
+ return data_.empty() ? end_offset_ : data_.front().start_offset;
+ }
+
+ // Returns the offset to the end of the available data.
+ size_t end_offset() const { return end_offset_; }
+
+ // Returns the number of bytes of buffered data.
+ size_t avail() const { return end_offset() - start_offset(); }
+
+ bool empty() const { return data_.empty(); }
+
+ private:
+ struct Entry {
+ // File offset of the first byte in `data`.
+ size_t start_offset;
+ TraceBlobView data;
+ };
+ using Iterator = base::CircularQueue<Entry>::Iterator;
+
+ // CircularQueue has no const_iterator, so mutable is needed to access it from
+ // const methods.
+ mutable base::CircularQueue<Entry> data_;
+ size_t end_offset_ = 0;
+};
+
+} // namespace perfetto::trace_processor::util
+
+#endif // SRC_TRACE_PROCESSOR_UTIL_TRACE_BLOB_VIEW_READER_H_
diff --git a/src/trace_processor/util/file_buffer_unittest.cc b/src/trace_processor/util/trace_blob_view_reader_unittest.cc
similarity index 86%
rename from src/trace_processor/util/file_buffer_unittest.cc
rename to src/trace_processor/util/trace_blob_view_reader_unittest.cc
index 1518fe4..583648f 100644
--- a/src/trace_processor/util/file_buffer_unittest.cc
+++ b/src/trace_processor/util/trace_blob_view_reader_unittest.cc
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-#include "src/trace_processor/util/file_buffer.h"
+#include "src/trace_processor/util/trace_blob_view_reader.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
+#include <optional>
+#include <ostream>
#include <vector>
#include "perfetto/trace_processor/trace_blob.h"
@@ -92,19 +94,21 @@
return chunks;
}
-FileBuffer CreateFileBuffer(const std::vector<TraceBlobView>& chunks) {
- FileBuffer chunked_buffer;
+TraceBlobViewReader CreateTraceBlobViewReader(
+ const std::vector<TraceBlobView>& chunks) {
+ TraceBlobViewReader chunked_buffer;
for (const auto& chunk : chunks) {
chunked_buffer.PushBack(chunk.copy());
}
return chunked_buffer;
}
-TEST(FileBuffer, ContiguousAccessAtOffset) {
+TEST(TraceBlobViewReader, ContiguousAccessAtOffset) {
constexpr size_t kExpectedSize = 256;
constexpr size_t kChunkSize = kExpectedSize / 4;
TraceBlobView expected_data = CreateExpectedData(kExpectedSize);
- FileBuffer buffer = CreateFileBuffer(Slice(expected_data, kChunkSize));
+ TraceBlobViewReader buffer =
+ CreateTraceBlobViewReader(Slice(expected_data, kChunkSize));
for (size_t file_offset = 0; file_offset <= kExpectedSize; ++file_offset) {
EXPECT_TRUE(buffer.PopFrontUntil(file_offset));
@@ -116,12 +120,12 @@
}
}
-TEST(FileBuffer, NoCopyIfDataIsContiguous) {
+TEST(TraceBlobViewReader, NoCopyIfDataIsContiguous) {
constexpr size_t kExpectedSize = 256;
constexpr size_t kChunkSize = kExpectedSize / 4;
std::vector<TraceBlobView> chunks =
Slice(CreateExpectedData(kExpectedSize), kChunkSize);
- FileBuffer buffer = CreateFileBuffer(chunks);
+ TraceBlobViewReader buffer = CreateTraceBlobViewReader(chunks);
for (size_t i = 0; i < chunks.size(); ++i) {
for (size_t off = 0; off < kChunkSize; ++off) {
@@ -133,17 +137,18 @@
}
}
-TEST(FileBuffer, PopRemovesData) {
+TEST(TraceBlobViewReader, PopRemovesData) {
size_t expected_size = 256;
size_t expected_file_offset = 0;
const size_t kChunkSize = expected_size / 4;
TraceBlobView expected_data = CreateExpectedData(expected_size);
- FileBuffer buffer = CreateFileBuffer(Slice(expected_data, kChunkSize));
+ TraceBlobViewReader buffer =
+ CreateTraceBlobViewReader(Slice(expected_data, kChunkSize));
--expected_size;
++expected_file_offset;
buffer.PopFrontUntil(expected_file_offset);
- EXPECT_THAT(buffer.file_offset(), Eq(expected_file_offset));
+ EXPECT_THAT(buffer.start_offset(), Eq(expected_file_offset));
EXPECT_THAT(buffer.SliceOff(expected_file_offset, expected_size),
Optional(SameDataAs(expected_data.slice_off(
expected_data.size() - expected_size, expected_size))));
@@ -151,7 +156,7 @@
expected_size -= kChunkSize;
expected_file_offset += kChunkSize;
buffer.PopFrontUntil(expected_file_offset);
- EXPECT_THAT(buffer.file_offset(), Eq(expected_file_offset));
+ EXPECT_THAT(buffer.start_offset(), Eq(expected_file_offset));
EXPECT_THAT(buffer.SliceOff(expected_file_offset, expected_size),
Optional(SameDataAs(expected_data.slice_off(
expected_data.size() - expected_size, expected_size))));