/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SRC_TRACING_SERVICE_TRACE_BUFFER_H_ #define SRC_TRACING_SERVICE_TRACE_BUFFER_H_ #include <stdint.h> #include <string.h> #include <array> #include <limits> #include <map> #include <tuple> #include "perfetto/base/logging.h" #include "perfetto/ext/base/flat_hash_map.h" #include "perfetto/ext/base/paged_memory.h" #include "perfetto/ext/base/thread_annotations.h" #include "perfetto/ext/base/utils.h" #include "perfetto/ext/tracing/core/basic_types.h" #include "perfetto/ext/tracing/core/client_identity.h" #include "perfetto/ext/tracing/core/slice.h" #include "perfetto/ext/tracing/core/trace_stats.h" #include "src/tracing/service/histogram.h" namespace perfetto { class TracePacket; // The main buffer, owned by the tracing service, where all the trace data is // ultimately stored into. The service will own several instances of this class, // at least one per active consumer (as defined in the |buffers| section of // trace_config.proto) and will copy chunks from the producer's shared memory // buffers into here when a CommitData IPC is received. // // Writing into the buffer // ----------------------- // Data is copied from the SMB(s) using CopyChunkUntrusted(). The buffer will // hence contain data coming from different producers and different writer // sequences, more specifically: // - The service receives data by several producer(s), identified by their ID. // - Each producer writes several sequences identified by the same WriterID. // (they correspond to TraceWriter instances in the producer). // - Each Writer writes, in order, several chunks. // - Each chunk contains zero, one, or more TracePacket(s), or even just // fragments of packets (when they span across several chunks). // // So at any point in time, the buffer will contain a variable number of logical // sequences identified by the {ProducerID, WriterID} tuple. Any given chunk // will only contain packets (or fragments) belonging to the same sequence. // // The buffer operates by default as a ring buffer. // It has two overwrite policies: // 1. kOverwrite (default): if the write pointer reaches the read pointer, old // unread chunks will be overwritten by new chunks. // 2. kDiscard: if the write pointer reaches the read pointer, unread chunks // are preserved and the new chunks are discarded. Any future write becomes // a no-op, even if the reader manages to fully catch up. This is because // once a chunk is discarded, the sequence of packets is broken and trying // to recover would be too hard (also due to the fact that, at the same // time, we allow out-of-order commits and chunk re-writes). // // Chunks are (over)written in the same order of the CopyChunkUntrusted() calls. // When overwriting old content, entire chunks are overwritten or clobbered. // The buffer never leaves a partial chunk around. Chunks' payload is copied // as-is, but their header is not and is repacked in order to keep the // ProducerID around. // // Chunks are stored in the buffer next to each other. Each chunk is prefixed by // an inline header (ChunkRecord), which contains most of the fields of the // SharedMemoryABI ChunkHeader + the ProducerID + the size of the payload. // It's a conventional binary object stream essentially, where each ChunkRecord // tells where it ends and hence where to find the next one, like this: // // .-------------------------. 16 byte boundary // | ChunkRecord: 16 bytes | // | - chunk id: 4 bytes | // | - producer id: 2 bytes | // | - writer id: 2 bytes | // | - #fragments: 2 bytes | // +-----+ - record size: 2 bytes | // | | - flags+pad: 4 bytes | // | +-------------------------+ // | | | // | : Chunk payload : // | | | // | +-------------------------+ // | | Optional padding | // +---> +-------------------------+ 16 byte boundary // | ChunkRecord | // : : // Chunks stored in the buffer are always rounded up to 16 bytes (that is // sizeof(ChunkRecord)), in order to avoid further inner fragmentation. // Special "padding" chunks can be put in the buffer, e.g. in the case when we // try to write a chunk of size N while the write pointer is at the end of the // buffer, but the write pointer is < N bytes from the end (and hence needs to // wrap over). // Because of this, the buffer is self-describing: the contents of the buffer // can be reconstructed by just looking at the buffer content (this will be // quite useful in future to recover the buffer from crash reports). // // However, in order to keep some operations (patching and reading) fast, a // lookaside index is maintained (in |index_|), keeping each chunk in the buffer // indexed by their {ProducerID, WriterID, ChunkID} tuple. // // Patching data out-of-band // ------------------------- // This buffer also supports patching chunks' payload out-of-band, after they // have been stored. This is to allow producers to backfill the "size" fields // of the protos that spawn across several chunks, when the previous chunks are // returned to the service. The MaybePatchChunkContents() deals with the fact // that a chunk might have been lost (because of wrapping) by the time the OOB // IPC comes. // // Reading from the buffer // ----------------------- // This class supports one reader only (the consumer). Reads are NOT idempotent // as they move the read cursors around. Reading back the buffer is the most // conceptually complex part. The ReadNextTracePacket() method operates with // whole packet granularity. Packets are returned only when all their fragments // are available. // This class takes care of: // - Gluing packets within the same sequence, even if they are not stored // adjacently in the buffer. // - Re-ordering chunks within a sequence (using the ChunkID, which wraps). // - Detecting holes in packet fragments (because of loss of chunks). // Reads guarantee that packets for the same sequence are read in FIFO order // (according to their ChunkID), but don't give any guarantee about the read // order of packets from different sequences, see comments in // ReadNextTracePacket() below. class TraceBuffer { … }; } // namespace perfetto #endif // SRC_TRACING_SERVICE_TRACE_BUFFER_H_