/* * Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_TRACING_CORE_SHARED_MEMORY_ABI_H_ #define INCLUDE_PERFETTO_EXT_TRACING_CORE_SHARED_MEMORY_ABI_H_ #include <stddef.h> #include <stdint.h> #include <array> #include <atomic> #include <bitset> #include <thread> #include <type_traits> #include <utility> #include "perfetto/base/logging.h" #include "perfetto/protozero/proto_utils.h" namespace perfetto { // This file defines the binary interface of the memory buffers shared between // Producer and Service. This is a long-term stable ABI and has to be backwards // compatible to deal with mismatching Producer and Service versions. // // Overview // -------- // SMB := "Shared Memory Buffer". // In the most typical case of a multi-process architecture (i.e. Producer and // Service are hosted by different processes), a Producer means almost always // a "client process producing data" (almost: in some cases a process might host // > 1 Producer, if it links two libraries, independent of each other, that both // use Perfetto tracing). // The Service has one SMB for each Producer. // A producer has one or (typically) more data sources. They all share the same // SMB. // The SMB is a staging area to decouple data sources living in the Producer // and allow them to do non-blocking async writes. // The SMB is *not* the ultimate logging buffer seen by the Consumer. That one // is larger (~MBs) and not shared with Producers. // Each SMB is small, typically few KB. Its size is configurable by the producer // within a max limit of ~MB (see kMaxShmSize in tracing_service_impl.cc). // The SMB is partitioned into fixed-size Page(s). The size of the Pages are // determined by each Producer at connection time and cannot be changed. // Hence, different producers can have SMB(s) that have a different Page size // from each other, but the page size will be constant throughout all the // lifetime of the SMB. // Page(s) are partitioned by the Producer into variable size Chunk(s): // // +------------+ +--------------------------+ // | Producer 1 | <-> | SMB 1 [~32K - 1MB] | // +------------+ +--------+--------+--------+ // | Page | Page | Page | // +--------+--------+--------+ // | Chunk | | Chunk | // +--------+ Chunk +--------+ <----+ // | Chunk | | Chunk | | // +--------+--------+--------+ +---------------------+ // | Service | // +------------+ +--------------------------+ +---------------------+ // | Producer 2 | <-> | SMB 2 [~32K - 1MB] | /| large ring buffers | // +------------+ +--------+--------+--------+ <--+ | (100K - several MB) | // | Page | Page | Page | +---------------------+ // +--------+--------+--------+ // | Chunk | | Chunk | // +--------+ Chunk +--------+ // | Chunk | | Chunk | // +--------+--------+--------+ // // * Sizes of both SMB and ring buffers are purely indicative and decided at // configuration time by the Producer (for SMB sizes) and the Consumer (for the // final ring buffer size). // Page // ---- // A page is a portion of the shared memory buffer and defines the granularity // of the interaction between the Producer and tracing Service. When scanning // the shared memory buffer to determine if something should be moved to the // central logging buffers, the Service most of the times looks at and moves // whole pages. Similarly, the Producer sends an IPC to invite the Service to // drain the shared memory buffer only when a whole page is filled. // Having fixed the total SMB size (hence the total memory overhead), the page // size is a triangular tradeoff between: // 1) IPC traffic: smaller pages -> more IPCs. // 2) Producer lock freedom: larger pages -> larger chunks -> data sources can // write more data without needing to swap chunks and synchronize. // 3) Risk of write-starving the SMB: larger pages -> higher chance that the // Service won't manage to drain them and the SMB remains full. // The page size, on the other side, has no implications on wasted memory due to // fragmentations (see Chunk below). // The size of the page is chosen by the Service at connection time and stays // fixed throughout all the lifetime of the Producer. Different producers (i.e. // ~ different client processes) can use different page sizes. // The page size must be an integer multiple of 4k (this is to allow VM page // stealing optimizations) and obviously has to be an integer divisor of the // total SMB size. // Chunk // ----- // A chunk is a portion of a Page which is written and handled by a Producer. // A chunk contains a linear sequence of TracePacket(s) (the root proto). // A chunk cannot be written concurrently by two data sources. Protobufs must be // encoded as contiguous byte streams and cannot be interleaved. Therefore, on // the Producer side, a chunk is almost always owned exclusively by one thread // (% extremely peculiar slow-path cases). // Chunks are essentially single-writer single-thread lock-free arenas. Locking // happens only when a Chunk is full and a new one needs to be acquired. // Locking happens only within the scope of a Producer process. There is no // inter-process locking. The Producer cannot lock the Service and viceversa. // In the worst case, any of the two can starve the SMB, by marking all chunks // as either being read or written. But that has the only side effect of // losing the trace data. // The Producer can decide to partition each page into a number of limited // configurations (e.g., 1 page == 1 chunk, 1 page == 2 chunks and so on). // TracePacket // ----------- // Is the atom of tracing. Putting aside pages and chunks a trace is merely a // sequence of TracePacket(s). TracePacket is the root protobuf message. // A TracePacket can span across several chunks (hence even across several // pages). A TracePacket can therefore be >> chunk size, >> page size and even // >> SMB size. The Chunk header carries metadata to deal with the TracePacket // splitting case. // Use only explicitly-sized types below. DO NOT use size_t or any architecture // dependent size (e.g. size_t) in the struct fields. This buffer will be read // and written by processes that have a different bitness in the same OS. // Instead it's fine to assume little-endianess. Big-endian is a dream we are // not currently pursuing. class SharedMemoryABI { … }; } // namespace perfetto #endif // INCLUDE_PERFETTO_EXT_TRACING_CORE_SHARED_MEMORY_ABI_H_