1 /* 2 * Copyright (C) 2017 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_H_ 18 #define INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ 19 20 #include <assert.h> 21 #include <stdint.h> 22 #include <string.h> 23 24 #include <type_traits> 25 26 #include "perfetto/base/export.h" 27 #include "perfetto/base/logging.h" 28 #include "perfetto/protozero/contiguous_memory_range.h" 29 #include "perfetto/protozero/proto_utils.h" 30 #include "perfetto/protozero/scattered_stream_writer.h" 31 32 namespace perfetto { 33 namespace shm_fuzz { 34 class FakeProducer; 35 } // namespace shm_fuzz 36 } // namespace perfetto 37 38 namespace protozero { 39 40 class MessageHandleBase; 41 42 // Base class extended by the proto C++ stubs generated by the ProtoZero 43 // compiler. This class provides the minimal runtime required to support 44 // append-only operations and is designed for performance. None of the methods 45 // require any dynamic memory allocation. 46 class PERFETTO_EXPORT Message { 47 public: 48 friend class MessageHandleBase; 49 // Grant end_to_end_shared_memory_fuzzer access in order to write raw 50 // bytes into the buffer. 51 friend class ::perfetto::shm_fuzz::FakeProducer; 52 // Adjust the |nested_messages_arena_| size when changing this, or the 53 // static_assert in the .cc file will bark. 54 static constexpr uint32_t kMaxNestingDepth = 8; 55 56 // Ctor and Dtor of Message are never called, with the exeception 57 // of root (non-nested) messages. Nested messages are allocated via placement 58 // new in the |nested_messages_arena_| and implictly destroyed when the arena 59 // of the root message goes away. This is fine as long as all the fields are 60 // PODs, which is checked by the static_assert in the ctor (see the Reset() 61 // method in the .cc file). 62 Message() = default; 63 64 // Clears up the state, allowing the message to be reused as a fresh one. 65 void Reset(ScatteredStreamWriter*); 66 67 // Commits all the changes to the buffer (backfills the size field of this and 68 // all nested messages) and seals the message. Returns the size of the message 69 // (and all nested sub-messages), without taking into account any chunking. 70 // Finalize is idempotent and can be called several times w/o side effects. 71 uint32_t Finalize(); 72 73 // Optional. If is_valid() == true, the corresponding memory region (its 74 // length == proto_utils::kMessageLengthFieldSize) is backfilled with the size 75 // of this message (minus |size_already_written| below). This is the mechanism 76 // used by messages to backfill their corresponding size field in the parent 77 // message. 78 uint8_t* size_field() const { return size_field_; } 79 void set_size_field(uint8_t* size_field) { size_field_ = size_field; } 80 81 // This is to deal with case of backfilling the size of a root (non-nested) 82 // message which is split into multiple chunks. Upon finalization only the 83 // partial size that lies in the last chunk has to be backfilled. 84 void inc_size_already_written(uint32_t sz) { size_already_written_ += sz; } 85 86 Message* nested_message() { return nested_message_; } 87 88 bool is_finalized() const { return finalized_; } 89 90 #if PERFETTO_DCHECK_IS_ON() 91 void set_handle(MessageHandleBase* handle) { handle_ = handle; } 92 #endif 93 94 // Proto types: uint64, uint32, int64, int32, bool, enum. 95 template <typename T> 96 void AppendVarInt(uint32_t field_id, T value) { 97 if (nested_message_) 98 EndNestedMessage(); 99 100 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 101 uint8_t* pos = buffer; 102 103 pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos); 104 // WriteVarInt encodes signed values in two's complement form. 105 pos = proto_utils::WriteVarInt(value, pos); 106 WriteToStream(buffer, pos); 107 } 108 109 // Proto types: sint64, sint32. 110 template <typename T> 111 void AppendSignedVarInt(uint32_t field_id, T value) { 112 AppendVarInt(field_id, proto_utils::ZigZagEncode(value)); 113 } 114 115 // Proto types: bool, enum (small). 116 // Faster version of AppendVarInt for tiny numbers. 117 void AppendTinyVarInt(uint32_t field_id, int32_t value) { 118 PERFETTO_DCHECK(0 <= value && value < 0x80); 119 if (nested_message_) 120 EndNestedMessage(); 121 122 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 123 uint8_t* pos = buffer; 124 // MakeTagVarInt gets super optimized here for constexpr. 125 pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos); 126 *pos++ = static_cast<uint8_t>(value); 127 WriteToStream(buffer, pos); 128 } 129 130 // Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float. 131 template <typename T> 132 void AppendFixed(uint32_t field_id, T value) { 133 if (nested_message_) 134 EndNestedMessage(); 135 136 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 137 uint8_t* pos = buffer; 138 139 pos = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<T>(field_id), pos); 140 memcpy(pos, &value, sizeof(T)); 141 pos += sizeof(T); 142 // TODO: Optimize memcpy performance, see http://crbug.com/624311 . 143 WriteToStream(buffer, pos); 144 } 145 146 void AppendString(uint32_t field_id, const char* str); 147 void AppendBytes(uint32_t field_id, const void* value, size_t size); 148 149 // Begins a nested message, using the static storage provided by the parent 150 // class (see comment in |nested_messages_arena_|). The nested message ends 151 // either when Finalize() is called or when any other Append* method is called 152 // in the parent class. 153 // The template argument T is supposed to be a stub class auto generated from 154 // a .proto, hence a subclass of Message. 155 template <class T> 156 T* BeginNestedMessage(uint32_t field_id) { 157 // This is to prevent subclasses (which should be autogenerated, though), to 158 // introduce extra state fields (which wouldn't be initialized by Reset()). 159 static_assert(std::is_base_of<Message, T>::value, 160 "T must be a subclass of Message"); 161 static_assert(sizeof(T) == sizeof(Message), 162 "Message subclasses cannot introduce extra state."); 163 T* message = reinterpret_cast<T*>(nested_messages_arena_); 164 BeginNestedMessageInternal(field_id, message); 165 return message; 166 } 167 168 private: 169 Message(const Message&) = delete; 170 Message& operator=(const Message&) = delete; 171 172 void BeginNestedMessageInternal(uint32_t field_id, Message*); 173 174 // Called by Finalize and Append* methods. 175 void EndNestedMessage(); 176 177 void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) { 178 PERFETTO_DCHECK(!finalized_); 179 PERFETTO_DCHECK(src_begin <= src_end); 180 const uint32_t size = static_cast<uint32_t>(src_end - src_begin); 181 stream_writer_->WriteBytes(src_begin, size); 182 size_ += size; 183 } 184 185 // Only POD fields are allowed. This class's dtor is never called. 186 // See the comment on the static_assert in the the corresponding .cc file. 187 188 // The stream writer interface used for the serialization. 189 ScatteredStreamWriter* stream_writer_; 190 191 uint8_t* size_field_; 192 193 // Keeps track of the size of the current message. 194 uint32_t size_; 195 196 // See comment for inc_size_already_written(). 197 uint32_t size_already_written_; 198 199 // When true, no more changes to the message are allowed. This is to DCHECK 200 // attempts of writing to a message which has been Finalize()-d. 201 bool finalized_; 202 203 // Used to detect attemps to create messages with a nesting level > 204 // kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages. 205 uint8_t nesting_depth_; 206 207 #if PERFETTO_DCHECK_IS_ON() 208 // Current generation of message. Incremented on Reset. 209 // Used to detect stale handles. 210 uint32_t generation_; 211 212 MessageHandleBase* handle_; 213 #endif 214 215 // Pointer to the last child message created through BeginNestedMessage(), if 216 // any, nullptr otherwise. There is no need to keep track of more than one 217 // message per nesting level as the proto-zero API contract mandates that 218 // nested fields can be filled only in a stacked fashion. In other words, 219 // nested messages are finalized and sealed when any other field is set in the 220 // parent message (or the parent message itself is finalized) and cannot be 221 // accessed anymore afterwards. 222 // TODO(primiano): optimization: I think that nested_message_, when non-null. 223 // will always be @ (this) + offsetof(nested_messages_arena_). 224 Message* nested_message_; 225 226 // The root message owns the storage for all its nested messages, up to a max 227 // of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of 228 // the arena are meaningful only for the root message. 229 // Unfortunately we cannot put the sizeof() math here because we cannot sizeof 230 // the current class in a header. However the .cc file has a static_assert 231 // that guarantees that (see the Reset() method in the .cc file). 232 alignas(sizeof(void*)) uint8_t nested_messages_arena_[512]; 233 234 // DO NOT add any fields below |nested_messages_arena_|. The memory layout of 235 // nested messages would overflow the storage allocated by the root message. 236 }; 237 238 } // namespace protozero 239 240 #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ 241