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 MessageArena; 42 class MessageHandleBase; 43 44 // Base class extended by the proto C++ stubs generated by the ProtoZero 45 // compiler. This class provides the minimal runtime required to support 46 // append-only operations and is designed for performance. None of the methods 47 // require any dynamic memory allocation, unless more than 16 nested messages 48 // are created via BeginNestedMessage() calls. 49 class PERFETTO_EXPORT Message { 50 public: 51 friend class MessageHandleBase; 52 53 // The ctor is deliberately a no-op to avoid forwarding args from all 54 // subclasses. The real initialization is performed by Reset(). 55 // Nested messages are allocated via placement new by MessageArena and 56 // implictly destroyed when the RootMessage's arena goes away. This is 57 // fine as long as all the fields are PODs, which is checked by the 58 // static_assert()s in the Reset() method. 59 Message() = default; 60 61 // Clears up the state, allowing the message to be reused as a fresh one. 62 void Reset(ScatteredStreamWriter*, MessageArena*); 63 64 // Commits all the changes to the buffer (backfills the size field of this and 65 // all nested messages) and seals the message. Returns the size of the message 66 // (and all nested sub-messages), without taking into account any chunking. 67 // Finalize is idempotent and can be called several times w/o side effects. 68 uint32_t Finalize(); 69 70 // Optional. If is_valid() == true, the corresponding memory region (its 71 // length == proto_utils::kMessageLengthFieldSize) is backfilled with the size 72 // of this message (minus |size_already_written| below). This is the mechanism 73 // used by messages to backfill their corresponding size field in the parent 74 // message. size_field()75 uint8_t* size_field() const { return size_field_; } set_size_field(uint8_t * size_field)76 void set_size_field(uint8_t* size_field) { size_field_ = size_field; } 77 78 // This is to deal with case of backfilling the size of a root (non-nested) 79 // message which is split into multiple chunks. Upon finalization only the 80 // partial size that lies in the last chunk has to be backfilled. inc_size_already_written(uint32_t sz)81 void inc_size_already_written(uint32_t sz) { size_already_written_ += sz; } 82 nested_message()83 Message* nested_message() { return nested_message_; } 84 is_finalized()85 bool is_finalized() const { return finalized_; } 86 87 #if PERFETTO_DCHECK_IS_ON() set_handle(MessageHandleBase * handle)88 void set_handle(MessageHandleBase* handle) { handle_ = handle; } 89 #endif 90 91 // Proto types: uint64, uint32, int64, int32, bool, enum. 92 template <typename T> AppendVarInt(uint32_t field_id,T value)93 void AppendVarInt(uint32_t field_id, T value) { 94 if (nested_message_) 95 EndNestedMessage(); 96 97 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 98 uint8_t* pos = buffer; 99 100 pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos); 101 // WriteVarInt encodes signed values in two's complement form. 102 pos = proto_utils::WriteVarInt(value, pos); 103 WriteToStream(buffer, pos); 104 } 105 106 // Proto types: sint64, sint32. 107 template <typename T> AppendSignedVarInt(uint32_t field_id,T value)108 void AppendSignedVarInt(uint32_t field_id, T value) { 109 AppendVarInt(field_id, proto_utils::ZigZagEncode(value)); 110 } 111 112 // Proto types: bool, enum (small). 113 // Faster version of AppendVarInt for tiny numbers. AppendTinyVarInt(uint32_t field_id,int32_t value)114 void AppendTinyVarInt(uint32_t field_id, int32_t value) { 115 PERFETTO_DCHECK(0 <= value && value < 0x80); 116 if (nested_message_) 117 EndNestedMessage(); 118 119 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 120 uint8_t* pos = buffer; 121 // MakeTagVarInt gets super optimized here for constexpr. 122 pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos); 123 *pos++ = static_cast<uint8_t>(value); 124 WriteToStream(buffer, pos); 125 } 126 127 // Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float. 128 template <typename T> AppendFixed(uint32_t field_id,T value)129 void AppendFixed(uint32_t field_id, T value) { 130 if (nested_message_) 131 EndNestedMessage(); 132 133 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 134 uint8_t* pos = buffer; 135 136 pos = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<T>(field_id), pos); 137 memcpy(pos, &value, sizeof(T)); 138 pos += sizeof(T); 139 // TODO: Optimize memcpy performance, see http://crbug.com/624311 . 140 WriteToStream(buffer, pos); 141 } 142 143 void AppendString(uint32_t field_id, const char* str); 144 AppendString(uint32_t field_id,const std::string & str)145 void AppendString(uint32_t field_id, const std::string& str) { 146 AppendBytes(field_id, str.data(), str.size()); 147 } 148 149 void AppendBytes(uint32_t field_id, const void* value, size_t size); 150 151 // Append raw bytes for a field, using the supplied |ranges| to 152 // copy from |num_ranges| individual buffers. 153 size_t AppendScatteredBytes(uint32_t field_id, 154 ContiguousMemoryRange* ranges, 155 size_t num_ranges); 156 157 // Begins a nested message. The returned object is owned by the MessageArena 158 // of the root message. The nested message ends either when Finalize() is 159 // called or when any other Append* method is called in the parent class. 160 // The template argument T is supposed to be a stub class auto generated from 161 // a .proto, hence a subclass of Message. 162 template <class T> BeginNestedMessage(uint32_t field_id)163 T* BeginNestedMessage(uint32_t field_id) { 164 // This is to prevent subclasses (which should be autogenerated, though), to 165 // introduce extra state fields (which wouldn't be initialized by Reset()). 166 static_assert(std::is_base_of<Message, T>::value, 167 "T must be a subclass of Message"); 168 static_assert(sizeof(T) == sizeof(Message), 169 "Message subclasses cannot introduce extra state."); 170 return static_cast<T*>(BeginNestedMessageInternal(field_id)); 171 } 172 173 // Gives read-only access to the underlying stream_writer. This is used only 174 // by few internals to query the state of the underlying buffer. It is almost 175 // always a bad idea to poke at the stream_writer() internals. stream_writer()176 const ScatteredStreamWriter* stream_writer() const { return stream_writer_; } 177 178 // Appends some raw bytes to the message. The use-case for this is preserving 179 // unknown fields in the decode -> re-encode path of xxx.gen.cc classes 180 // generated by the cppgen_plugin.cc. 181 // The caller needs to guarantee that the appended data is properly 182 // proto-encoded and each field has a proto preamble. AppendRawProtoBytes(const void * data,size_t size)183 void AppendRawProtoBytes(const void* data, size_t size) { 184 const uint8_t* src = reinterpret_cast<const uint8_t*>(data); 185 WriteToStream(src, src + size); 186 } 187 188 private: 189 Message(const Message&) = delete; 190 Message& operator=(const Message&) = delete; 191 192 Message* BeginNestedMessageInternal(uint32_t field_id); 193 194 // Called by Finalize and Append* methods. 195 void EndNestedMessage(); 196 WriteToStream(const uint8_t * src_begin,const uint8_t * src_end)197 void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) { 198 PERFETTO_DCHECK(!finalized_); 199 PERFETTO_DCHECK(src_begin <= src_end); 200 const uint32_t size = static_cast<uint32_t>(src_end - src_begin); 201 stream_writer_->WriteBytes(src_begin, size); 202 size_ += size; 203 } 204 205 // Only POD fields are allowed. This class's dtor is never called. 206 // See the comment on the static_assert in the corresponding .cc file. 207 208 // The stream writer interface used for the serialization. 209 ScatteredStreamWriter* stream_writer_; 210 211 // The storage used to allocate nested Message objects. 212 // This is owned by RootMessage<T>. 213 MessageArena* arena_; 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 Message* nested_message_; 223 224 // [optional] Pointer to a non-aligned pre-reserved var-int slot of 225 // kMessageLengthFieldSize bytes. When set, the Finalize() method will write 226 // the size of proto-encoded message in the pointed memory region. 227 uint8_t* size_field_; 228 229 // Keeps track of the size of the current message. 230 uint32_t size_; 231 232 // See comment for inc_size_already_written(). 233 uint32_t size_already_written_; 234 235 // When true, no more changes to the message are allowed. This is to DCHECK 236 // attempts of writing to a message which has been Finalize()-d. 237 bool finalized_; 238 239 #if PERFETTO_DCHECK_IS_ON() 240 // Current generation of message. Incremented on Reset. 241 // Used to detect stale handles. 242 uint32_t generation_; 243 244 MessageHandleBase* handle_; 245 #endif 246 }; 247 248 } // namespace protozero 249 250 #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ 251