• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <type_traits>
25 
26 #include "perfetto/base/export.h"
27 #include "perfetto/base/logging.h"
28 #include "perfetto/protozero/contiguous_memory_range.h"
29 #include "perfetto/protozero/proto_utils.h"
30 #include "perfetto/protozero/scattered_stream_writer.h"
31 
32 namespace perfetto {
33 namespace shm_fuzz {
34 class FakeProducer;
35 }  // namespace shm_fuzz
36 }  // namespace perfetto
37 
38 namespace protozero {
39 
40 class MessageHandleBase;
41 
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.
46 class PERFETTO_EXPORT Message {
47  public:
48   friend class MessageHandleBase;
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;
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 = 10;
55 
56   // Ctor and Dtor of Message are never called, with the exeception
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).
62   Message() = default;
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.
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 (minus |size_already_written| below). This is the mechanism
76   // used by messages to backfill their corresponding size field in the parent
77   // message.
size_field()78   uint8_t* size_field() const { return size_field_; }
set_size_field(uint8_t * 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.
inc_size_already_written(uint32_t sz)84   void inc_size_already_written(uint32_t sz) { size_already_written_ += sz; }
85 
nested_message()86   Message* nested_message() { return nested_message_; }
87 
is_finalized()88   bool is_finalized() const { return finalized_; }
89 
90 #if PERFETTO_DCHECK_IS_ON()
set_handle(MessageHandleBase * handle)91   void set_handle(MessageHandleBase* handle) { handle_ = handle; }
92 #endif
93 
94   // Proto types: uint64, uint32, int64, int32, bool, enum.
95   template <typename T>
AppendVarInt(uint32_t field_id,T value)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>
AppendSignedVarInt(uint32_t field_id,T value)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.
AppendTinyVarInt(uint32_t field_id,int32_t value)117   void AppendTinyVarInt(uint32_t field_id, int32_t value) {
118     PERFETTO_DCHECK(0 <= value && value < 0x80);
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>
AppendFixed(uint32_t field_id,T value)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   // Append raw bytes for a field, using the supplied |ranges| to
150   // copy from |num_ranges| individual buffers.
151   size_t AppendScatteredBytes(uint32_t field_id,
152                               ContiguousMemoryRange* ranges,
153                               size_t num_ranges);
154 
155   // Begins a nested message, using the static storage provided by the parent
156   // class (see comment in |nested_messages_arena_|). The nested message ends
157   // either when Finalize() is called or when any other Append* method is called
158   // in the parent class.
159   // The template argument T is supposed to be a stub class auto generated from
160   // a .proto, hence a subclass of Message.
161   template <class T>
BeginNestedMessage(uint32_t field_id)162   T* BeginNestedMessage(uint32_t field_id) {
163     // This is to prevent subclasses (which should be autogenerated, though), to
164     // introduce extra state fields (which wouldn't be initialized by Reset()).
165     static_assert(std::is_base_of<Message, T>::value,
166                   "T must be a subclass of Message");
167     static_assert(sizeof(T) == sizeof(Message),
168                   "Message subclasses cannot introduce extra state.");
169     T* message = reinterpret_cast<T*>(nested_messages_arena_);
170     BeginNestedMessageInternal(field_id, message);
171     return message;
172   }
173 
174  private:
175   Message(const Message&) = delete;
176   Message& operator=(const Message&) = delete;
177 
178   void BeginNestedMessageInternal(uint32_t field_id, Message*);
179 
180   // Called by Finalize and Append* methods.
181   void EndNestedMessage();
182 
WriteToStream(const uint8_t * src_begin,const uint8_t * src_end)183   void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) {
184     PERFETTO_DCHECK(!finalized_);
185     PERFETTO_DCHECK(src_begin <= src_end);
186     const uint32_t size = static_cast<uint32_t>(src_end - src_begin);
187     stream_writer_->WriteBytes(src_begin, size);
188     size_ += size;
189   }
190 
191   // Only POD fields are allowed. This class's dtor is never called.
192   // See the comment on the static_assert in the the corresponding .cc file.
193 
194   // The stream writer interface used for the serialization.
195   ScatteredStreamWriter* stream_writer_;
196 
197   uint8_t* size_field_;
198 
199   // Keeps track of the size of the current message.
200   uint32_t size_;
201 
202   // See comment for inc_size_already_written().
203   uint32_t size_already_written_;
204 
205   // When true, no more changes to the message are allowed. This is to DCHECK
206   // attempts of writing to a message which has been Finalize()-d.
207   bool finalized_;
208 
209   // Used to detect attemps to create messages with a nesting level >
210   // kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages.
211   uint8_t nesting_depth_;
212 
213 #if PERFETTO_DCHECK_IS_ON()
214   // Current generation of message. Incremented on Reset.
215   // Used to detect stale handles.
216   uint32_t generation_;
217 
218   MessageHandleBase* handle_;
219 #endif
220 
221   // Pointer to the last child message created through BeginNestedMessage(), if
222   // any, nullptr otherwise. There is no need to keep track of more than one
223   // message per nesting level as the proto-zero API contract mandates that
224   // nested fields can be filled only in a stacked fashion. In other words,
225   // nested messages are finalized and sealed when any other field is set in the
226   // parent message (or the parent message itself is finalized) and cannot be
227   // accessed anymore afterwards.
228   // TODO(primiano): optimization: I think that nested_message_, when non-null.
229   // will always be @ (this) + offsetof(nested_messages_arena_).
230   Message* nested_message_;
231 
232   // The root message owns the storage for all its nested messages, up to a max
233   // of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of
234   // the arena are meaningful only for the root message.
235   // Unfortunately we cannot put the sizeof() math here because we cannot sizeof
236   // the current class in a header. However the .cc file has a static_assert
237   // that guarantees that (see the Reset() method in the .cc file).
238   alignas(sizeof(void*)) uint8_t nested_messages_arena_[512];
239 
240   // DO NOT add any fields below |nested_messages_arena_|. The memory layout of
241   // nested messages would overflow the storage allocated by the root message.
242 };
243 
244 }  // namespace protozero
245 
246 #endif  // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_
247