Home | History | Annotate | Download | only in protozero
      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 <type_traits>
     25 
     26 #include "perfetto/base/export.h"
     27 #include "perfetto/base/logging.h"
     28 #include "perfetto/protozero/contiguous_memory_range.h"
     29 #include "perfetto/protozero/proto_utils.h"
     30 #include "perfetto/protozero/scattered_stream_writer.h"
     31 
     32 namespace perfetto {
     33 namespace shm_fuzz {
     34 class FakeProducer;
     35 }  // namespace shm_fuzz
     36 }  // namespace perfetto
     37 
     38 namespace protozero {
     39 
     40 class MessageHandleBase;
     41 
     42 // Base class extended by the proto C++ stubs generated by the ProtoZero
     43 // compiler. This class provides the minimal runtime required to support
     44 // append-only operations and is designed for performance. None of the methods
     45 // require any dynamic memory allocation.
     46 class PERFETTO_EXPORT Message {
     47  public:
     48   friend class MessageHandleBase;
     49   // Grant end_to_end_shared_memory_fuzzer access in order to write raw
     50   // bytes into the buffer.
     51   friend class ::perfetto::shm_fuzz::FakeProducer;
     52   // Adjust the |nested_messages_arena_| size when changing this, or the
     53   // static_assert in the .cc file will bark.
     54   static constexpr uint32_t kMaxNestingDepth = 8;
     55 
     56   // Ctor and Dtor of Message are never called, with the exeception
     57   // of root (non-nested) messages. Nested messages are allocated via placement
     58   // new in the |nested_messages_arena_| and implictly destroyed when the arena
     59   // of the root message goes away. This is fine as long as all the fields are
     60   // PODs, which is checked by the static_assert in the ctor (see the Reset()
     61   // method in the .cc file).
     62   Message() = default;
     63 
     64   // Clears up the state, allowing the message to be reused as a fresh one.
     65   void Reset(ScatteredStreamWriter*);
     66 
     67   // Commits all the changes to the buffer (backfills the size field of this and
     68   // all nested messages) and seals the message. Returns the size of the message
     69   // (and all nested sub-messages), without taking into account any chunking.
     70   // Finalize is idempotent and can be called several times w/o side effects.
     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 (minus |size_already_written| below). This is the mechanism
     76   // used by messages to backfill their corresponding size field in the parent
     77   // message.
     78   uint8_t* size_field() const { return size_field_; }
     79   void set_size_field(uint8_t* size_field) { size_field_ = size_field; }
     80 
     81   // This is to deal with case of backfilling the size of a root (non-nested)
     82   // message which is split into multiple chunks. Upon finalization only the
     83   // partial size that lies in the last chunk has to be backfilled.
     84   void inc_size_already_written(uint32_t sz) { size_already_written_ += sz; }
     85 
     86   Message* nested_message() { return nested_message_; }
     87 
     88   bool is_finalized() const { return finalized_; }
     89 
     90 #if PERFETTO_DCHECK_IS_ON()
     91   void set_handle(MessageHandleBase* handle) { handle_ = handle; }
     92 #endif
     93 
     94   // Proto types: uint64, uint32, int64, int32, bool, enum.
     95   template <typename T>
     96   void AppendVarInt(uint32_t field_id, T value) {
     97     if (nested_message_)
     98       EndNestedMessage();
     99 
    100     uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
    101     uint8_t* pos = buffer;
    102 
    103     pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos);
    104     // WriteVarInt encodes signed values in two's complement form.
    105     pos = proto_utils::WriteVarInt(value, pos);
    106     WriteToStream(buffer, pos);
    107   }
    108 
    109   // Proto types: sint64, sint32.
    110   template <typename T>
    111   void AppendSignedVarInt(uint32_t field_id, T value) {
    112     AppendVarInt(field_id, proto_utils::ZigZagEncode(value));
    113   }
    114 
    115   // Proto types: bool, enum (small).
    116   // Faster version of AppendVarInt for tiny numbers.
    117   void AppendTinyVarInt(uint32_t field_id, int32_t value) {
    118     PERFETTO_DCHECK(0 <= value && value < 0x80);
    119     if (nested_message_)
    120       EndNestedMessage();
    121 
    122     uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
    123     uint8_t* pos = buffer;
    124     // MakeTagVarInt gets super optimized here for constexpr.
    125     pos = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), pos);
    126     *pos++ = static_cast<uint8_t>(value);
    127     WriteToStream(buffer, pos);
    128   }
    129 
    130   // Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float.
    131   template <typename T>
    132   void AppendFixed(uint32_t field_id, T value) {
    133     if (nested_message_)
    134       EndNestedMessage();
    135 
    136     uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
    137     uint8_t* pos = buffer;
    138 
    139     pos = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<T>(field_id), pos);
    140     memcpy(pos, &value, sizeof(T));
    141     pos += sizeof(T);
    142     // TODO: Optimize memcpy performance, see http://crbug.com/624311 .
    143     WriteToStream(buffer, pos);
    144   }
    145 
    146   void AppendString(uint32_t field_id, const char* str);
    147   void AppendBytes(uint32_t field_id, const void* value, size_t size);
    148 
    149   // Begins a nested message, using the static storage provided by the parent
    150   // class (see comment in |nested_messages_arena_|). The nested message ends
    151   // either when Finalize() is called or when any other Append* method is called
    152   // in the parent class.
    153   // The template argument T is supposed to be a stub class auto generated from
    154   // a .proto, hence a subclass of Message.
    155   template <class T>
    156   T* BeginNestedMessage(uint32_t field_id) {
    157     // This is to prevent subclasses (which should be autogenerated, though), to
    158     // introduce extra state fields (which wouldn't be initialized by Reset()).
    159     static_assert(std::is_base_of<Message, T>::value,
    160                   "T must be a subclass of Message");
    161     static_assert(sizeof(T) == sizeof(Message),
    162                   "Message subclasses cannot introduce extra state.");
    163     T* message = reinterpret_cast<T*>(nested_messages_arena_);
    164     BeginNestedMessageInternal(field_id, message);
    165     return message;
    166   }
    167 
    168  private:
    169   Message(const Message&) = delete;
    170   Message& operator=(const Message&) = delete;
    171 
    172   void BeginNestedMessageInternal(uint32_t field_id, Message*);
    173 
    174   // Called by Finalize and Append* methods.
    175   void EndNestedMessage();
    176 
    177   void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) {
    178     PERFETTO_DCHECK(!finalized_);
    179     PERFETTO_DCHECK(src_begin <= src_end);
    180     const uint32_t size = static_cast<uint32_t>(src_end - src_begin);
    181     stream_writer_->WriteBytes(src_begin, size);
    182     size_ += size;
    183   }
    184 
    185   // Only POD fields are allowed. This class's dtor is never called.
    186   // See the comment on the static_assert in the the corresponding .cc file.
    187 
    188   // The stream writer interface used for the serialization.
    189   ScatteredStreamWriter* stream_writer_;
    190 
    191   uint8_t* size_field_;
    192 
    193   // Keeps track of the size of the current message.
    194   uint32_t size_;
    195 
    196   // See comment for inc_size_already_written().
    197   uint32_t size_already_written_;
    198 
    199   // When true, no more changes to the message are allowed. This is to DCHECK
    200   // attempts of writing to a message which has been Finalize()-d.
    201   bool finalized_;
    202 
    203   // Used to detect attemps to create messages with a nesting level >
    204   // kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages.
    205   uint8_t nesting_depth_;
    206 
    207 #if PERFETTO_DCHECK_IS_ON()
    208   // Current generation of message. Incremented on Reset.
    209   // Used to detect stale handles.
    210   uint32_t generation_;
    211 
    212   MessageHandleBase* handle_;
    213 #endif
    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   // TODO(primiano): optimization: I think that nested_message_, when non-null.
    223   // will always be @ (this) + offsetof(nested_messages_arena_).
    224   Message* nested_message_;
    225 
    226   // The root message owns the storage for all its nested messages, up to a max
    227   // of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of
    228   // the arena are meaningful only for the root message.
    229   // Unfortunately we cannot put the sizeof() math here because we cannot sizeof
    230   // the current class in a header. However the .cc file has a static_assert
    231   // that guarantees that (see the Reset() method in the .cc file).
    232   alignas(sizeof(void*)) uint8_t nested_messages_arena_[512];
    233 
    234   // DO NOT add any fields below |nested_messages_arena_|. The memory layout of
    235   // nested messages would overflow the storage allocated by the root message.
    236 };
    237 
    238 }  // namespace protozero
    239 
    240 #endif  // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_H_
    241