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