1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_TRACING_CORE_PROTO_ZERO_MESSAGE_H_
#define COMPONENTS_TRACING_CORE_PROTO_ZERO_MESSAGE_H_
#include <stdint.h>
#include <type_traits>
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/template_util.h"
#include "build/build_config.h"
#include "components/tracing/core/proto_utils.h"
#include "components/tracing/core/scattered_stream_writer.h"
#include "components/tracing/tracing_export.h"
namespace tracing {
namespace v2 {
class ProtoZeroMessageHandleBase;
// Base class extended by the proto C++ stubs generated by the ProtoZero
// compiler (see //components/tracing/tools/). This class provides the minimal
// runtime required to support append-only operations and is desiged for
// performance. None of the methods require any dynamic memory allocation.
class TRACING_EXPORT ProtoZeroMessage {
public:
ProtoZeroMessage();
// Clears up the state, allowing the message to be reused as a fresh one.
void Reset(ScatteredStreamWriter*);
// Commits all the changes to the buffer (backfills the size field of this and
// all nested messages) and seals the message. Returns the size of the message
// (and all nested sub-messages), without taking into account any chunking.
// Finalize is idempotent and can be called several times w/o side effects.
size_t Finalize();
// Optional. If is_valid() == true, the corresponding memory region (its
// length == proto::kMessageLengthFieldSize) is backfilled with the size of
// this message (minus |size_already_written| below) when the message is
// finalized. This is the mechanism used by messages to backfill their
// corresponding size field in the parent message.
ContiguousMemoryRange size_field() const { return size_field_; }
void set_size_field(const ContiguousMemoryRange& reserved_range) {
size_field_ = reserved_range;
}
// This is to deal with case of backfilling the size of a root (non-nested)
// message which is split into multiple chunks. Upon finalization only the
// partial size that lies in the last chunk has to be backfilled.
void inc_size_already_written(size_t size) { size_already_written_ += size; }
#if DCHECK_IS_ON()
// Only for ProtoZeroMessageHandleBase.
void set_handle(ProtoZeroMessageHandleBase* handle) { handle_ = handle; }
#endif
protected:
// Proto types: uint64, uint32, int64, int32, bool, enum.
template <typename T>
void AppendVarInt(uint32_t field_id, T value) {
if (nested_message_)
EndNestedMessage();
uint8_t buffer[proto::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
pos = proto::WriteVarInt(proto::MakeTagVarInt(field_id), pos);
// WriteVarInt encodes signed values in two's complement form.
pos = proto::WriteVarInt(value, pos);
WriteToStream(buffer, pos);
}
// Proto types: sint64, sint32.
template <typename T>
void AppendSignedVarInt(uint32_t field_id, T value) {
AppendVarInt(field_id, proto::ZigZagEncode(value));
}
// Proto types: bool, enum (small).
// Faster version of AppendVarInt for tiny numbers.
void AppendTinyVarInt(uint32_t field_id, int32_t value) {
DCHECK(0 <= value && value < 0x80);
if (nested_message_)
EndNestedMessage();
uint8_t buffer[proto::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
// MakeTagVarInt gets super optimized here for constexpr.
pos = proto::WriteVarInt(proto::MakeTagVarInt(field_id), pos);
*pos++ = static_cast<uint8_t>(value);
WriteToStream(buffer, pos);
}
// Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float.
template <typename T>
void AppendFixed(uint32_t field_id, T value) {
if (nested_message_)
EndNestedMessage();
uint8_t buffer[proto::kMaxSimpleFieldEncodedSize];
uint8_t* pos = buffer;
pos = proto::WriteVarInt(proto::MakeTagFixed<T>(field_id), pos);
memcpy(pos, &value, sizeof(T));
pos += sizeof(T);
// TODO(kraynov): Optimize memcpy performance, see http://crbug.com/624311 .
WriteToStream(buffer, pos);
}
void AppendString(uint32_t field_id, const char* str);
void AppendBytes(uint32_t field_id, const void* value, size_t size);
// Begins a nested message, using the static storage provided by the parent
// class (see comment in |nested_messages_arena_|). The nested message ends
// either when Finalize() is called or when any other Append* method is called
// in the parent class.
// The template argument T is supposed to be a stub class auto generated from
// a .proto, hence a subclass of ProtoZeroMessage.
template <class T>
T* BeginNestedMessage(uint32_t field_id) {
// This is to prevent subclasses (which should be autogenerated, though), to
// introduce extra state fields (which wouldn't be initialized by Reset()).
static_assert(std::is_base_of<ProtoZeroMessage, T>::value,
"T must be a subclass of ProtoZeroMessage");
static_assert(sizeof(T) == sizeof(ProtoZeroMessage),
"ProtoZeroMessage subclasses cannot introduce extra state.");
T* message = reinterpret_cast<T*>(nested_messages_arena_);
BeginNestedMessageInternal(field_id, message);
return message;
}
private:
friend class ProtoZeroMessageTest;
FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, BasicTypesNoNesting);
FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, BackfillSizeOnFinalization);
FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, NestedMessagesSimple);
FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, StressTest);
FRIEND_TEST_ALL_PREFIXES(ProtoZeroMessageTest, MessageHandle);
enum : uint32_t { kMaxNestingDepth = 8 };
void BeginNestedMessageInternal(uint32_t field_id, ProtoZeroMessage*);
// Called by Finalize and Append* methods.
void EndNestedMessage();
void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) {
#if DCHECK_IS_ON()
DCHECK(!sealed_);
#endif
DCHECK(src_begin < src_end);
const size_t size = static_cast<size_t>(src_end - src_begin);
stream_writer_->WriteBytes(src_begin, size);
size_ += size;
}
// Only POD fields are allowed. This class's dtor is never called.
// See the comment on the static_assert in the the corresponding .cc file.
// The stream writer interface used for the serialization.
ScatteredStreamWriter* stream_writer_;
// Keeps track of the size of the current message.
size_t size_;
ContiguousMemoryRange size_field_;
size_t size_already_written_;
// Used to detect attemps to create messages with a nesting level >
// kMaxNestingDepth. |nesting_depth_| == 0 for root (non-nested) messages.
uint32_t nesting_depth_;
#if DCHECK_IS_ON()
// When true, no more changes to the message are allowed. This is to DCHECK
// attempts of writing to a message which has been Finalize()-d.
bool sealed_;
ProtoZeroMessageHandleBase* handle_;
#endif
// Pointer to the last child message created through BeginNestedMessage(), if
// any. nullptr otherwise. There is no need to keep track of more than one
// message per nesting level as the proto-zero API contract mandates that
// nested fields can be filled only in a stacked fashion. In other words,
// nested messages are finalized and sealed when any other field is set in the
// parent message (or the parent message itself is finalized) and cannot be
// accessed anymore afterwards.
ProtoZeroMessage* nested_message_;
// The root message owns the storage for all its nested messages, up to a max
// of kMaxNestingDepth levels (see the .cc file). Note that the boundaries of
// the arena are meaningful only for the root message. The static_assert in
// the .cc file guarantees that the sizeof(nested_messages_arena_) is enough
// to contain up to kMaxNestingDepth messages.
ALIGNAS(sizeof(void*)) uint8_t nested_messages_arena_[512];
// DO NOT add any fields below |nested_messages_arena_|. The memory layout of
// nested messages would overflow the storage allocated by the root message.
DISALLOW_COPY_AND_ASSIGN(ProtoZeroMessage);
};
} // namespace v2
} // namespace tracing
#endif // COMPONENTS_TRACING_CORE_PROTO_ZERO_MESSAGE_H_
|