1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
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
17 #include "src/tracing/core/trace_buffer.h"
18
19 #include <limits>
20
21 #include "perfetto/base/logging.h"
22 #include "perfetto/ext/base/utils.h"
23 #include "perfetto/ext/tracing/core/shared_memory_abi.h"
24 #include "perfetto/ext/tracing/core/trace_packet.h"
25 #include "perfetto/protozero/proto_utils.h"
26
27 #define TRACE_BUFFER_VERBOSE_LOGGING() 0 // Set to 1 when debugging unittests.
28 #if TRACE_BUFFER_VERBOSE_LOGGING()
29 #define TRACE_BUFFER_DLOG PERFETTO_DLOG
30 #else
31 #define TRACE_BUFFER_DLOG(...) void()
32 #endif
33
34 namespace perfetto {
35
36 namespace {
37 constexpr uint8_t kFirstPacketContinuesFromPrevChunk =
38 SharedMemoryABI::ChunkHeader::kFirstPacketContinuesFromPrevChunk;
39 constexpr uint8_t kLastPacketContinuesOnNextChunk =
40 SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk;
41 constexpr uint8_t kChunkNeedsPatching =
42 SharedMemoryABI::ChunkHeader::kChunkNeedsPatching;
43 } // namespace.
44
45 const size_t TraceBuffer::InlineChunkHeaderSize = sizeof(ChunkRecord);
46
47 // static
Create(size_t size_in_bytes,OverwritePolicy pol)48 std::unique_ptr<TraceBuffer> TraceBuffer::Create(size_t size_in_bytes,
49 OverwritePolicy pol) {
50 std::unique_ptr<TraceBuffer> trace_buffer(new TraceBuffer(pol));
51 if (!trace_buffer->Initialize(size_in_bytes))
52 return nullptr;
53 return trace_buffer;
54 }
55
TraceBuffer(OverwritePolicy pol)56 TraceBuffer::TraceBuffer(OverwritePolicy pol) : overwrite_policy_(pol) {
57 // See comments in ChunkRecord for the rationale of this.
58 static_assert(sizeof(ChunkRecord) == sizeof(SharedMemoryABI::PageHeader) +
59 sizeof(SharedMemoryABI::ChunkHeader),
60 "ChunkRecord out of sync with the layout of SharedMemoryABI");
61 }
62
63 TraceBuffer::~TraceBuffer() = default;
64
Initialize(size_t size)65 bool TraceBuffer::Initialize(size_t size) {
66 static_assert(
67 SharedMemoryABI::kMinPageSize % sizeof(ChunkRecord) == 0,
68 "sizeof(ChunkRecord) must be an integer divider of a page size");
69 auto max_size = std::numeric_limits<decltype(ChunkMeta::record_off)>::max();
70 PERFETTO_CHECK(size <= static_cast<size_t>(max_size));
71 data_ = base::PagedMemory::Allocate(
72 size, base::PagedMemory::kMayFail | base::PagedMemory::kDontCommit);
73 if (!data_.IsValid()) {
74 PERFETTO_ELOG("Trace buffer allocation failed (size: %zu)", size);
75 return false;
76 }
77 size_ = size;
78 stats_.set_buffer_size(size);
79 max_chunk_size_ = std::min(size, ChunkRecord::kMaxSize);
80 wptr_ = begin();
81 index_.clear();
82 last_chunk_id_written_.clear();
83 read_iter_ = GetReadIterForSequence(index_.end());
84 return true;
85 }
86
87 // Note: |src| points to a shmem region that is shared with the producer. Assume
88 // that the producer is malicious and will change the content of |src|
89 // while we execute here. Don't do any processing on it other than memcpy().
CopyChunkUntrusted(ProducerID producer_id_trusted,uid_t producer_uid_trusted,pid_t producer_pid_trusted,WriterID writer_id,ChunkID chunk_id,uint16_t num_fragments,uint8_t chunk_flags,bool chunk_complete,const uint8_t * src,size_t size)90 void TraceBuffer::CopyChunkUntrusted(ProducerID producer_id_trusted,
91 uid_t producer_uid_trusted,
92 pid_t producer_pid_trusted,
93 WriterID writer_id,
94 ChunkID chunk_id,
95 uint16_t num_fragments,
96 uint8_t chunk_flags,
97 bool chunk_complete,
98 const uint8_t* src,
99 size_t size) {
100 PERFETTO_CHECK(!read_only_);
101
102 // |record_size| = |size| + sizeof(ChunkRecord), rounded up to avoid to end
103 // up in a fragmented state where size_to_end() < sizeof(ChunkRecord).
104 const size_t record_size =
105 base::AlignUp<sizeof(ChunkRecord)>(size + sizeof(ChunkRecord));
106 TRACE_BUFFER_DLOG("CopyChunk @ %" PRIdPTR ", size=%zu", wptr_ - begin(), record_size);
107 if (PERFETTO_UNLIKELY(record_size > max_chunk_size_)) {
108 stats_.set_abi_violations(stats_.abi_violations() + 1);
109 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
110 return;
111 }
112
113 #if PERFETTO_DCHECK_IS_ON()
114 changed_since_last_read_ = true;
115 #endif
116
117 // If the chunk hasn't been completed, we should only consider the first
118 // |num_fragments - 1| packets complete. For simplicity, we simply disregard
119 // the last one when we copy the chunk.
120 if (PERFETTO_UNLIKELY(!chunk_complete)) {
121 if (num_fragments > 0) {
122 num_fragments--;
123 // These flags should only affect the last packet in the chunk. We clear
124 // them, so that TraceBuffer is able to look at the remaining packets in
125 // this chunk.
126 chunk_flags &= ~kLastPacketContinuesOnNextChunk;
127 chunk_flags &= ~kChunkNeedsPatching;
128 }
129 }
130
131 ChunkRecord record(record_size);
132 record.producer_id = producer_id_trusted;
133 record.chunk_id = chunk_id;
134 record.writer_id = writer_id;
135 record.num_fragments = num_fragments;
136 record.flags = chunk_flags;
137 ChunkMeta::Key key(record);
138
139 // Check whether we have already copied the same chunk previously. This may
140 // happen if the service scrapes chunks in a potentially incomplete state
141 // before receiving commit requests for them from the producer. Note that the
142 // service may scrape and thus override chunks in arbitrary order since the
143 // chunks aren't ordered in the SMB.
144 const auto it = index_.find(key);
145 if (PERFETTO_UNLIKELY(it != index_.end())) {
146 ChunkMeta* record_meta = &it->second;
147 ChunkRecord* prev = GetChunkRecordAt(begin() + record_meta->record_off);
148
149 // Verify that the old chunk's metadata corresponds to the new one.
150 // Overridden chunks should never change size, since the page layout is
151 // fixed per writer. The number of fragments should also never decrease and
152 // flags should not be removed.
153 if (PERFETTO_UNLIKELY(ChunkMeta::Key(*prev) != key ||
154 prev->size != record_size ||
155 prev->num_fragments > num_fragments ||
156 (prev->flags & chunk_flags) != prev->flags)) {
157 stats_.set_abi_violations(stats_.abi_violations() + 1);
158 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
159 return;
160 }
161
162 // If this chunk was previously copied with the same number of fragments and
163 // the number didn't change, there's no need to copy it again. If the
164 // previous chunk was complete already, this should always be the case.
165 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_ ||
166 !record_meta->is_complete() ||
167 (chunk_complete && prev->num_fragments == num_fragments));
168 if (prev->num_fragments == num_fragments) {
169 TRACE_BUFFER_DLOG(" skipping recommit of identical chunk");
170 return;
171 }
172
173 // If we've already started reading from chunk N+1 following this chunk N,
174 // don't override chunk N. Otherwise we may end up reading a packet from
175 // chunk N after having read from chunk N+1, thereby violating sequential
176 // read of packets. This shouldn't happen if the producer is well-behaved,
177 // because it shouldn't start chunk N+1 before completing chunk N.
178 ChunkMeta::Key subsequent_key = key;
179 static_assert(std::numeric_limits<ChunkID>::max() == kMaxChunkID,
180 "ChunkID wraps");
181 subsequent_key.chunk_id++;
182 const auto subsequent_it = index_.find(subsequent_key);
183 if (subsequent_it != index_.end() &&
184 subsequent_it->second.num_fragments_read > 0) {
185 stats_.set_abi_violations(stats_.abi_violations() + 1);
186 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
187 return;
188 }
189
190 // We should not have read past the last packet.
191 if (record_meta->num_fragments_read > prev->num_fragments) {
192 PERFETTO_ELOG(
193 "TraceBuffer read too many fragments from an incomplete chunk");
194 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
195 return;
196 }
197
198 uint8_t* wptr = reinterpret_cast<uint8_t*>(prev);
199 TRACE_BUFFER_DLOG(" overriding chunk @ %" PRIdPTR ", size=%zu", wptr - begin(),
200 record_size);
201
202 // Update chunk meta data stored in the index, as it may have changed.
203 record_meta->num_fragments = num_fragments;
204 record_meta->flags = chunk_flags;
205 record_meta->set_complete(chunk_complete);
206
207 // Override the ChunkRecord contents at the original |wptr|.
208 TRACE_BUFFER_DLOG(" copying @ [%" PRIdPTR " - %" PRIdPTR "] %zu", wptr - begin(),
209 uintptr_t(wptr - begin()) + record_size, record_size);
210 WriteChunkRecord(wptr, record, src, size);
211 TRACE_BUFFER_DLOG("Chunk raw: %s",
212 base::HexDump(wptr, record_size).c_str());
213 stats_.set_chunks_rewritten(stats_.chunks_rewritten() + 1);
214 return;
215 }
216
217 if (PERFETTO_UNLIKELY(discard_writes_))
218 return DiscardWrite();
219
220 // If there isn't enough room from the given write position. Write a padding
221 // record to clear the end of the buffer and wrap back.
222 const size_t cached_size_to_end = size_to_end();
223 if (PERFETTO_UNLIKELY(record_size > cached_size_to_end)) {
224 ssize_t res = DeleteNextChunksFor(cached_size_to_end);
225 if (res == -1)
226 return DiscardWrite();
227 PERFETTO_DCHECK(static_cast<size_t>(res) <= cached_size_to_end);
228 AddPaddingRecord(cached_size_to_end);
229 wptr_ = begin();
230 stats_.set_write_wrap_count(stats_.write_wrap_count() + 1);
231 PERFETTO_DCHECK(size_to_end() >= record_size);
232 }
233
234 // At this point either |wptr_| points to an untouched part of the buffer
235 // (i.e. *wptr_ == 0) or we are about to overwrite one or more ChunkRecord(s).
236 // In the latter case we need to first figure out where the next valid
237 // ChunkRecord is (if it exists) and add padding between the new record.
238 // Example ((w) == write cursor):
239 //
240 // Initial state (wtpr_ == 0):
241 // |0 (w) |10 |30 |50
242 // +---------+-----------------+--------------------+--------------------+
243 // | Chunk 1 | Chunk 2 | Chunk 3 | Chunk 4 |
244 // +---------+-----------------+--------------------+--------------------+
245 //
246 // Let's assume we now want now write a 5th Chunk of size == 35. The final
247 // state should look like this:
248 // |0 |35 (w) |50
249 // +---------------------------------+---------------+--------------------+
250 // | Chunk 5 | Padding Chunk | Chunk 4 |
251 // +---------------------------------+---------------+--------------------+
252
253 // Deletes all chunks from |wptr_| to |wptr_| + |record_size|.
254 ssize_t del_res = DeleteNextChunksFor(record_size);
255 if (del_res == -1)
256 return DiscardWrite();
257 size_t padding_size = static_cast<size_t>(del_res);
258
259 // Now first insert the new chunk. At the end, if necessary, add the padding.
260 stats_.set_chunks_written(stats_.chunks_written() + 1);
261 stats_.set_bytes_written(stats_.bytes_written() + record_size);
262
263 uint32_t chunk_off = GetOffset(GetChunkRecordAt(wptr_));
264 auto it_and_inserted = index_.emplace(
265 key, ChunkMeta(chunk_off, num_fragments, chunk_complete, chunk_flags,
266 producer_uid_trusted, producer_pid_trusted));
267 PERFETTO_DCHECK(it_and_inserted.second);
268 TRACE_BUFFER_DLOG(" copying @ [%" PRIdPTR " - %" PRIdPTR "] %zu", wptr_ - begin(),
269 uintptr_t(wptr_ - begin()) + record_size, record_size);
270 WriteChunkRecord(wptr_, record, src, size);
271 TRACE_BUFFER_DLOG("Chunk raw: %s", base::HexDump(wptr_, record_size).c_str());
272 wptr_ += record_size;
273 if (wptr_ >= end()) {
274 PERFETTO_DCHECK(padding_size == 0);
275 wptr_ = begin();
276 stats_.set_write_wrap_count(stats_.write_wrap_count() + 1);
277 }
278 DcheckIsAlignedAndWithinBounds(wptr_);
279
280 // Chunks may be received out of order, so only update last_chunk_id if the
281 // new chunk_id is larger. But take into account overflows by only selecting
282 // the new ID if its distance to the latest ID is smaller than half the number
283 // space.
284 //
285 // This accounts for both the case where the new ID has just overflown and
286 // last_chunk_id be updated even though it's smaller (e.g. |chunk_id| = 1 and
287 // |last_chunk_id| = kMaxChunkId; chunk_id - last_chunk_id = 0) and the case
288 // where the new ID is an out-of-order ID right after an overflow and
289 // last_chunk_id shouldn't be updated even though it's larger (e.g. |chunk_id|
290 // = kMaxChunkId and |last_chunk_id| = 1; chunk_id - last_chunk_id =
291 // kMaxChunkId - 1).
292 auto producer_and_writer_id = std::make_pair(producer_id_trusted, writer_id);
293 ChunkID& last_chunk_id = last_chunk_id_written_[producer_and_writer_id];
294 static_assert(std::numeric_limits<ChunkID>::max() == kMaxChunkID,
295 "This code assumes that ChunkID wraps at kMaxChunkID");
296 if (chunk_id - last_chunk_id < kMaxChunkID / 2) {
297 last_chunk_id = chunk_id;
298 } else {
299 stats_.set_chunks_committed_out_of_order(
300 stats_.chunks_committed_out_of_order() + 1);
301 }
302
303 if (padding_size)
304 AddPaddingRecord(padding_size);
305 }
306
DeleteNextChunksFor(size_t bytes_to_clear)307 ssize_t TraceBuffer::DeleteNextChunksFor(size_t bytes_to_clear) {
308 PERFETTO_CHECK(!discard_writes_);
309
310 // Find the position of the first chunk which begins at or after
311 // (|wptr_| + |bytes|). Note that such a chunk might not exist and we might
312 // either reach the end of the buffer or a zeroed region of the buffer.
313 uint8_t* next_chunk_ptr = wptr_;
314 uint8_t* search_end = wptr_ + bytes_to_clear;
315 TRACE_BUFFER_DLOG("Delete [%zu %zu]", wptr_ - begin(), search_end - begin());
316 DcheckIsAlignedAndWithinBounds(wptr_);
317 PERFETTO_DCHECK(search_end <= end());
318 std::vector<ChunkMap::iterator> index_delete;
319 uint64_t chunks_overwritten = stats_.chunks_overwritten();
320 uint64_t bytes_overwritten = stats_.bytes_overwritten();
321 uint64_t padding_bytes_cleared = stats_.padding_bytes_cleared();
322 while (next_chunk_ptr < search_end) {
323 const ChunkRecord& next_chunk = *GetChunkRecordAt(next_chunk_ptr);
324 TRACE_BUFFER_DLOG(
325 " scanning chunk [%zu %zu] (valid=%d)", next_chunk_ptr - begin(),
326 next_chunk_ptr - begin() + next_chunk.size, next_chunk.is_valid());
327
328 // We just reached the untouched part of the buffer, it's going to be all
329 // zeroes from here to end().
330 // Optimization: if during Initialize() we fill the buffer with padding
331 // records we could get rid of this branch.
332 if (PERFETTO_UNLIKELY(!next_chunk.is_valid())) {
333 // This should happen only at the first iteration. The zeroed area can
334 // only begin precisely at the |wptr_|, not after. Otherwise it means that
335 // we wrapped but screwed up the ChunkRecord chain.
336 PERFETTO_DCHECK(next_chunk_ptr == wptr_);
337 return 0;
338 }
339
340 // Remove |next_chunk| from the index, unless it's a padding record (padding
341 // records are not part of the index).
342 if (PERFETTO_LIKELY(!next_chunk.is_padding)) {
343 ChunkMeta::Key key(next_chunk);
344 auto it = index_.find(key);
345 bool will_remove = false;
346 if (PERFETTO_LIKELY(it != index_.end())) {
347 const ChunkMeta& meta = it->second;
348 if (PERFETTO_UNLIKELY(meta.num_fragments_read < meta.num_fragments)) {
349 if (overwrite_policy_ == kDiscard)
350 return -1;
351 chunks_overwritten++;
352 bytes_overwritten += next_chunk.size;
353 }
354 index_delete.push_back(it);
355 will_remove = true;
356 }
357 TRACE_BUFFER_DLOG(
358 " del index {%" PRIu32 ",%" PRIu32 ",%u} @ [%" PRIdPTR " - %" PRIdPTR "] %d",
359 key.producer_id, key.writer_id, key.chunk_id,
360 next_chunk_ptr - begin(), next_chunk_ptr - begin() + next_chunk.size,
361 will_remove);
362 PERFETTO_DCHECK(will_remove);
363 } else {
364 padding_bytes_cleared += next_chunk.size;
365 }
366
367 next_chunk_ptr += next_chunk.size;
368
369 // We should never hit this, unless we managed to screw up while writing
370 // to the buffer and breaking the ChunkRecord(s) chain.
371 // TODO(primiano): Write more meaningful logging with the status of the
372 // buffer, to get more actionable bugs in case we hit this.
373 PERFETTO_CHECK(next_chunk_ptr <= end());
374 }
375
376 // Remove from the index.
377 for (auto it : index_delete) {
378 index_.erase(it);
379 }
380 stats_.set_chunks_overwritten(chunks_overwritten);
381 stats_.set_bytes_overwritten(bytes_overwritten);
382 stats_.set_padding_bytes_cleared(padding_bytes_cleared);
383
384 PERFETTO_DCHECK(next_chunk_ptr >= search_end && next_chunk_ptr <= end());
385 return static_cast<ssize_t>(next_chunk_ptr - search_end);
386 }
387
AddPaddingRecord(size_t size)388 void TraceBuffer::AddPaddingRecord(size_t size) {
389 PERFETTO_DCHECK(size >= sizeof(ChunkRecord) && size <= ChunkRecord::kMaxSize);
390 ChunkRecord record(size);
391 record.is_padding = 1;
392 TRACE_BUFFER_DLOG("AddPaddingRecord @ [%" PRIdPTR " - %" PRIdPTR "] %zu", wptr_ - begin(),
393 uintptr_t(wptr_ - begin()) + size, size);
394 WriteChunkRecord(wptr_, record, nullptr, size - sizeof(ChunkRecord));
395 stats_.set_padding_bytes_written(stats_.padding_bytes_written() + size);
396 // |wptr_| is deliberately not advanced when writing a padding record.
397 }
398
TryPatchChunkContents(ProducerID producer_id,WriterID writer_id,ChunkID chunk_id,const Patch * patches,size_t patches_size,bool other_patches_pending)399 bool TraceBuffer::TryPatchChunkContents(ProducerID producer_id,
400 WriterID writer_id,
401 ChunkID chunk_id,
402 const Patch* patches,
403 size_t patches_size,
404 bool other_patches_pending) {
405 PERFETTO_CHECK(!read_only_);
406 ChunkMeta::Key key(producer_id, writer_id, chunk_id);
407 auto it = index_.find(key);
408 if (it == index_.end()) {
409 stats_.set_patches_failed(stats_.patches_failed() + 1);
410 return false;
411 }
412 ChunkMeta& chunk_meta = it->second;
413
414 // Check that the index is consistent with the actual ProducerID/WriterID
415 // stored in the ChunkRecord.
416
417 ChunkRecord* chunk_record = GetChunkRecordAt(begin() + chunk_meta.record_off);
418 PERFETTO_DCHECK(ChunkMeta::Key(*chunk_record) == key);
419 uint8_t* chunk_begin = reinterpret_cast<uint8_t*>(chunk_record);
420 PERFETTO_DCHECK(chunk_begin >= begin());
421 uint8_t* chunk_end = chunk_begin + chunk_record->size;
422 PERFETTO_DCHECK(chunk_end <= end());
423
424 static_assert(Patch::kSize == SharedMemoryABI::kPacketHeaderSize,
425 "Patch::kSize out of sync with SharedMemoryABI");
426
427 for (size_t i = 0; i < patches_size; i++) {
428 uint8_t* ptr =
429 chunk_begin + sizeof(ChunkRecord) + patches[i].offset_untrusted;
430 TRACE_BUFFER_DLOG("PatchChunk {%" PRIu32 ",%" PRIu32
431 ",%u} size=%zu @ %zu with {%02x %02x %02x %02x} cur "
432 "{%02x %02x %02x %02x}",
433 producer_id, writer_id, chunk_id, chunk_end - chunk_begin,
434 patches[i].offset_untrusted, patches[i].data[0],
435 patches[i].data[1], patches[i].data[2],
436 patches[i].data[3], ptr[0], ptr[1], ptr[2], ptr[3]);
437 if (ptr < chunk_begin + sizeof(ChunkRecord) ||
438 ptr > chunk_end - Patch::kSize) {
439 // Either the IPC was so slow and in the meantime the writer managed to
440 // wrap over |chunk_id| or the producer sent a malicious IPC.
441 stats_.set_patches_failed(stats_.patches_failed() + 1);
442 return false;
443 }
444
445 memcpy(ptr, &patches[i].data[0], Patch::kSize);
446 }
447 TRACE_BUFFER_DLOG("Chunk raw (after patch): %s",
448 base::HexDump(chunk_begin, chunk_record->size).c_str());
449
450 stats_.set_patches_succeeded(stats_.patches_succeeded() + patches_size);
451 if (!other_patches_pending) {
452 chunk_meta.flags &= ~kChunkNeedsPatching;
453 chunk_record->flags = chunk_meta.flags;
454 }
455 return true;
456 }
457
BeginRead()458 void TraceBuffer::BeginRead() {
459 read_iter_ = GetReadIterForSequence(index_.begin());
460 #if PERFETTO_DCHECK_IS_ON()
461 changed_since_last_read_ = false;
462 #endif
463 }
464
GetReadIterForSequence(ChunkMap::iterator seq_begin)465 TraceBuffer::SequenceIterator TraceBuffer::GetReadIterForSequence(
466 ChunkMap::iterator seq_begin) {
467 SequenceIterator iter;
468 iter.seq_begin = seq_begin;
469 if (seq_begin == index_.end()) {
470 iter.cur = iter.seq_end = index_.end();
471 return iter;
472 }
473
474 #if PERFETTO_DCHECK_IS_ON()
475 // Either |seq_begin| is == index_.begin() or the item immediately before must
476 // belong to a different {ProducerID, WriterID} sequence.
477 if (seq_begin != index_.begin() && seq_begin != index_.end()) {
478 auto prev_it = seq_begin;
479 prev_it--;
480 PERFETTO_DCHECK(
481 seq_begin == index_.begin() ||
482 std::tie(prev_it->first.producer_id, prev_it->first.writer_id) <
483 std::tie(seq_begin->first.producer_id, seq_begin->first.writer_id));
484 }
485 #endif
486
487 // Find the first entry that has a greater {ProducerID, WriterID} (or just
488 // index_.end() if we reached the end).
489 ChunkMeta::Key key = seq_begin->first; // Deliberate copy.
490 key.chunk_id = kMaxChunkID;
491 iter.seq_end = index_.upper_bound(key);
492 PERFETTO_DCHECK(iter.seq_begin != iter.seq_end);
493
494 // Now find the first entry between [seq_begin, seq_end) that is
495 // > last_chunk_id_written_. This is where we the sequence will start (see
496 // notes about wrapping of IDs in the header).
497 auto producer_and_writer_id = std::make_pair(key.producer_id, key.writer_id);
498 PERFETTO_DCHECK(last_chunk_id_written_.count(producer_and_writer_id));
499 iter.wrapping_id = last_chunk_id_written_[producer_and_writer_id];
500 key.chunk_id = iter.wrapping_id;
501 iter.cur = index_.upper_bound(key);
502 if (iter.cur == iter.seq_end)
503 iter.cur = iter.seq_begin;
504 return iter;
505 }
506
MoveNext()507 void TraceBuffer::SequenceIterator::MoveNext() {
508 // Stop iterating when we reach the end of the sequence.
509 // Note: |seq_begin| might be == |seq_end|.
510 if (cur == seq_end || cur->first.chunk_id == wrapping_id) {
511 cur = seq_end;
512 return;
513 }
514
515 // If the current chunk wasn't completed yet, we shouldn't advance past it as
516 // it may be rewritten with additional packets.
517 if (!cur->second.is_complete()) {
518 cur = seq_end;
519 return;
520 }
521
522 ChunkID last_chunk_id = cur->first.chunk_id;
523 if (++cur == seq_end)
524 cur = seq_begin;
525
526 // There may be a missing chunk in the sequence of chunks, in which case the
527 // next chunk's ID won't follow the last one's. If so, skip the rest of the
528 // sequence. We'll return to it later once the hole is filled.
529 if (last_chunk_id + 1 != cur->first.chunk_id)
530 cur = seq_end;
531 }
532
ReadNextTracePacket(TracePacket * packet,PacketSequenceProperties * sequence_properties,bool * previous_packet_on_sequence_dropped)533 bool TraceBuffer::ReadNextTracePacket(
534 TracePacket* packet,
535 PacketSequenceProperties* sequence_properties,
536 bool* previous_packet_on_sequence_dropped) {
537 // Note: MoveNext() moves only within the next chunk within the same
538 // {ProducerID, WriterID} sequence. Here we want to:
539 // - return the next patched+complete packet in the current sequence, if any.
540 // - return the first patched+complete packet in the next sequence, if any.
541 // - return false if none of the above is found.
542 TRACE_BUFFER_DLOG("ReadNextTracePacket()");
543
544 // Just in case we forget to initialize these below.
545 *sequence_properties = {0, kInvalidUid, base::kInvalidPid, 0};
546 *previous_packet_on_sequence_dropped = false;
547
548 // At the start of each sequence iteration, we consider the last read packet
549 // dropped. While iterating over the chunks in the sequence, we update this
550 // flag based on our knowledge about the last packet that was read from each
551 // chunk (|last_read_packet_skipped| in ChunkMeta).
552 bool previous_packet_dropped = true;
553
554 #if PERFETTO_DCHECK_IS_ON()
555 PERFETTO_DCHECK(!changed_since_last_read_);
556 #endif
557 for (;; read_iter_.MoveNext()) {
558 if (PERFETTO_UNLIKELY(!read_iter_.is_valid())) {
559 // We ran out of chunks in the current {ProducerID, WriterID} sequence or
560 // we just reached the index_.end().
561
562 if (PERFETTO_UNLIKELY(read_iter_.seq_end == index_.end()))
563 return false;
564
565 // We reached the end of sequence, move to the next one.
566 // Note: ++read_iter_.seq_end might become index_.end(), but
567 // GetReadIterForSequence() knows how to deal with that.
568 read_iter_ = GetReadIterForSequence(read_iter_.seq_end);
569 PERFETTO_DCHECK(read_iter_.is_valid() && read_iter_.cur != index_.end());
570 previous_packet_dropped = true;
571 }
572
573 ChunkMeta* chunk_meta = &*read_iter_;
574
575 // If the chunk has holes that are awaiting to be patched out-of-band,
576 // skip the current sequence and move to the next one.
577 if (chunk_meta->flags & kChunkNeedsPatching) {
578 read_iter_.MoveToEnd();
579 continue;
580 }
581
582 const ProducerID trusted_producer_id = read_iter_.producer_id();
583 const WriterID writer_id = read_iter_.writer_id();
584 const ProducerAndWriterID producer_and_writer_id =
585 MkProducerAndWriterID(trusted_producer_id, writer_id);
586 const uid_t trusted_uid = chunk_meta->trusted_uid;
587 const pid_t trusted_pid = chunk_meta->trusted_pid;
588
589 // At this point we have a chunk in |chunk_meta| that has not been fully
590 // read. We don't know yet whether we have enough data to read the full
591 // packet (in the case it's fragmented over several chunks) and we are about
592 // to find that out. Specifically:
593 // A) If the first fragment is unread and is a fragment continuing from a
594 // previous chunk, it means we have missed the previous ChunkID. In
595 // fact, if this wasn't the case, a previous call to ReadNext() shouldn't
596 // have moved the cursor to this chunk.
597 // B) Any fragment > 0 && < last is always readable. By definition an inner
598 // packet is never fragmented and hence doesn't require neither stitching
599 // nor any out-of-band patching. The same applies to the last packet
600 // iff it doesn't continue on the next chunk.
601 // C) If the last packet (which might be also the only packet in the chunk)
602 // is a fragment and continues on the next chunk, we peek at the next
603 // chunks and, if we have all of them, mark as read and move the cursor.
604 //
605 // +---------------+ +-------------------+ +---------------+
606 // | ChunkID: 1 | | ChunkID: 2 | | ChunkID: 3 |
607 // |---------------+ +-------------------+ +---------------+
608 // | Packet 1 | | | | ... Packet 3 |
609 // | Packet 2 | | ... Packet 3 ... | | Packet 4 |
610 // | Packet 3 ... | | | | Packet 5 ... |
611 // +---------------+ +-------------------+ +---------------+
612
613 PERFETTO_DCHECK(chunk_meta->num_fragments_read <=
614 chunk_meta->num_fragments);
615
616 // If we didn't read any packets from this chunk, the last packet was from
617 // the previous chunk we iterated over; so don't update
618 // |previous_packet_dropped| in this case.
619 if (chunk_meta->num_fragments_read > 0)
620 previous_packet_dropped = chunk_meta->last_read_packet_skipped();
621
622 while (chunk_meta->num_fragments_read < chunk_meta->num_fragments) {
623 enum { kSkip = 0, kReadOnePacket, kTryReadAhead } action;
624 if (chunk_meta->num_fragments_read == 0) {
625 if (chunk_meta->flags & kFirstPacketContinuesFromPrevChunk) {
626 action = kSkip; // Case A.
627 } else if (chunk_meta->num_fragments == 1 &&
628 (chunk_meta->flags & kLastPacketContinuesOnNextChunk)) {
629 action = kTryReadAhead; // Case C.
630 } else {
631 action = kReadOnePacket; // Case B.
632 }
633 } else if (chunk_meta->num_fragments_read <
634 chunk_meta->num_fragments - 1 ||
635 !(chunk_meta->flags & kLastPacketContinuesOnNextChunk)) {
636 action = kReadOnePacket; // Case B.
637 } else {
638 action = kTryReadAhead; // Case C.
639 }
640
641 TRACE_BUFFER_DLOG(" chunk %u, packet %hu of %hu, action=%d",
642 read_iter_.chunk_id(), chunk_meta->num_fragments_read,
643 chunk_meta->num_fragments, action);
644
645 if (action == kSkip) {
646 // This fragment will be skipped forever, not just in this ReadPacket()
647 // iteration. This happens by virtue of ReadNextPacketInChunk()
648 // incrementing the |num_fragments_read| and marking the fragment as
649 // read even if we didn't really.
650 ReadNextPacketInChunk(producer_and_writer_id, chunk_meta, nullptr);
651 chunk_meta->set_last_read_packet_skipped(true);
652 previous_packet_dropped = true;
653 continue;
654 }
655
656 if (action == kReadOnePacket) {
657 // The easy peasy case B.
658 ReadPacketResult result =
659 ReadNextPacketInChunk(producer_and_writer_id, chunk_meta, packet);
660
661 if (PERFETTO_LIKELY(result == ReadPacketResult::kSucceeded)) {
662 *sequence_properties = {trusted_producer_id, trusted_uid, trusted_pid,
663 writer_id};
664 *previous_packet_on_sequence_dropped = previous_packet_dropped;
665 return true;
666 } else if (result == ReadPacketResult::kFailedEmptyPacket) {
667 // We can ignore and skip empty packets.
668 PERFETTO_DCHECK(packet->slices().empty());
669 continue;
670 }
671
672 // In extremely rare cases (producer bugged / malicious) the chunk might
673 // contain an invalid fragment. In such case we don't want to stall the
674 // sequence but just skip the chunk and move on. ReadNextPacketInChunk()
675 // marks the chunk as fully read, so we don't attempt to read from it
676 // again in a future call to ReadBuffers(). It also already records an
677 // abi violation for this.
678 PERFETTO_DCHECK(result == ReadPacketResult::kFailedInvalidPacket);
679 chunk_meta->set_last_read_packet_skipped(true);
680 previous_packet_dropped = true;
681 break;
682 }
683
684 PERFETTO_DCHECK(action == kTryReadAhead);
685 ReadAheadResult ra_res = ReadAhead(packet);
686 if (ra_res == ReadAheadResult::kSucceededReturnSlices) {
687 stats_.set_readaheads_succeeded(stats_.readaheads_succeeded() + 1);
688 *sequence_properties = {trusted_producer_id, trusted_uid, trusted_pid,
689 writer_id};
690 *previous_packet_on_sequence_dropped = previous_packet_dropped;
691 return true;
692 }
693
694 if (ra_res == ReadAheadResult::kFailedMoveToNextSequence) {
695 // readahead didn't find a contiguous packet sequence. We'll try again
696 // on the next ReadPacket() call.
697 stats_.set_readaheads_failed(stats_.readaheads_failed() + 1);
698
699 // TODO(primiano): optimization: this MoveToEnd() is the reason why
700 // MoveNext() (that is called in the outer for(;;MoveNext)) needs to
701 // deal gracefully with the case of |cur|==|seq_end|. Maybe we can do
702 // something to avoid that check by reshuffling the code here?
703 read_iter_.MoveToEnd();
704
705 // This break will go back to beginning of the for(;;MoveNext()). That
706 // will move to the next sequence because we set the read iterator to
707 // its end.
708 break;
709 }
710
711 PERFETTO_DCHECK(ra_res == ReadAheadResult::kFailedStayOnSameSequence);
712
713 // In this case ReadAhead() might advance |read_iter_|, so we need to
714 // re-cache the |chunk_meta| pointer to point to the current chunk.
715 chunk_meta = &*read_iter_;
716 chunk_meta->set_last_read_packet_skipped(true);
717 previous_packet_dropped = true;
718 } // while(...) [iterate over packet fragments for the current chunk].
719 } // for(;;MoveNext()) [iterate over chunks].
720 }
721
ReadAhead(TracePacket * packet)722 TraceBuffer::ReadAheadResult TraceBuffer::ReadAhead(TracePacket* packet) {
723 static_assert(static_cast<ChunkID>(kMaxChunkID + 1) == 0,
724 "relying on kMaxChunkID to wrap naturally");
725 TRACE_BUFFER_DLOG(" readahead start @ chunk %u", read_iter_.chunk_id());
726 ChunkID next_chunk_id = read_iter_.chunk_id() + 1;
727 SequenceIterator it = read_iter_;
728 for (it.MoveNext(); it.is_valid(); it.MoveNext(), next_chunk_id++) {
729 // We should stay within the same sequence while iterating here.
730 PERFETTO_DCHECK(it.producer_id() == read_iter_.producer_id() &&
731 it.writer_id() == read_iter_.writer_id());
732
733 TRACE_BUFFER_DLOG(" expected chunk ID: %u, actual ID: %u", next_chunk_id,
734 it.chunk_id());
735
736 if (PERFETTO_UNLIKELY((*it).num_fragments == 0))
737 continue;
738
739 // If we miss the next chunk, stop looking in the current sequence and
740 // try another sequence. This chunk might come in the near future.
741 // The second condition is the edge case of a buggy/malicious
742 // producer. The ChunkID is contiguous but its flags don't make sense.
743 if (it.chunk_id() != next_chunk_id ||
744 PERFETTO_UNLIKELY(
745 !((*it).flags & kFirstPacketContinuesFromPrevChunk))) {
746 return ReadAheadResult::kFailedMoveToNextSequence;
747 }
748
749 // If the chunk is contiguous but has not been patched yet move to the next
750 // sequence and try coming back here on the next ReadNextTracePacket() call.
751 // TODO(primiano): add a test to cover this, it's a subtle case.
752 if ((*it).flags & kChunkNeedsPatching)
753 return ReadAheadResult::kFailedMoveToNextSequence;
754
755 // This is the case of an intermediate chunk which contains only one
756 // fragment which continues on the next chunk. This is the case for large
757 // packets, e.g.: [Packet0, Packet1(0)] [Packet1(1)] [Packet1(2), ...]
758 // (Packet1(X) := fragment X of Packet1).
759 if ((*it).num_fragments == 1 &&
760 ((*it).flags & kLastPacketContinuesOnNextChunk)) {
761 continue;
762 }
763
764 // We made it! We got all fragments for the packet without holes.
765 TRACE_BUFFER_DLOG(" readahead success @ chunk %u", it.chunk_id());
766 PERFETTO_DCHECK(((*it).num_fragments == 1 &&
767 !((*it).flags & kLastPacketContinuesOnNextChunk)) ||
768 (*it).num_fragments > 1);
769
770 // Now let's re-iterate over the [read_iter_, it] sequence and mark
771 // all the fragments as read.
772 bool packet_corruption = false;
773 for (;;) {
774 PERFETTO_DCHECK(read_iter_.is_valid());
775 TRACE_BUFFER_DLOG(" commit chunk %u", read_iter_.chunk_id());
776 if (PERFETTO_LIKELY((*read_iter_).num_fragments > 0)) {
777 // In the unlikely case of a corrupted packet (corrupted or empty
778 // fragment), invalidate the all stitching and move on to the next chunk
779 // in the same sequence, if any.
780 auto pw_id = MkProducerAndWriterID(it.producer_id(), it.writer_id());
781 packet_corruption |=
782 ReadNextPacketInChunk(pw_id, &*read_iter_, packet) ==
783 ReadPacketResult::kFailedInvalidPacket;
784 }
785 if (read_iter_.cur == it.cur)
786 break;
787 read_iter_.MoveNext();
788 } // for(;;)
789 PERFETTO_DCHECK(read_iter_.cur == it.cur);
790
791 if (PERFETTO_UNLIKELY(packet_corruption)) {
792 // ReadNextPacketInChunk() already records an abi violation for this case.
793 *packet = TracePacket(); // clear.
794 return ReadAheadResult::kFailedStayOnSameSequence;
795 }
796
797 return ReadAheadResult::kSucceededReturnSlices;
798 } // for(it...) [readahead loop]
799 return ReadAheadResult::kFailedMoveToNextSequence;
800 }
801
ReadNextPacketInChunk(ProducerAndWriterID producer_and_writer_id,ChunkMeta * const chunk_meta,TracePacket * packet)802 TraceBuffer::ReadPacketResult TraceBuffer::ReadNextPacketInChunk(
803 ProducerAndWriterID producer_and_writer_id,
804 ChunkMeta* const chunk_meta,
805 TracePacket* packet) {
806 PERFETTO_DCHECK(chunk_meta->num_fragments_read < chunk_meta->num_fragments);
807 PERFETTO_DCHECK(!(chunk_meta->flags & kChunkNeedsPatching));
808
809 const uint8_t* record_begin = begin() + chunk_meta->record_off;
810 DcheckIsAlignedAndWithinBounds(record_begin);
811 auto* chunk_record = reinterpret_cast<const ChunkRecord*>(record_begin);
812 const uint8_t* record_end = record_begin + chunk_record->size;
813 const uint8_t* packets_begin = record_begin + sizeof(ChunkRecord);
814 const uint8_t* packet_begin = packets_begin + chunk_meta->cur_fragment_offset;
815
816 if (PERFETTO_UNLIKELY(packet_begin < packets_begin ||
817 packet_begin >= record_end)) {
818 // The producer has a bug or is malicious and did declare that the chunk
819 // contains more packets beyond its boundaries.
820 stats_.set_abi_violations(stats_.abi_violations() + 1);
821 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
822 chunk_meta->cur_fragment_offset = 0;
823 chunk_meta->num_fragments_read = chunk_meta->num_fragments;
824 if (PERFETTO_LIKELY(chunk_meta->is_complete())) {
825 stats_.set_chunks_read(stats_.chunks_read() + 1);
826 stats_.set_bytes_read(stats_.bytes_read() + chunk_record->size);
827 }
828 return ReadPacketResult::kFailedInvalidPacket;
829 }
830
831 // A packet (or a fragment) starts with a varint stating its size, followed
832 // by its content. The varint shouldn't be larger than 4 bytes (just in case
833 // the producer is using a redundant encoding)
834 uint64_t packet_size = 0;
835 const uint8_t* header_end =
836 std::min(packet_begin + protozero::proto_utils::kMessageLengthFieldSize,
837 record_end);
838 const uint8_t* packet_data = protozero::proto_utils::ParseVarInt(
839 packet_begin, header_end, &packet_size);
840
841 const uint8_t* next_packet = packet_data + packet_size;
842 if (PERFETTO_UNLIKELY(next_packet <= packet_begin ||
843 next_packet > record_end)) {
844 // In BufferExhaustedPolicy::kDrop mode, TraceWriter may abort a fragmented
845 // packet by writing an invalid size in the last fragment's header. We
846 // should handle this case without recording an ABI violation (since Android
847 // R).
848 if (packet_size != SharedMemoryABI::kPacketSizeDropPacket) {
849 stats_.set_abi_violations(stats_.abi_violations() + 1);
850 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
851 } else {
852 stats_.set_trace_writer_packet_loss(stats_.trace_writer_packet_loss() +
853 1);
854 }
855 chunk_meta->cur_fragment_offset = 0;
856 chunk_meta->num_fragments_read = chunk_meta->num_fragments;
857 if (PERFETTO_LIKELY(chunk_meta->is_complete())) {
858 stats_.set_chunks_read(stats_.chunks_read() + 1);
859 stats_.set_bytes_read(stats_.bytes_read() + chunk_record->size);
860 }
861 return ReadPacketResult::kFailedInvalidPacket;
862 }
863
864 chunk_meta->cur_fragment_offset =
865 static_cast<uint16_t>(next_packet - packets_begin);
866 chunk_meta->num_fragments_read++;
867
868 if (PERFETTO_UNLIKELY(chunk_meta->num_fragments_read ==
869 chunk_meta->num_fragments &&
870 chunk_meta->is_complete())) {
871 stats_.set_chunks_read(stats_.chunks_read() + 1);
872 stats_.set_bytes_read(stats_.bytes_read() + chunk_record->size);
873 auto* writer_stats = writer_stats_.Insert(producer_and_writer_id, {}).first;
874 writer_stats->used_chunk_hist.Add(chunk_meta->cur_fragment_offset);
875 } else {
876 // We have at least one more packet to parse. It should be within the chunk.
877 if (chunk_meta->cur_fragment_offset + sizeof(ChunkRecord) >=
878 chunk_record->size) {
879 PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
880 }
881 }
882
883 chunk_meta->set_last_read_packet_skipped(false);
884
885 if (PERFETTO_UNLIKELY(packet_size == 0))
886 return ReadPacketResult::kFailedEmptyPacket;
887
888 if (PERFETTO_LIKELY(packet))
889 packet->AddSlice(packet_data, static_cast<size_t>(packet_size));
890
891 return ReadPacketResult::kSucceeded;
892 }
893
DiscardWrite()894 void TraceBuffer::DiscardWrite() {
895 PERFETTO_DCHECK(overwrite_policy_ == kDiscard);
896 discard_writes_ = true;
897 stats_.set_chunks_discarded(stats_.chunks_discarded() + 1);
898 TRACE_BUFFER_DLOG(" discarding write");
899 }
900
CloneReadOnly() const901 std::unique_ptr<TraceBuffer> TraceBuffer::CloneReadOnly() const {
902 std::unique_ptr<TraceBuffer> buf(new TraceBuffer(CloneCtor(), *this));
903 if (!buf->data_.IsValid())
904 return nullptr; // PagedMemory::Allocate() failed. We are out of memory.
905 return buf;
906 }
907
TraceBuffer(CloneCtor,const TraceBuffer & src)908 TraceBuffer::TraceBuffer(CloneCtor, const TraceBuffer& src)
909 : overwrite_policy_(src.overwrite_policy_),
910 read_only_(true),
911 discard_writes_(src.discard_writes_) {
912 if (!Initialize(src.data_.size()))
913 return; // TraceBuffer::Clone() will check |data_| and return nullptr.
914
915 // The assignments below must be done after Initialize().
916
917 data_.EnsureCommitted(data_.size());
918 memcpy(data_.Get(), src.data_.Get(), src.data_.size());
919 last_chunk_id_written_ = src.last_chunk_id_written_;
920
921 stats_ = src.stats_;
922 stats_.set_bytes_read(0);
923 stats_.set_chunks_read(0);
924 stats_.set_readaheads_failed(0);
925 stats_.set_readaheads_succeeded(0);
926
927 // Copy the index of chunk metadata and reset the read states.
928 index_ = ChunkMap(src.index_);
929 for (auto& kv : index_) {
930 ChunkMeta& chunk_meta = kv.second;
931 chunk_meta.num_fragments_read = 0;
932 chunk_meta.cur_fragment_offset = 0;
933 chunk_meta.set_last_read_packet_skipped(false);
934 }
935 read_iter_ = SequenceIterator();
936 }
937
938 } // namespace perfetto
939