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 #ifndef SRC_TRACING_CORE_TRACE_BUFFER_H_
     18 #define SRC_TRACING_CORE_TRACE_BUFFER_H_
     19 
     20 #include <stdint.h>
     21 #include <string.h>
     22 
     23 #include <array>
     24 #include <limits>
     25 #include <map>
     26 #include <tuple>
     27 
     28 #include "perfetto/base/logging.h"
     29 #include "perfetto/base/page_allocator.h"
     30 #include "perfetto/tracing/core/basic_types.h"
     31 #include "perfetto/tracing/core/slice.h"
     32 
     33 namespace perfetto {
     34 
     35 class TracePacket;
     36 
     37 // The main buffer, owned by the tracing service, where all the trace data is
     38 // ultimately stored into. The service will own several instances of this class,
     39 // at least one per active consumer (as defined in the |buffers| section of
     40 // trace_config.proto) and will copy chunks from the producer's shared memory
     41 // buffers into here when a CommitData IPC is received.
     42 //
     43 // Writing into the buffer
     44 // -----------------------
     45 // Data is copied from the SMB(s) using CopyChunkUntrusted(). The buffer will
     46 // hence contain data coming from different producers and different writer
     47 // sequences, more specifically:
     48 // - The service receives data by several producer(s), identified by their ID.
     49 // - Each producer writes several sequences identified by the same WriterID.
     50 //   (they correspond to TraceWriter instances in the producer).
     51 // - Each Writer writes, in order, several chunks.
     52 // - Each chunk contains zero, one, or more TracePacket(s), or even just
     53 //   fragments of packets (when they span across several chunks).
     54 //
     55 // So at any point in time, the buffer will contain a variable number of logical
     56 // sequences identified by the {ProducerID, WriterID} tuple. Any given chunk
     57 // will only contain packets (or fragments) belonging to the same sequence.
     58 //
     59 // The buffer operates by default as a ring buffer. Chunks are (over-)written
     60 // in the same order of the CopyChunkUntrusted() calls. When overwriting old
     61 // content, entire chunks are overwritten or clobbered. The buffer never leaves
     62 // a partial chunk around. Chunks' payload is copied as-is, but their header is
     63 // not and is repacked in order to keep the ProducerID around.
     64 //
     65 // Chunks are stored in the buffer next to each other. Each chunk is prefixed by
     66 // an inline header (ChunkRecord), which contains most of the fields of the
     67 // SharedMemoryABI ChunkHeader + the ProducerID + the size of the payload.
     68 // It's a conventional binary object stream essentially, where each ChunkRecord
     69 // tells where it ends and hence where to find the next one, like this:
     70 //
     71 //          .-------------------------. 16 byte boundary
     72 //          | ChunkRecord:   16 bytes |
     73 //          | - chunk id:     4 bytes |
     74 //          | - producer id:  2 bytes |
     75 //          | - writer id:    2 bytes |
     76 //          | - #fragments:   2 bytes |
     77 //    +-----+ - record size:  2 bytes |
     78 //    |     | - flags+pad:    4 bytes |
     79 //    |     +-------------------------+
     80 //    |     |                         |
     81 //    |     :     Chunk payload       :
     82 //    |     |                         |
     83 //    |     +-------------------------+
     84 //    |     |    Optional padding     |
     85 //    +---> +-------------------------+ 16 byte boundary
     86 //          |      ChunkRecord        |
     87 //          :                         :
     88 // Chunks stored in the buffer are always rounded up to 16 bytes (that is
     89 // sizeof(ChunkRecord)), in order to avoid further inner fragmentation.
     90 // Special "padding" chunks can be put in the buffer, e.g. in the case when we
     91 // try to write a chunk of size N while the write pointer is at the end of the
     92 // buffer, but the write pointer is < N bytes from the end (and hence needs to
     93 // wrap over).
     94 // Because of this, the buffer is self-describing: the contents of the buffer
     95 // can be reconstructed by just looking at the buffer content (this will be
     96 // quite useful in future to recover the buffer from crash reports).
     97 //
     98 // However, in order to keep some operations (patching and reading) fast, a
     99 // lookaside index is maintained (in |index_|), keeping each chunk in the buffer
    100 // indexed by their {ProducerID, WriterID, ChunkID} tuple.
    101 //
    102 // Patching data out-of-band
    103 // -------------------------
    104 // This buffer also supports patching chunks' payload out-of-band, after they
    105 // have been stored. This is to allow producers to backfill the "size" fields
    106 // of the protos that spawn across several chunks, when the previous chunks are
    107 // returned to the service. The MaybePatchChunkContents() deals with the fact
    108 // that a chunk might have been lost (because of wrapping) by the time the OOB
    109 // IPC comes.
    110 //
    111 // Reading from the buffer
    112 // -----------------------
    113 // This class supports one reader only (the consumer). Reads are NOT idempotent
    114 // as they move the read cursors around. Reading back the buffer is the most
    115 // conceptually complex part. The ReadNextTracePacket() method operates with
    116 // whole packet granularity. Packets are returned only when all their fragments
    117 // are available.
    118 // This class takes care of:
    119 // - Gluing packets within the same sequence, even if they are not stored
    120 //   adjacently in the buffer.
    121 // - Re-ordering chunks within a sequence (using the ChunkID, which wraps).
    122 // - Detecting holes in packet fragments (because of loss of chunks).
    123 // Reads guarantee that packets for the same sequence are read in FIFO order
    124 // (according to their ChunkID), but don't give any guarantee about the read
    125 // order of packets from different sequences, see comments in
    126 // ReadNextTracePacket() below.
    127 class TraceBuffer {
    128  public:
    129   static const size_t InlineChunkHeaderSize;  // For test/fake_packet.{cc,h}.
    130 
    131   // Maintain these fields consistent with trace_stats.proto. See comments in
    132   // the .proto for the semantic of these fields.
    133   struct Stats {
    134     uint64_t bytes_written = 0;
    135     uint64_t chunks_written = 0;
    136     uint64_t chunks_overwritten = 0;
    137     uint64_t write_wrap_count = 0;
    138     uint64_t patches_succeeded = 0;
    139     uint64_t patches_failed = 0;
    140     uint64_t readaheads_succeeded = 0;
    141     uint64_t readaheads_failed = 0;
    142     uint64_t abi_violations = 0;
    143     // TODO(primiano): add bytes_lost_for_padding.
    144   };
    145 
    146   // Argument for out-of-band patches applied through TryPatchChunkContents().
    147   struct Patch {
    148     // From SharedMemoryABI::kPacketHeaderSize.
    149     static constexpr size_t kSize = 4;
    150 
    151     size_t offset_untrusted;
    152     std::array<uint8_t, kSize> data;
    153   };
    154 
    155   // Can return nullptr if the memory allocation fails.
    156   static std::unique_ptr<TraceBuffer> Create(size_t size_in_bytes);
    157 
    158   ~TraceBuffer();
    159 
    160   // Copies a Chunk from a producer Shared Memory Buffer into the trace buffer.
    161   // |src| points to the first packet in the SharedMemoryABI's chunk shared
    162   // with an untrusted producer. "untrusted" here means: the producer might be
    163   // malicious and might change |src| concurrently while we read it (internally
    164   // this method memcpy()-s first the chunk before processing it).
    165   // None of the arguments should be trusted, unless otherwise stated. We can
    166   // trust that |src| points to a valid memory area, but not its contents.
    167   void CopyChunkUntrusted(ProducerID producer_id_trusted,
    168                           uid_t producer_uid_trusted,
    169                           WriterID writer_id,
    170                           ChunkID chunk_id,
    171                           uint16_t num_fragments,
    172                           uint8_t chunk_flags,
    173                           const uint8_t* src,
    174                           size_t size);
    175   // Applies a batch of |patches| to the given chunk, if the given chunk is
    176   // still in the buffer. Does nothing if the given ChunkID is gone.
    177   // Returns true if the chunk has been found and patched, false otherwise.
    178   // |other_patches_pending| is used to determine whether this is the only
    179   // batch of patches for the chunk or there is more.
    180   // If |other_patches_pending| == false, the chunk is marked as ready to be
    181   // consumed. If true, the state of the chunk is not altered.
    182   bool TryPatchChunkContents(ProducerID,
    183                              WriterID,
    184                              ChunkID,
    185                              const Patch* patches,
    186                              size_t patches_size,
    187                              bool other_patches_pending);
    188 
    189   // To read the contents of the buffer the caller needs to:
    190   //   BeginRead()
    191   //   while (ReadNextTracePacket(packet_fragments)) { ... }
    192   // No other calls to any other method should be interleaved between
    193   // BeginRead() and ReadNextTracePacket().
    194   // Reads in the TraceBuffer are NOT idempotent.
    195   void BeginRead();
    196 
    197   // Returns the next packet in the buffer, if any, and the uid of the producer
    198   // that wrote it (as passed in the CopyChunkUntrusted() call). Returns false
    199   // if no packets can be read at this point.
    200   // This function returns only complete packets. Specifically:
    201   // When there is at least one complete packet in the buffer, this function
    202   // returns true and populates the TracePacket argument with the boundaries of
    203   // each fragment for one packet.
    204   // TracePacket will have at least one slice when this function returns true.
    205   // When there are no whole packets eligible to read (e.g. we are still missing
    206   // fragments) this function returns false.
    207   // This function guarantees also that packets for a given
    208   // {ProducerID, WriterID} are read in FIFO order.
    209   // This function does not guarantee any ordering w.r.t. packets belonging to
    210   // different WriterID(s). For instance, given the following packets copied
    211   // into the buffer:
    212   //   {ProducerID: 1, WriterID: 1}: P1 P2 P3
    213   //   {ProducerID: 1, WriterID: 2}: P4 P5 P6
    214   //   {ProducerID: 2, WriterID: 1}: P7 P8 P9
    215   // The following read sequence is possible:
    216   //   P1, P4, P7, P2, P3, P5, P8, P9, P6
    217   // But the following is guaranteed to NOT happen:
    218   //   P1, P5, P7, P4 (P4 cannot come after P5)
    219   bool ReadNextTracePacket(TracePacket*, uid_t* producer_uid);
    220 
    221   const Stats& stats() const { return stats_; }
    222   size_t size() const { return size_; }
    223 
    224  private:
    225   friend class TraceBufferTest;
    226 
    227   // ChunkRecord is a Chunk header stored inline in the |data_| buffer, before
    228   // the chunk payload (the packets' data). The |data_| buffer looks like this:
    229   // +---------------+------------------++---------------+-----------------+
    230   // | ChunkRecord 1 | Chunk payload 1  || ChunkRecord 2 | Chunk payload 2 | ...
    231   // +---------------+------------------++---------------+-----------------+
    232   // Most of the ChunkRecord fields are copied from SharedMemoryABI::ChunkHeader
    233   // (the chunk header used in the the shared memory buffers).
    234   // A ChunkRecord can be a special "padding" record. In this case its payload
    235   // should be ignored and the record should be just skipped.
    236   //
    237   // Full page move optimization:
    238   // This struct has to be exactly (sizeof(PageHeader) + sizeof(ChunkHeader))
    239   // (from shared_memory_abi.h) to allow full page move optimizations
    240   // (TODO(primiano): not implemented yet). In the special case of moving a full
    241   // 4k page that contains only one chunk, in fact, we can just ask the kernel
    242   // to move the full SHM page (see SPLICE_F_{GIFT,MOVE}) and overlay the
    243   // ChunkRecord on top of the moved SMB's header (page + chunk header).
    244   // This special requirement is covered by static_assert(s) in the .cc file.
    245   struct ChunkRecord {
    246     explicit ChunkRecord(size_t sz) : flags{0}, is_padding{0} {
    247       PERFETTO_DCHECK(sz >= sizeof(ChunkRecord) &&
    248                       sz % sizeof(ChunkRecord) == 0 && sz <= kMaxSize);
    249       size = static_cast<decltype(size)>(sz);
    250     }
    251 
    252     bool is_valid() const { return size != 0; }
    253 
    254     // Keep this structure packed and exactly 16 bytes (128 bits) big.
    255 
    256     // [32 bits] Monotonic counter within the same writer_id.
    257     ChunkID chunk_id = 0;
    258 
    259     // [16 bits] ID of the Producer from which the Chunk was copied from.
    260     ProducerID producer_id = 0;
    261 
    262     // [16 bits] Unique per Producer (but not within the service).
    263     // If writer_id == kWriterIdPadding the record should just be skipped.
    264     WriterID writer_id = 0;
    265 
    266     // Number of fragments contained in the chunk.
    267     uint16_t num_fragments = 0;
    268 
    269     // Size in bytes, including sizeof(ChunkRecord) itself.
    270     uint16_t size;
    271 
    272     uint8_t flags : 6;  // See SharedMemoryABI::ChunkHeader::flags.
    273     uint8_t is_padding : 1;
    274     uint8_t unused_flag : 1;
    275 
    276     // Not strictly needed, can be reused for more fields in the future. But
    277     // right now helps to spot chunks in hex dumps.
    278     char unused[3] = {'C', 'H', 'U'};
    279 
    280     static constexpr size_t kMaxSize =
    281         std::numeric_limits<decltype(size)>::max();
    282   };
    283 
    284   // Lookaside index entry. This serves two purposes:
    285   // 1) Allow a fast lookup of ChunkRecord by their ID (the tuple
    286   //   {ProducerID, WriterID, ChunkID}). This is used when applying out-of-band
    287   //   patches to the contents of the chunks after they have been copied into
    288   //   the TraceBuffer.
    289   // 2) keep the chunks ordered by their ID. This is used when reading back.
    290   // 3) Keep metadata about the status of the chunk, e.g. whether the contents
    291   //    have been read already and should be skipped in a future read pass.
    292   // This struct should not have any field that is essential for reconstructing
    293   // the contents of the buffer from a crash dump.
    294   struct ChunkMeta {
    295     // Key used for sorting in the map.
    296     struct Key {
    297       Key(ProducerID p, WriterID w, ChunkID c)
    298           : producer_id{p}, writer_id{w}, chunk_id{c} {}
    299 
    300       explicit Key(const ChunkRecord& cr)
    301           : Key(cr.producer_id, cr.writer_id, cr.chunk_id) {}
    302 
    303       // Note that this sorting doesn't keep into account the fact that ChunkID
    304       // will wrap over at some point. The extra logic in SequenceIterator deals
    305       // with that.
    306       bool operator<(const Key& other) const {
    307         return std::tie(producer_id, writer_id, chunk_id) <
    308                std::tie(other.producer_id, other.writer_id, other.chunk_id);
    309       }
    310 
    311       bool operator==(const Key& other) const {
    312         return std::tie(producer_id, writer_id, chunk_id) ==
    313                std::tie(other.producer_id, other.writer_id, other.chunk_id);
    314       }
    315 
    316       // These fields should match at all times the corresponding fields in
    317       // the |chunk_record|. They are copied here purely for efficiency to avoid
    318       // dereferencing the buffer all the time.
    319       ProducerID producer_id;
    320       WriterID writer_id;
    321       ChunkID chunk_id;
    322     };
    323 
    324     ChunkMeta(ChunkRecord* c, uint16_t p, uint8_t f, uid_t u)
    325         : chunk_record{c}, trusted_uid{u}, flags{f}, num_fragments{p} {}
    326 
    327     ChunkRecord* const chunk_record;   // Addr of ChunkRecord within |data_|.
    328     const uid_t trusted_uid;           // uid of the producer.
    329     uint8_t flags = 0;                 // See SharedMemoryABI::flags.
    330     const uint16_t num_fragments = 0;  // Total number of packet fragments.
    331     uint16_t num_fragments_read = 0;   // Number of fragments already read.
    332 
    333     // The start offset of the next fragment (the |num_fragments_read|-th) to be
    334     // read. This is the offset in bytes from the beginning of the ChunkRecord's
    335     // payload (the 1st fragment starts at |chunk_record| +
    336     // sizeof(ChunkRecord)).
    337     uint16_t cur_fragment_offset = 0;
    338   };
    339 
    340   using ChunkMap = std::map<ChunkMeta::Key, ChunkMeta>;
    341 
    342   // Allows to iterate over a sub-sequence of |index_| for all keys belonging to
    343   // the same {ProducerID,WriterID}. Furthermore takes into account the wrapping
    344   // of ChunkID. Instances are valid only as long as the |index_| is not altered
    345   // (can be used safely only between adjacent ReadNextTracePacket() calls).
    346   // The order of the iteration will proceed in the following order:
    347   // |wrapping_id| + 1 -> |seq_end|, |seq_begin| -> |wrapping_id|.
    348   // Practical example:
    349   // - Assume that kMaxChunkID == 7
    350   // - Assume that we have all 8 chunks in the range (0..7).
    351   // - Hence, |seq_begin| == c0, |seq_end| == c7
    352   // - Assume |wrapping_id| = 4 (c4 is the last chunk copied over
    353   //   through a CopyChunkUntrusted()).
    354   // The resulting iteration order will be: c5, c6, c7, c0, c1, c2, c3, c4.
    355   struct SequenceIterator {
    356     // Points to the 1st key (the one with the numerically min ChunkID).
    357     ChunkMap::iterator seq_begin;
    358 
    359     // Points one past the last key (the one with the numerically max ChunkID).
    360     ChunkMap::iterator seq_end;
    361 
    362     // Current iterator, always >= seq_begin && <= seq_end.
    363     ChunkMap::iterator cur;
    364 
    365     // The latest ChunkID written. Determines the start/end of the sequence.
    366     ChunkID wrapping_id;
    367 
    368     bool is_valid() const { return cur != seq_end; }
    369 
    370     ProducerID producer_id() const {
    371       PERFETTO_DCHECK(is_valid());
    372       return cur->first.producer_id;
    373     }
    374 
    375     WriterID writer_id() const {
    376       PERFETTO_DCHECK(is_valid());
    377       return cur->first.writer_id;
    378     }
    379 
    380     ChunkID chunk_id() const {
    381       PERFETTO_DCHECK(is_valid());
    382       return cur->first.chunk_id;
    383     }
    384 
    385     ChunkMeta& operator*() {
    386       PERFETTO_DCHECK(is_valid());
    387       return cur->second;
    388     }
    389 
    390     // Moves |cur| to the next chunk in the index.
    391     // is_valid() will become false after calling this, if this was the last
    392     // entry of the sequence.
    393     void MoveNext();
    394 
    395     void MoveToEnd() { cur = seq_end; }
    396   };
    397 
    398   enum class ReadAheadResult {
    399     kSucceededReturnSlices,
    400     kFailedMoveToNextSequence,
    401     kFailedStayOnSameSequence,
    402   };
    403 
    404   TraceBuffer();
    405   TraceBuffer(const TraceBuffer&) = delete;
    406   TraceBuffer& operator=(const TraceBuffer&) = delete;
    407 
    408   bool Initialize(size_t size);
    409 
    410   // Returns an object that allows to iterate over chunks in the |index_| that
    411   // have the same {ProducerID, WriterID} of
    412   // |seq_begin.first.{producer,writer}_id|. |seq_begin| must be an iterator to
    413   // the first entry in the |index_| that has a different {ProducerID, WriterID}
    414   // from the previous one. It is valid for |seq_begin| to be == index_.end()
    415   // (i.e. if the index is empty). The iteration takes care of ChunkID wrapping,
    416   // by using |last_chunk_id_|.
    417   SequenceIterator GetReadIterForSequence(ChunkMap::iterator seq_begin);
    418 
    419   // Used as a last resort when a buffer corruption is detected.
    420   void ClearContentsAndResetRWCursors();
    421 
    422   // Adds a padding record of the given size (must be a multiple of
    423   // sizeof(ChunkRecord)).
    424   void AddPaddingRecord(size_t);
    425 
    426   // Look for contiguous fragment of the same packet starting from |read_iter_|.
    427   // If a contiguous packet is found, all the fragments are pushed into
    428   // TracePacket and the function returns kSucceededReturnSlices. If not, the
    429   // function returns either kFailedMoveToNextSequence or
    430   // kFailedStayOnSameSequence, telling the caller to continue looking for
    431   // packets.
    432   ReadAheadResult ReadAhead(TracePacket*);
    433 
    434   // Deletes (by marking the record invalid and removing form the index) all
    435   // chunks from |wptr_| to |wptr_| + |bytes_to_clear|. Returns the size of the
    436   // gap left between the next valid Chunk and the end of the deletion range, or
    437   // 0 if such next valid chunk doesn't exist (if the buffer is still zeroed).
    438   // Graphically, assume the initial situation is the following (|wptr_| = 10).
    439   // |0        |10 (wptr_)       |30       |40                 |60
    440   // +---------+-----------------+---------+-------------------+---------+
    441   // | Chunk 1 | Chunk 2         | Chunk 3 | Chunk 4           | Chunk 5 |
    442   // +---------+-----------------+---------+-------------------+---------+
    443   //           |_________Deletion range_______|~~return value~~|
    444   //
    445   // A call to DeleteNextChunksFor(32) will remove chunks 2,3,4 and return 18
    446   // (60 - 42), the distance between chunk 5 and the end of the deletion range.
    447   size_t DeleteNextChunksFor(size_t bytes_to_clear);
    448 
    449   // Decodes the boundaries of the next packet (or a fragment) pointed by
    450   // ChunkMeta and pushes that into |TracePacket|. It also increments the
    451   // |num_fragments_read| counter.
    452   // TracePacket can be nullptr, in which case the read state is still advanced.
    453   // When TracePacket is not nullptr, ProducerID must also be not null and will
    454   // be updated with the ProducerID that originally wrote the chunk.
    455   bool ReadNextPacketInChunk(ChunkMeta*, TracePacket*);
    456 
    457   void DcheckIsAlignedAndWithinBounds(const uint8_t* ptr) const {
    458     PERFETTO_DCHECK(ptr >= begin() && ptr <= end() - sizeof(ChunkRecord));
    459     PERFETTO_DCHECK(
    460         (reinterpret_cast<uintptr_t>(ptr) & (alignof(ChunkRecord) - 1)) == 0);
    461   }
    462 
    463   ChunkRecord* GetChunkRecordAt(uint8_t* ptr) const {
    464     DcheckIsAlignedAndWithinBounds(ptr);
    465     return reinterpret_cast<ChunkRecord*>(ptr);
    466   }
    467 
    468   // |src| can be nullptr (in which case |size| must be ==
    469   // record.size - sizeof(ChunkRecord)), for the case of writing a padding
    470   // record. |wptr_| is NOT advanced by this function, the caller must do that.
    471   void WriteChunkRecord(const ChunkRecord& record,
    472                         const uint8_t* src,
    473                         size_t size) {
    474     // Note: |record.size| will be slightly bigger than |size| because of the
    475     // ChunkRecord header and rounding, to ensure that all ChunkRecord(s) are
    476     // multiple of sizeof(ChunkRecord). The invariant is:
    477     // record.size >= |size| + sizeof(ChunkRecord) (== if no rounding).
    478     PERFETTO_DCHECK(size <= ChunkRecord::kMaxSize);
    479     PERFETTO_DCHECK(record.size >= sizeof(record));
    480     PERFETTO_DCHECK(record.size % sizeof(record) == 0);
    481     PERFETTO_DCHECK(record.size >= size + sizeof(record));
    482     PERFETTO_CHECK(record.size <= size_to_end());
    483     DcheckIsAlignedAndWithinBounds(wptr_);
    484 
    485     // Deliberately not a *D*CHECK.
    486     PERFETTO_CHECK(wptr_ + sizeof(record) + size <= end());
    487     memcpy(wptr_, &record, sizeof(record));
    488     if (PERFETTO_LIKELY(src)) {
    489       memcpy(wptr_ + sizeof(record), src, size);
    490     } else {
    491       PERFETTO_DCHECK(size == record.size - sizeof(record));
    492     }
    493     const size_t rounding_size = record.size - sizeof(record) - size;
    494     memset(wptr_ + sizeof(record) + size, 0, rounding_size);
    495   }
    496 
    497   uint8_t* begin() const { return reinterpret_cast<uint8_t*>(data_.get()); }
    498   uint8_t* end() const { return begin() + size_; }
    499   size_t size_to_end() const { return static_cast<size_t>(end() - wptr_); }
    500 
    501   base::PageAllocator::UniquePtr data_;
    502   size_t size_ = 0;            // Size in bytes of |data_|.
    503   size_t max_chunk_size_ = 0;  // Max size in bytes allowed for a chunk.
    504   uint8_t* wptr_ = nullptr;    // Write pointer.
    505 
    506   // An index that keeps track of the positions and metadata of each
    507   // ChunkRecord.
    508   ChunkMap index_;
    509 
    510   // Read iterator used for ReadNext(). It is reset by calling BeginRead().
    511   // It becomes invalid after any call to methods that alters the |index_|.
    512   SequenceIterator read_iter_;
    513 
    514   // Keeps track of the last ChunkID written for a given writer.
    515   // TODO(primiano): should clean up keys from this map. Right now this map
    516   // grows without bounds (although realistically is not a problem unless we
    517   // have too many producers/writers within the same trace session).
    518   std::map<std::pair<ProducerID, WriterID>, ChunkID> last_chunk_id_;
    519 
    520   // Statistics about buffer usage.
    521   Stats stats_;
    522 
    523 #if PERFETTO_DCHECK_IS_ON()
    524   bool changed_since_last_read_ = false;
    525 #endif
    526 
    527   // When true disable some DCHECKs that have been put in place to detect
    528   // bugs in the producers. This is for tests that feed malicious inputs and
    529   // hence mimic a buggy producer.
    530   bool suppress_sanity_dchecks_for_testing_ = false;
    531 };
    532 
    533 }  // namespace perfetto
    534 
    535 #endif  // SRC_TRACING_CORE_TRACE_BUFFER_H_
    536