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