• 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 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