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_COMPONENT 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 // Short messages may be compacted in memory into the size field, since their 69 // size can be represented with fewer than 70 // proto_utils::kMessageLengthFieldSize bytes. 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. This is the mechanism used by messages to backfill their 76 // corresponding size field in the parent message. In most cases this is only 77 // used for nested messages and the ScatteredStreamWriter::Delegate (e.g. 78 // TraceWriterImpl), takes case of the outer message. size_field()79 uint8_t* size_field() const { return size_field_; } set_size_field(uint8_t * size_field)80 void set_size_field(uint8_t* size_field) { size_field_ = size_field; } 81 nested_message()82 Message* nested_message() { return nested_message_; } 83 is_finalized()84 bool is_finalized() const { 85 return message_state_ != MessageState::kNotFinalized; 86 } 87 88 #if PERFETTO_DCHECK_IS_ON() set_handle(MessageHandleBase * handle)89 void set_handle(MessageHandleBase* handle) { handle_ = handle; } 90 #endif 91 92 // Proto types: uint64, uint32, int64, int32, bool, enum. 93 template <typename T> AppendVarInt(uint32_t field_id,T value)94 void AppendVarInt(uint32_t field_id, T value) { 95 if (nested_message_) 96 EndNestedMessage(); 97 98 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 99 uint8_t* pos = buffer; 100 101 pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos); 102 // WriteVarInt encodes signed values in two's complement form. 103 pos = proto_utils::WriteVarInt(value, pos); 104 WriteToStream(buffer, pos); 105 } 106 107 // Proto types: sint64, sint32. 108 template <typename T> AppendSignedVarInt(uint32_t field_id,T value)109 void AppendSignedVarInt(uint32_t field_id, T value) { 110 AppendVarInt(field_id, proto_utils::ZigZagEncode(value)); 111 } 112 113 // Proto types: bool, enum (small). 114 // Faster version of AppendVarInt for tiny numbers. AppendTinyVarInt(uint32_t field_id,int32_t value)115 void AppendTinyVarInt(uint32_t field_id, int32_t value) { 116 PERFETTO_DCHECK(0 <= value && value < 0x80); 117 if (nested_message_) 118 EndNestedMessage(); 119 120 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 121 uint8_t* pos = buffer; 122 // MakeTagVarInt gets super optimized here for constexpr. 123 pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos); 124 *pos++ = static_cast<uint8_t>(value); 125 WriteToStream(buffer, pos); 126 } 127 128 // Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float. 129 template <typename T> AppendFixed(uint32_t field_id,T value)130 void AppendFixed(uint32_t field_id, T value) { 131 if (nested_message_) 132 EndNestedMessage(); 133 134 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize]; 135 uint8_t* pos = buffer; 136 137 pos = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<T>(field_id), pos); 138 memcpy(pos, &value, sizeof(T)); 139 pos += sizeof(T); 140 // TODO: Optimize memcpy performance, see http://crbug.com/624311 . 141 WriteToStream(buffer, pos); 142 } 143 144 void AppendString(uint32_t field_id, const char* str); 145 AppendString(uint32_t field_id,const std::string & str)146 void AppendString(uint32_t field_id, const std::string& str) { 147 AppendBytes(field_id, str.data(), str.size()); 148 } 149 150 void AppendBytes(uint32_t field_id, const void* value, size_t size); 151 152 // Append raw bytes for a field, using the supplied |ranges| to 153 // copy from |num_ranges| individual buffers. 154 size_t AppendScatteredBytes(uint32_t field_id, 155 ContiguousMemoryRange* ranges, 156 size_t num_ranges); 157 158 // Begins a nested message. The returned object is owned by the MessageArena 159 // of the root message. The nested message ends either when Finalize() is 160 // called or when any other Append* method is called in the parent class. 161 // The template argument T is supposed to be a stub class auto generated from 162 // a .proto, hence a subclass of Message. 163 template <class T> BeginNestedMessage(uint32_t field_id)164 T* BeginNestedMessage(uint32_t field_id) { 165 // This is to prevent subclasses (which should be autogenerated, though), to 166 // introduce extra state fields (which wouldn't be initialized by Reset()). 167 static_assert(std::is_base_of<Message, T>::value, 168 "T must be a subclass of Message"); 169 static_assert(sizeof(T) == sizeof(Message), 170 "Message subclasses cannot introduce extra state."); 171 return static_cast<T*>(BeginNestedMessageInternal(field_id)); 172 } 173 174 // Gives read-only access to the underlying stream_writer. This is used only 175 // by few internals to query the state of the underlying buffer. It is almost 176 // always a bad idea to poke at the stream_writer() internals. stream_writer()177 const ScatteredStreamWriter* stream_writer() const { return stream_writer_; } 178 179 // Appends some raw bytes to the message. The use-case for this is preserving 180 // unknown fields in the decode -> re-encode path of xxx.gen.cc classes 181 // generated by the cppgen_plugin.cc. 182 // The caller needs to guarantee that the appended data is properly 183 // proto-encoded and each field has a proto preamble. AppendRawProtoBytes(const void * data,size_t size)184 void AppendRawProtoBytes(const void* data, size_t size) { 185 if (nested_message_) 186 EndNestedMessage(); 187 const uint8_t* src = reinterpret_cast<const uint8_t*>(data); 188 WriteToStream(src, src + size); 189 } 190 191 private: 192 Message(const Message&) = delete; 193 Message& operator=(const Message&) = delete; 194 195 Message* BeginNestedMessageInternal(uint32_t field_id); 196 197 // Called by Finalize and Append* methods. 198 void EndNestedMessage(); 199 WriteToStream(const uint8_t * src_begin,const uint8_t * src_end)200 void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) { 201 PERFETTO_DCHECK(!is_finalized()); 202 PERFETTO_DCHECK(src_begin <= src_end); 203 const uint32_t size = static_cast<uint32_t>(src_end - src_begin); 204 stream_writer_->WriteBytes(src_begin, size); 205 size_ += size; 206 } 207 208 // Only POD fields are allowed. This class's dtor is never called. 209 // See the comment on the static_assert in the corresponding .cc file. 210 211 // The stream writer interface used for the serialization. 212 ScatteredStreamWriter* stream_writer_; 213 214 // The storage used to allocate nested Message objects. 215 // This is owned by RootMessage<T>. 216 MessageArena* arena_; 217 218 // Pointer to the last child message created through BeginNestedMessage(), if 219 // any, nullptr otherwise. There is no need to keep track of more than one 220 // message per nesting level as the proto-zero API contract mandates that 221 // nested fields can be filled only in a stacked fashion. In other words, 222 // nested messages are finalized and sealed when any other field is set in the 223 // parent message (or the parent message itself is finalized) and cannot be 224 // accessed anymore afterwards. 225 Message* nested_message_; 226 227 // [optional] Pointer to a non-aligned pre-reserved var-int slot of 228 // kMessageLengthFieldSize bytes. When set, the Finalize() method will write 229 // the size of proto-encoded message in the pointed memory region. 230 uint8_t* size_field_; 231 232 // Keeps track of the size of the current message. 233 uint32_t size_; 234 235 enum class MessageState : uint8_t { 236 // Message is still being written to. 237 kNotFinalized, 238 // Finalized, no more changes to the message are allowed. This is to DCHECK 239 // attempts of writing to a message which has been Finalize()-d. 240 kFinalized, 241 // Finalized, and additionally the message data has been partially or fully 242 // compacted into the last 3 bytes of `size_field_`. See the comment in 243 // Finalize(). 244 kFinalizedWithCompaction, 245 }; 246 247 MessageState message_state_; 248 249 #if PERFETTO_DCHECK_IS_ON() 250 // Current generation of message. Incremented on Reset. 251 // Used to detect stale handles. 252 uint32_t generation_; 253 254 MessageHandleBase* handle_; 255 #endif 256 }; 257 258 } // namespace protozero 259 260 #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ 261