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 #include "perfetto/protozero/message.h"
18
19 #include <atomic>
20 #include <type_traits>
21
22 #include "perfetto/base/logging.h"
23 #include "perfetto/protozero/message_arena.h"
24 #include "perfetto/protozero/message_handle.h"
25
26 #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
27 // The memcpy() for float and double below needs to be adjusted if we want to
28 // support big endian CPUs. There doesn't seem to be a compelling need today.
29 #error Unimplemented for big endian archs.
30 #endif
31
32 namespace protozero {
33
34 namespace {
35
36 #if PERFETTO_DCHECK_IS_ON()
37 std::atomic<uint32_t> g_generation;
38 #endif
39
40 } // namespace
41
42 // Do NOT put any code in the constructor or use default initialization.
43 // Use the Reset() method below instead.
44
45 // This method is called to initialize both root and nested messages.
Reset(ScatteredStreamWriter * stream_writer,MessageArena * arena)46 void Message::Reset(ScatteredStreamWriter* stream_writer, MessageArena* arena) {
47 // Older versions of libstdcxx don't have is_trivially_constructible.
48 #if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
49 static_assert(std::is_trivially_constructible<Message>::value,
50 "Message must be trivially constructible");
51 #endif
52
53 static_assert(std::is_trivially_destructible<Message>::value,
54 "Message must be trivially destructible");
55 stream_writer_ = stream_writer;
56 arena_ = arena;
57 size_ = 0;
58 size_field_ = nullptr;
59 size_already_written_ = 0;
60 nested_message_ = nullptr;
61 finalized_ = false;
62 #if PERFETTO_DCHECK_IS_ON()
63 handle_ = nullptr;
64 generation_ = g_generation.fetch_add(1, std::memory_order_relaxed);
65 #endif
66 }
67
AppendString(uint32_t field_id,const char * str)68 void Message::AppendString(uint32_t field_id, const char* str) {
69 AppendBytes(field_id, str, strlen(str));
70 }
71
AppendBytes(uint32_t field_id,const void * src,size_t size)72 void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
73 if (nested_message_)
74 EndNestedMessage();
75
76 PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
77 // Write the proto preamble (field id, type and length of the field).
78 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
79 uint8_t* pos = buffer;
80 pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
81 pos);
82 pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
83 WriteToStream(buffer, pos);
84
85 const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
86 WriteToStream(src_u8, src_u8 + size);
87 }
88
AppendScatteredBytes(uint32_t field_id,ContiguousMemoryRange * ranges,size_t num_ranges)89 size_t Message::AppendScatteredBytes(uint32_t field_id,
90 ContiguousMemoryRange* ranges,
91 size_t num_ranges) {
92 size_t size = 0;
93 for (size_t i = 0; i < num_ranges; ++i) {
94 size += ranges[i].size();
95 }
96
97 PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
98
99 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
100 uint8_t* pos = buffer;
101 pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
102 pos);
103 pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
104 WriteToStream(buffer, pos);
105
106 for (size_t i = 0; i < num_ranges; ++i) {
107 auto& range = ranges[i];
108 WriteToStream(range.begin, range.end);
109 }
110
111 return size;
112 }
113
Finalize()114 uint32_t Message::Finalize() {
115 if (finalized_)
116 return size_;
117
118 if (nested_message_)
119 EndNestedMessage();
120
121 // Write the length of the nested message a posteriori, using a leading-zero
122 // redundant varint encoding.
123 if (size_field_) {
124 PERFETTO_DCHECK(!finalized_);
125 PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
126 PERFETTO_DCHECK(size_ >= size_already_written_);
127 proto_utils::WriteRedundantVarInt(size_ - size_already_written_,
128 size_field_);
129 size_field_ = nullptr;
130 }
131
132 finalized_ = true;
133 #if PERFETTO_DCHECK_IS_ON()
134 if (handle_)
135 handle_->reset_message();
136 #endif
137
138 return size_;
139 }
140
BeginNestedMessageInternal(uint32_t field_id)141 Message* Message::BeginNestedMessageInternal(uint32_t field_id) {
142 if (nested_message_)
143 EndNestedMessage();
144
145 // Write the proto preamble for the nested message.
146 uint8_t data[proto_utils::kMaxTagEncodedSize];
147 uint8_t* data_end = proto_utils::WriteVarInt(
148 proto_utils::MakeTagLengthDelimited(field_id), data);
149 WriteToStream(data, data_end);
150
151 Message* message = arena_->NewMessage();
152 message->Reset(stream_writer_, arena_);
153
154 // The length of the nested message cannot be known upfront. So right now
155 // just reserve the bytes to encode the size after the nested message is done.
156 message->set_size_field(
157 stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
158 size_ += proto_utils::kMessageLengthFieldSize;
159
160 nested_message_ = message;
161 return message;
162 }
163
EndNestedMessage()164 void Message::EndNestedMessage() {
165 size_ += nested_message_->Finalize();
166 arena_->DeleteLastMessage(nested_message_);
167 nested_message_ = nullptr;
168 }
169
170 } // namespace protozero
171