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