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 pos += sizeof(T); 141 // TODO: Optimize memcpy performance, see http://crbug.com/624311 . 142 WriteToStream(buffer, pos); 143 } 144 145 void AppendString(uint32_t field_id, const char* str); 146 AppendString(uint32_t field_id,const std::string & str)147 void AppendString(uint32_t field_id, const std::string& str) { 148 AppendBytes(field_id, str.data(), str.size()); 149 } 150 151 void AppendBytes(uint32_t field_id, const void* value, size_t size); 152 153 // Append raw bytes for a field, using the supplied |ranges| to 154 // copy from |num_ranges| individual buffers. 155 size_t AppendScatteredBytes(uint32_t field_id, 156 ContiguousMemoryRange* ranges, 157 size_t num_ranges); 158 159 // Begins a nested message, using the static storage provided by the parent 160 // class (see comment in |nested_messages_arena_|). The nested message ends 161 // either when Finalize() is called or when any other Append* method is called 162 // in the parent class. 163 // The template argument T is supposed to be a stub class auto generated from 164 // a .proto, hence a subclass of Message. 165 template <class T> BeginNestedMessage(uint32_t field_id)166 T* BeginNestedMessage(uint32_t field_id) { 167 // This is to prevent subclasses (which should be autogenerated, though), to 168 // introduce extra state fields (which wouldn't be initialized by Reset()). 169 static_assert(std::is_base_of<Message, T>::value, 170 "T must be a subclass of Message"); 171 static_assert(sizeof(T) == sizeof(Message), 172 "Message subclasses cannot introduce extra state."); 173 T* message = reinterpret_cast<T*>(nested_messages_arena_); 174 BeginNestedMessageInternal(field_id, message); 175 return message; 176 } 177 stream_writer_for_testing()178 ScatteredStreamWriter* stream_writer_for_testing() { return stream_writer_; } 179 180 // Appends some raw bytes to the message. The use-case for this is preserving 181 // unknown fields in the decode -> re-encode path of xxx.gen.cc classes 182 // generated by the cppgen_plugin.cc. 183 // The caller needs to guarantee that the appended data is properly 184 // proto-encoded and each field has a proto preamble. AppendRawProtoBytes(const void * data,size_t size)185 void AppendRawProtoBytes(const void* data, size_t size) { 186 const uint8_t* src = reinterpret_cast<const uint8_t*>(data); 187 WriteToStream(src, src + size); 188 } 189 190 private: 191 Message(const Message&) = delete; 192 Message& operator=(const Message&) = delete; 193 194 void BeginNestedMessageInternal(uint32_t field_id, Message*); 195 196 // Called by Finalize and Append* methods. 197 void EndNestedMessage(); 198 WriteToStream(const uint8_t * src_begin,const uint8_t * src_end)199 void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) { 200 PERFETTO_DCHECK(!finalized_); 201 PERFETTO_DCHECK(src_begin <= src_end); 202 const uint32_t size = static_cast<uint32_t>(src_end - src_begin); 203 stream_writer_->WriteBytes(src_begin, size); 204 size_ += size; 205 } 206 207 // Only POD fields are allowed. This class's dtor is never called. 208 // See the comment on the static_assert in the corresponding .cc file. 209 210 // The stream writer interface used for the serialization. 211 ScatteredStreamWriter* stream_writer_; 212 213 uint8_t* size_field_; 214 215 // Keeps track of the size of the current message. 216 uint32_t size_; 217 218 // See comment for inc_size_already_written(). 219 uint32_t size_already_written_; 220 221 // When true, no more changes to the message are allowed. This is to DCHECK 222 // attempts of writing to a message which has been Finalize()-d. 223 bool finalized_; 224 225 // Used to detect attemps to create messages with a nesting level > 226 // kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages. 227 uint8_t nesting_depth_; 228 229 #if PERFETTO_DCHECK_IS_ON() 230 // Current generation of message. Incremented on Reset. 231 // Used to detect stale handles. 232 uint32_t generation_; 233 234 MessageHandleBase* handle_; 235 #endif 236 237 // Pointer to the last child message created through BeginNestedMessage(), if 238 // any, nullptr otherwise. There is no need to keep track of more than one 239 // message per nesting level as the proto-zero API contract mandates that 240 // nested fields can be filled only in a stacked fashion. In other words, 241 // nested messages are finalized and sealed when any other field is set in the 242 // parent message (or the parent message itself is finalized) and cannot be 243 // accessed anymore afterwards. 244 // TODO(primiano): optimization: I think that nested_message_, when non-null. 245 // will always be @ (this) + offsetof(nested_messages_arena_). 246 Message* nested_message_; 247 248 // The root message owns the storage for all its nested messages, up to a max 249 // of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of 250 // the arena are meaningful only for the root message. 251 // Unfortunately we cannot put the sizeof() math here because we cannot sizeof 252 // the current class in a header. However the .cc file has a static_assert 253 // that guarantees that (see the Reset() method in the .cc file). 254 alignas(sizeof(void*)) uint8_t nested_messages_arena_[512]; 255 256 // DO NOT add any fields below |nested_messages_arena_|. The memory layout of 257 // nested messages would overflow the storage allocated by the root message. 258 }; 259 260 } // namespace protozero 261 262 #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ 263