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 = 10; 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. size_field()78 uint8_t* size_field() const { return size_field_; } set_size_field(uint8_t * 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. inc_size_already_written(uint32_t sz)84 void inc_size_already_written(uint32_t sz) { size_already_written_ += sz; } 85 nested_message()86 Message* nested_message() { return nested_message_; } 87 is_finalized()88 bool is_finalized() const { return finalized_; } 89 90 #if PERFETTO_DCHECK_IS_ON() set_handle(MessageHandleBase * handle)91 void set_handle(MessageHandleBase* handle) { handle_ = handle; } 92 #endif 93 94 // Proto types: uint64, uint32, int64, int32, bool, enum. 95 template <typename T> AppendVarInt(uint32_t field_id,T value)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> AppendSignedVarInt(uint32_t field_id,T value)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. AppendTinyVarInt(uint32_t field_id,int32_t value)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> AppendFixed(uint32_t field_id,T value)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 // Append raw bytes for a field, using the supplied |ranges| to 150 // copy from |num_ranges| individual buffers. 151 size_t AppendScatteredBytes(uint32_t field_id, 152 ContiguousMemoryRange* ranges, 153 size_t num_ranges); 154 155 // Begins a nested message, using the static storage provided by the parent 156 // class (see comment in |nested_messages_arena_|). The nested message ends 157 // either when Finalize() is called or when any other Append* method is called 158 // in the parent class. 159 // The template argument T is supposed to be a stub class auto generated from 160 // a .proto, hence a subclass of Message. 161 template <class T> BeginNestedMessage(uint32_t field_id)162 T* BeginNestedMessage(uint32_t field_id) { 163 // This is to prevent subclasses (which should be autogenerated, though), to 164 // introduce extra state fields (which wouldn't be initialized by Reset()). 165 static_assert(std::is_base_of<Message, T>::value, 166 "T must be a subclass of Message"); 167 static_assert(sizeof(T) == sizeof(Message), 168 "Message subclasses cannot introduce extra state."); 169 T* message = reinterpret_cast<T*>(nested_messages_arena_); 170 BeginNestedMessageInternal(field_id, message); 171 return message; 172 } 173 174 private: 175 Message(const Message&) = delete; 176 Message& operator=(const Message&) = delete; 177 178 void BeginNestedMessageInternal(uint32_t field_id, Message*); 179 180 // Called by Finalize and Append* methods. 181 void EndNestedMessage(); 182 WriteToStream(const uint8_t * src_begin,const uint8_t * src_end)183 void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) { 184 PERFETTO_DCHECK(!finalized_); 185 PERFETTO_DCHECK(src_begin <= src_end); 186 const uint32_t size = static_cast<uint32_t>(src_end - src_begin); 187 stream_writer_->WriteBytes(src_begin, size); 188 size_ += size; 189 } 190 191 // Only POD fields are allowed. This class's dtor is never called. 192 // See the comment on the static_assert in the the corresponding .cc file. 193 194 // The stream writer interface used for the serialization. 195 ScatteredStreamWriter* stream_writer_; 196 197 uint8_t* size_field_; 198 199 // Keeps track of the size of the current message. 200 uint32_t size_; 201 202 // See comment for inc_size_already_written(). 203 uint32_t size_already_written_; 204 205 // When true, no more changes to the message are allowed. This is to DCHECK 206 // attempts of writing to a message which has been Finalize()-d. 207 bool finalized_; 208 209 // Used to detect attemps to create messages with a nesting level > 210 // kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages. 211 uint8_t nesting_depth_; 212 213 #if PERFETTO_DCHECK_IS_ON() 214 // Current generation of message. Incremented on Reset. 215 // Used to detect stale handles. 216 uint32_t generation_; 217 218 MessageHandleBase* handle_; 219 #endif 220 221 // Pointer to the last child message created through BeginNestedMessage(), if 222 // any, nullptr otherwise. There is no need to keep track of more than one 223 // message per nesting level as the proto-zero API contract mandates that 224 // nested fields can be filled only in a stacked fashion. In other words, 225 // nested messages are finalized and sealed when any other field is set in the 226 // parent message (or the parent message itself is finalized) and cannot be 227 // accessed anymore afterwards. 228 // TODO(primiano): optimization: I think that nested_message_, when non-null. 229 // will always be @ (this) + offsetof(nested_messages_arena_). 230 Message* nested_message_; 231 232 // The root message owns the storage for all its nested messages, up to a max 233 // of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of 234 // the arena are meaningful only for the root message. 235 // Unfortunately we cannot put the sizeof() math here because we cannot sizeof 236 // the current class in a header. However the .cc file has a static_assert 237 // that guarantees that (see the Reset() method in the .cc file). 238 alignas(sizeof(void*)) uint8_t nested_messages_arena_[512]; 239 240 // DO NOT add any fields below |nested_messages_arena_|. The memory layout of 241 // nested messages would overflow the storage allocated by the root message. 242 }; 243 244 } // namespace protozero 245 246 #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ 247