Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 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 | |
Hector Dearman | e1e56b6 | 2018-02-21 19:11:58 +0000 | [diff] [blame] | 17 | #ifndef INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ |
| 18 | #define INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 19 | |
| 20 | #include <assert.h> |
| 21 | #include <stdint.h> |
| 22 | #include <string.h> |
| 23 | |
| 24 | #include <type_traits> |
| 25 | |
Oystein Eftevaag | a812a94 | 2018-03-23 11:52:32 -0700 | [diff] [blame] | 26 | #include "perfetto/base/export.h" |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 27 | #include "perfetto/base/logging.h" |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 28 | #include "perfetto/protozero/contiguous_memory_range.h" |
| 29 | #include "perfetto/protozero/proto_utils.h" |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 30 | #include "perfetto/protozero/scattered_stream_writer.h" |
| 31 | |
Florian Mayer | 60d1e13 | 2018-01-26 15:00:52 +0000 | [diff] [blame] | 32 | namespace perfetto { |
| 33 | namespace shm_fuzz { |
| 34 | class FakeProducer; |
| 35 | } // namespace shm_fuzz |
| 36 | } // namespace perfetto |
| 37 | |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 38 | namespace protozero { |
| 39 | |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 40 | class MessageHandleBase; |
Florian Mayer | 8a8044f | 2018-01-11 16:03:09 +0000 | [diff] [blame] | 41 | |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 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. |
Oystein Eftevaag | a812a94 | 2018-03-23 11:52:32 -0700 | [diff] [blame] | 46 | class PERFETTO_EXPORT Message { |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 47 | public: |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 48 | friend class MessageHandleBase; |
Florian Mayer | 60d1e13 | 2018-01-26 15:00:52 +0000 | [diff] [blame] | 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; |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 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 = 8; |
| 55 | |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 56 | // Ctor and Dtor of Message are never called, with the exeception |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 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). |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 62 | Message() = default; |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 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. |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 71 | uint32_t Finalize(); |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 72 | |
| 73 | // Optional. If is_valid() == true, the corresponding memory region (its |
| 74 | // length == proto_utils::kMessageLengthFieldSize) is backfilled with the size |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 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. |
| 78 | uint8_t* size_field() const { return 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. |
| 84 | void inc_size_already_written(uint32_t sz) { size_already_written_ += sz; } |
| 85 | |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 86 | Message* nested_message() { return nested_message_; } |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 87 | |
| 88 | bool is_finalized() const { return finalized_; } |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 89 | |
Florian Mayer | 8a8044f | 2018-01-11 16:03:09 +0000 | [diff] [blame] | 90 | #if PERFETTO_DCHECK_IS_ON() |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 91 | void set_handle(MessageHandleBase* handle) { handle_ = handle; } |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 92 | #endif |
| 93 | |
| 94 | // Proto types: uint64, uint32, int64, int32, bool, enum. |
| 95 | template <typename T> |
| 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> |
| 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. |
| 117 | void AppendTinyVarInt(uint32_t field_id, int32_t value) { |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 118 | PERFETTO_DCHECK(0 <= value && value < 0x80); |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 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> |
| 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 | // Begins a nested message, using the static storage provided by the parent |
| 150 | // class (see comment in |nested_messages_arena_|). The nested message ends |
| 151 | // either when Finalize() is called or when any other Append* method is called |
| 152 | // in the parent class. |
| 153 | // The template argument T is supposed to be a stub class auto generated from |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 154 | // a .proto, hence a subclass of Message. |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 155 | template <class T> |
| 156 | T* BeginNestedMessage(uint32_t field_id) { |
| 157 | // This is to prevent subclasses (which should be autogenerated, though), to |
| 158 | // introduce extra state fields (which wouldn't be initialized by Reset()). |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 159 | static_assert(std::is_base_of<Message, T>::value, |
| 160 | "T must be a subclass of Message"); |
| 161 | static_assert(sizeof(T) == sizeof(Message), |
| 162 | "Message subclasses cannot introduce extra state."); |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 163 | T* message = reinterpret_cast<T*>(nested_messages_arena_); |
| 164 | BeginNestedMessageInternal(field_id, message); |
| 165 | return message; |
| 166 | } |
| 167 | |
| 168 | private: |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 169 | Message(const Message&) = delete; |
| 170 | Message& operator=(const Message&) = delete; |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 171 | |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 172 | void BeginNestedMessageInternal(uint32_t field_id, Message*); |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 173 | |
| 174 | // Called by Finalize and Append* methods. |
| 175 | void EndNestedMessage(); |
| 176 | |
| 177 | void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) { |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 178 | PERFETTO_DCHECK(!finalized_); |
Hector Dearman | 8bf3e25 | 2018-01-26 11:33:58 +0000 | [diff] [blame] | 179 | PERFETTO_DCHECK(src_begin <= src_end); |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 180 | const uint32_t size = static_cast<uint32_t>(src_end - src_begin); |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 181 | stream_writer_->WriteBytes(src_begin, size); |
| 182 | size_ += size; |
| 183 | } |
| 184 | |
| 185 | // Only POD fields are allowed. This class's dtor is never called. |
| 186 | // See the comment on the static_assert in the the corresponding .cc file. |
| 187 | |
| 188 | // The stream writer interface used for the serialization. |
| 189 | ScatteredStreamWriter* stream_writer_; |
| 190 | |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 191 | uint8_t* size_field_; |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 192 | |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 193 | // Keeps track of the size of the current message. |
| 194 | uint32_t size_; |
| 195 | |
| 196 | // See comment for inc_size_already_written(). |
| 197 | uint32_t size_already_written_; |
| 198 | |
| 199 | // When true, no more changes to the message are allowed. This is to DCHECK |
| 200 | // attempts of writing to a message which has been Finalize()-d. |
| 201 | bool finalized_; |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 202 | |
| 203 | // Used to detect attemps to create messages with a nesting level > |
| 204 | // kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages. |
| 205 | uint8_t nesting_depth_; |
| 206 | |
Florian Mayer | 8a8044f | 2018-01-11 16:03:09 +0000 | [diff] [blame] | 207 | #if PERFETTO_DCHECK_IS_ON() |
| 208 | // Current generation of message. Incremented on Reset. |
| 209 | // Used to detect stale handles. |
| 210 | uint32_t generation_; |
| 211 | |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 212 | MessageHandleBase* handle_; |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 213 | #endif |
| 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. |
Primiano Tucci | 3a91887 | 2017-12-18 10:53:39 +0100 | [diff] [blame] | 222 | // TODO(primiano): optimization: I think that nested_message_, when non-null. |
| 223 | // will always be @ (this) + offsetof(nested_messages_arena_). |
Hector Dearman | aaa4c19 | 2018-02-19 11:57:35 +0000 | [diff] [blame] | 224 | Message* nested_message_; |
Primiano Tucci | 4f9b6d7 | 2017-12-05 20:59:16 +0000 | [diff] [blame] | 225 | |
| 226 | // The root message owns the storage for all its nested messages, up to a max |
| 227 | // of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of |
| 228 | // the arena are meaningful only for the root message. |
| 229 | // Unfortunately we cannot put the sizeof() math here because we cannot sizeof |
| 230 | // the current class in a header. However the .cc file has a static_assert |
| 231 | // that guarantees that (see the Reset() method in the .cc file). |
| 232 | alignas(sizeof(void*)) uint8_t nested_messages_arena_[512]; |
| 233 | |
| 234 | // DO NOT add any fields below |nested_messages_arena_|. The memory layout of |
| 235 | // nested messages would overflow the storage allocated by the root message. |
| 236 | }; |
| 237 | |
| 238 | } // namespace protozero |
| 239 | |
Hector Dearman | e1e56b6 | 2018-02-21 19:11:58 +0000 | [diff] [blame] | 240 | #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_ |