• 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 
stream_writer_for_testing()173   ScatteredStreamWriter* stream_writer_for_testing() { return stream_writer_; }
174 
175   // Appends some raw bytes to the message. The use-case for this is preserving
176   // unknown fields in the decode -> re-encode path of xxx.gen.cc classes
177   // generated by the cppgen_plugin.cc.
178   // The caller needs to guarantee that the appended data is properly
179   // proto-encoded and each field has a proto preamble.
AppendRawProtoBytes(const void * data,size_t size)180   void AppendRawProtoBytes(const void* data, size_t size) {
181     const uint8_t* src = reinterpret_cast<const uint8_t*>(data);
182     WriteToStream(src, src + size);
183   }
184 
185  private:
186   Message(const Message&) = delete;
187   Message& operator=(const Message&) = delete;
188 
189   Message* BeginNestedMessageInternal(uint32_t field_id);
190 
191   // Called by Finalize and Append* methods.
192   void EndNestedMessage();
193 
WriteToStream(const uint8_t * src_begin,const uint8_t * src_end)194   void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) {
195     PERFETTO_DCHECK(!finalized_);
196     PERFETTO_DCHECK(src_begin <= src_end);
197     const uint32_t size = static_cast<uint32_t>(src_end - src_begin);
198     stream_writer_->WriteBytes(src_begin, size);
199     size_ += size;
200   }
201 
202   // Only POD fields are allowed. This class's dtor is never called.
203   // See the comment on the static_assert in the corresponding .cc file.
204 
205   // The stream writer interface used for the serialization.
206   ScatteredStreamWriter* stream_writer_;
207 
208   // The storage used to allocate nested Message objects.
209   // This is owned by RootMessage<T>.
210   MessageArena* arena_;
211 
212   // Pointer to the last child message created through BeginNestedMessage(), if
213   // any, nullptr otherwise. There is no need to keep track of more than one
214   // message per nesting level as the proto-zero API contract mandates that
215   // nested fields can be filled only in a stacked fashion. In other words,
216   // nested messages are finalized and sealed when any other field is set in the
217   // parent message (or the parent message itself is finalized) and cannot be
218   // accessed anymore afterwards.
219   Message* nested_message_;
220 
221   // [optional] Pointer to a non-aligned pre-reserved var-int slot of
222   // kMessageLengthFieldSize bytes. When set, the Finalize() method will write
223   // the size of proto-encoded message in the pointed memory region.
224   uint8_t* size_field_;
225 
226   // Keeps track of the size of the current message.
227   uint32_t size_;
228 
229   // See comment for inc_size_already_written().
230   uint32_t size_already_written_;
231 
232   // When true, no more changes to the message are allowed. This is to DCHECK
233   // attempts of writing to a message which has been Finalize()-d.
234   bool finalized_;
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 
245 }  // namespace protozero
246 
247 #endif  // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_
248