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