1 /* 2 * Copyright (C) 2020 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 INCLUDE_PERFETTO_PROTOZERO_MESSAGE_ARENA_H_ 18 #define INCLUDE_PERFETTO_PROTOZERO_MESSAGE_ARENA_H_ 19 20 #include <stdint.h> 21 22 #include <forward_list> 23 #include <type_traits> 24 25 #include "perfetto/base/export.h" 26 #include "perfetto/base/logging.h" 27 #include "perfetto/protozero/message.h" 28 29 namespace protozero { 30 31 class Message; 32 33 // Object allocator for fixed-sized protozero::Message objects. 34 // It's a simple bump-pointer allocator which leverages the stack-alike 35 // usage pattern of protozero nested messages. It avoids hitting the system 36 // allocator in most cases, by reusing the same block, and falls back on 37 // allocating new blocks only when using deeply nested messages (which are 38 // extremely rare). 39 // This is used by RootMessage<T> to handle the storage for root-level messages. 40 class PERFETTO_EXPORT_COMPONENT MessageArena { 41 public: 42 MessageArena(); 43 ~MessageArena(); 44 45 // Strictly no copies or moves as this is used to hand out pointers. 46 MessageArena(const MessageArena&) = delete; 47 MessageArena& operator=(const MessageArena&) = delete; 48 MessageArena(MessageArena&&) = delete; 49 MessageArena& operator=(MessageArena&&) = delete; 50 51 // Allocates a new Message object. 52 Message* NewMessage(); 53 54 // Deletes the last message allocated. The |msg| argument is used only for 55 // DCHECKs, it MUST be the pointer obtained by the last NewMessage() call. DeleteLastMessage(Message * msg)56 void DeleteLastMessage(Message* msg) { 57 PERFETTO_DCHECK(!blocks_.empty() && blocks_.front().entries > 0); 58 PERFETTO_DCHECK(&blocks_.front().storage[blocks_.front().entries - 1] == 59 static_cast<void*>(msg)); 60 DeleteLastMessageInternal(); 61 } 62 63 // Resets the state of the arena, clearing up all but one block. This is used 64 // to avoid leaking outstanding unfinished sub-messages while recycling the 65 // RootMessage object (this is extremely rare due to the RAII scoped handles 66 // but could happen if some client does some overly clever std::move() trick). Reset()67 void Reset() { 68 PERFETTO_DCHECK(!blocks_.empty()); 69 blocks_.resize(1); 70 auto& block = blocks_.front(); 71 block.entries = 0; 72 PERFETTO_ASAN_POISON(block.storage, sizeof(block.storage)); 73 } 74 75 private: 76 void DeleteLastMessageInternal(); 77 78 struct Block { 79 static constexpr size_t kCapacity = 16; 80 BlockBlock81 Block() { PERFETTO_ASAN_POISON(storage, sizeof(storage)); } 82 83 std::aligned_storage<sizeof(Message), alignof(Message)>::type 84 storage[kCapacity]; 85 uint32_t entries = 0; // # Message entries used (<= kCapacity). 86 }; 87 88 // blocks are used to hand out pointers and must not be moved. Hence why 89 // std::list rather than std::vector. 90 std::forward_list<Block> blocks_; 91 }; 92 93 } // namespace protozero 94 95 #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_ARENA_H_ 96