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