blob: 1b51e563c53996ccc4cb9e1bd9ed0a9d6fa87e35 [file] [log] [blame]
Primiano Tuccid933d912018-09-04 09:15:07 +01001/*
Lalit Magantieb63b082020-09-10 14:12:20 +01002 * Copyright (C) 2020 The Android Open Source Project
Primiano Tuccid933d912018-09-04 09:15:07 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Eric Seckler137a4672019-10-24 08:51:14 +010017#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_TRACE_TOKENIZER_H_
18#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_TRACE_TOKENIZER_H_
Primiano Tuccid933d912018-09-04 09:15:07 +010019
Primiano Tuccid933d912018-09-04 09:15:07 +010020#include <vector>
21
Lalit Magantieb63b082020-09-10 14:12:20 +010022#include "perfetto/protozero/proto_utils.h"
23#include "perfetto/trace_processor/status.h"
Primiano Tucci3264b592021-11-08 18:20:51 +000024#include "perfetto/trace_processor/trace_blob.h"
25#include "perfetto/trace_processor/trace_blob_view.h"
Lalit Maganti69216ec2021-05-21 14:10:42 +010026#include "src/trace_processor/util/gzip_utils.h"
Lalit Magantieb63b082020-09-10 14:12:20 +010027#include "src/trace_processor/util/status_macros.h"
Eric Seckler684a4f72019-04-26 14:34:07 +010028
Lalit Magantieb63b082020-09-10 14:12:20 +010029#include "protos/perfetto/trace/trace.pbzero.h"
30#include "protos/perfetto/trace/trace_packet.pbzero.h"
Primiano Tucci6756fb02019-08-14 15:49:18 +020031
Primiano Tuccid933d912018-09-04 09:15:07 +010032namespace perfetto {
33namespace trace_processor {
34
Primiano Tuccid933d912018-09-04 09:15:07 +010035// Reads a protobuf trace in chunks and extracts boundaries of trace packets
36// (or subfields, for the case of ftrace) with their timestamps.
Lalit Magantieb63b082020-09-10 14:12:20 +010037class ProtoTraceTokenizer {
Primiano Tuccid933d912018-09-04 09:15:07 +010038 public:
Lalit Maganti1caf3492020-09-10 21:00:08 +010039 ProtoTraceTokenizer();
40
Lalit Magantieb63b082020-09-10 14:12:20 +010041 template <typename Callback = util::Status(TraceBlobView)>
Primiano Tucci3264b592021-11-08 18:20:51 +000042 util::Status Tokenize(TraceBlobView blob, Callback callback) {
43 const uint8_t* data = blob.data();
44 size_t size = blob.size();
Lalit Magantieb63b082020-09-10 14:12:20 +010045 if (!partial_buf_.empty()) {
46 // It takes ~5 bytes for a proto preamble + the varint size.
47 const size_t kHeaderBytes = 5;
48 if (PERFETTO_UNLIKELY(partial_buf_.size() < kHeaderBytes)) {
49 size_t missing_len = std::min(kHeaderBytes - partial_buf_.size(), size);
50 partial_buf_.insert(partial_buf_.end(), &data[0], &data[missing_len]);
51 if (partial_buf_.size() < kHeaderBytes)
52 return util::OkStatus();
53 data += missing_len;
54 size -= missing_len;
55 }
Primiano Tuccid933d912018-09-04 09:15:07 +010056
Lalit Magantieb63b082020-09-10 14:12:20 +010057 // At this point we have enough data in |partial_buf_| to read at least
58 // the field header and know the size of the next TracePacket.
59 const uint8_t* pos = &partial_buf_[0];
60 uint8_t proto_field_tag = *pos;
61 uint64_t field_size = 0;
Florian Mayer82c68c62020-12-09 17:54:07 +000062 // We cannot do &partial_buf_[partial_buf_.size()] because that crashes
63 // on MSVC STL debug builds, so does &*partial_buf_.end().
Lalit Magantieb63b082020-09-10 14:12:20 +010064 const uint8_t* next = protozero::proto_utils::ParseVarInt(
Florian Mayer82c68c62020-12-09 17:54:07 +000065 ++pos, &partial_buf_.front() + partial_buf_.size(), &field_size);
Lalit Magantieb63b082020-09-10 14:12:20 +010066 bool parse_failed = next == pos;
67 pos = next;
68 if (proto_field_tag != kTracePacketTag || field_size == 0 ||
69 parse_failed) {
70 return util::ErrStatus(
71 "Failed parsing a TracePacket from the partial buffer");
72 }
73
74 // At this point we know how big the TracePacket is.
75 size_t hdr_size = static_cast<size_t>(pos - &partial_buf_[0]);
76 size_t size_incl_header = static_cast<size_t>(field_size + hdr_size);
77 PERFETTO_DCHECK(size_incl_header > partial_buf_.size());
78
79 // There is a good chance that between the |partial_buf_| and the new
80 // |data| of the current call we have enough bytes to parse a TracePacket.
81 if (partial_buf_.size() + size >= size_incl_header) {
82 // Create a new buffer for the whole TracePacket and copy into that:
83 // 1) The beginning of the TracePacket (including the proto header) from
84 // the partial buffer.
85 // 2) The rest of the TracePacket from the current |data| buffer (note
86 // that we might have consumed already a few bytes form |data|
87 // earlier in this function, hence we need to keep |off| into
88 // account).
Primiano Tucci3264b592021-11-08 18:20:51 +000089 TraceBlob glued = TraceBlob::Allocate(size_incl_header);
90 memcpy(glued.data(), partial_buf_.data(), partial_buf_.size());
Lalit Magantieb63b082020-09-10 14:12:20 +010091 // |size_missing| is the number of bytes for the rest of the TracePacket
92 // in |data|.
93 size_t size_missing = size_incl_header - partial_buf_.size();
Primiano Tucci3264b592021-11-08 18:20:51 +000094 memcpy(glued.data() + partial_buf_.size(), &data[0], size_missing);
Lalit Magantieb63b082020-09-10 14:12:20 +010095 data += size_missing;
96 size -= size_missing;
97 partial_buf_.clear();
Primiano Tucci3264b592021-11-08 18:20:51 +000098 RETURN_IF_ERROR(
99 ParseInternal(TraceBlobView(std::move(glued)), callback));
Lalit Magantieb63b082020-09-10 14:12:20 +0100100 } else {
101 partial_buf_.insert(partial_buf_.end(), data, &data[size]);
102 return util::OkStatus();
103 }
104 }
Primiano Tucci3264b592021-11-08 18:20:51 +0000105 return ParseInternal(blob.slice(data, size), callback);
Lalit Magantieb63b082020-09-10 14:12:20 +0100106 }
Primiano Tuccid933d912018-09-04 09:15:07 +0100107
108 private:
Lalit Magantieb63b082020-09-10 14:12:20 +0100109 static constexpr uint8_t kTracePacketTag =
110 protozero::proto_utils::MakeTagLengthDelimited(
111 protos::pbzero::Trace::kPacketFieldNumber);
112
113 template <typename Callback = util::Status(TraceBlobView)>
Primiano Tucci3264b592021-11-08 18:20:51 +0000114 util::Status ParseInternal(TraceBlobView whole_buf, Callback callback) {
115 const uint8_t* const start = whole_buf.data();
116 protos::pbzero::Trace::Decoder decoder(whole_buf.data(), whole_buf.size());
Lalit Magantieb63b082020-09-10 14:12:20 +0100117 for (auto it = decoder.packet(); it; ++it) {
118 protozero::ConstBytes packet = *it;
Primiano Tucci3264b592021-11-08 18:20:51 +0000119 TraceBlobView sliced = whole_buf.slice(packet.data, packet.size);
Lalit Magantieb63b082020-09-10 14:12:20 +0100120 RETURN_IF_ERROR(ParsePacket(std::move(sliced), callback));
121 }
122
123 const size_t bytes_left = decoder.bytes_left();
124 if (bytes_left > 0) {
125 PERFETTO_DCHECK(partial_buf_.empty());
Primiano Tucci3264b592021-11-08 18:20:51 +0000126 partial_buf_.insert(partial_buf_.end(), &start[decoder.read_offset()],
127 &start[decoder.read_offset() + bytes_left]);
Lalit Magantieb63b082020-09-10 14:12:20 +0100128 }
129 return util::OkStatus();
130 }
131
132 template <typename Callback = util::Status(TraceBlobView)>
133 util::Status ParsePacket(TraceBlobView packet, Callback callback) {
134 protos::pbzero::TracePacket::Decoder decoder(packet.data(),
135 packet.length());
136 if (decoder.has_compressed_packets()) {
Lalit Maganti69216ec2021-05-21 14:10:42 +0100137 if (!util::IsGzipSupported()) {
Lalit Magantieb63b082020-09-10 14:12:20 +0100138 return util::Status(
139 "Cannot decode compressed packets. Zlib not enabled");
140 }
141
142 protozero::ConstBytes field = decoder.compressed_packets();
Primiano Tucci3264b592021-11-08 18:20:51 +0000143 TraceBlobView compressed_packets = packet.slice(field.data, field.size);
144 TraceBlobView packets;
Lalit Magantieb63b082020-09-10 14:12:20 +0100145
146 RETURN_IF_ERROR(Decompress(std::move(compressed_packets), &packets));
147
148 const uint8_t* start = packets.data();
149 const uint8_t* end = packets.data() + packets.length();
150 const uint8_t* ptr = start;
151 while ((end - ptr) > 2) {
Primiano Tucci3264b592021-11-08 18:20:51 +0000152 const uint8_t* packet_outer = ptr;
Lalit Magantieb63b082020-09-10 14:12:20 +0100153 if (PERFETTO_UNLIKELY(*ptr != kTracePacketTag))
154 return util::ErrStatus("Expected TracePacket tag");
155 uint64_t packet_size = 0;
156 ptr = protozero::proto_utils::ParseVarInt(++ptr, end, &packet_size);
Primiano Tucci3264b592021-11-08 18:20:51 +0000157 const uint8_t* packet_start = ptr;
Lalit Magantieb63b082020-09-10 14:12:20 +0100158 ptr += packet_size;
Primiano Tucci3264b592021-11-08 18:20:51 +0000159 if (PERFETTO_UNLIKELY((ptr - packet_outer) < 2 || ptr > end))
Lalit Magantieb63b082020-09-10 14:12:20 +0100160 return util::ErrStatus("Invalid packet size");
161
162 TraceBlobView sliced =
Primiano Tucci3264b592021-11-08 18:20:51 +0000163 packets.slice(packet_start, static_cast<size_t>(packet_size));
Lalit Magantieb63b082020-09-10 14:12:20 +0100164 RETURN_IF_ERROR(ParsePacket(std::move(sliced), callback));
165 }
166 return util::OkStatus();
167 }
168 return callback(std::move(packet));
169 }
170
171 util::Status Decompress(TraceBlobView input, TraceBlobView* output);
Primiano Tuccid933d912018-09-04 09:15:07 +0100172
173 // Used to glue together trace packets that span across two (or more)
174 // Parse() boundaries.
175 std::vector<uint8_t> partial_buf_;
176
Lalit Maganti9d538bd2020-03-12 23:48:16 +0000177 // Allows support for compressed trace packets.
Lalit Maganti69216ec2021-05-21 14:10:42 +0100178 util::GzipDecompressor decompressor_;
Primiano Tuccid933d912018-09-04 09:15:07 +0100179};
180
181} // namespace trace_processor
182} // namespace perfetto
183
Eric Seckler137a4672019-10-24 08:51:14 +0100184#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_TRACE_TOKENIZER_H_