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