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