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/compiler.h"
23 #include "perfetto/base/logging.h"
24 #include "perfetto/protozero/message_arena.h"
25 #include "perfetto/protozero/message_handle.h"
26
27 #if !PERFETTO_IS_LITTLE_ENDIAN()
28 // The memcpy() for float and double below needs to be adjusted if we want to
29 // support big endian CPUs. There doesn't seem to be a compelling need today.
30 #error Unimplemented for big endian archs.
31 #endif
32
33 namespace protozero {
34
35 namespace {
36
37 constexpr int kBytesToCompact = proto_utils::kMessageLengthFieldSize - 1u;
38
39 #if PERFETTO_DCHECK_IS_ON()
40 std::atomic<uint32_t> g_generation;
41 #endif
42
43 } // namespace
44
45 // Do NOT put any code in the constructor or use default initialization.
46 // Use the Reset() method below instead.
47
48 // This method is called to initialize both root and nested messages.
Reset(ScatteredStreamWriter * stream_writer,MessageArena * arena)49 void Message::Reset(ScatteredStreamWriter* stream_writer, MessageArena* arena) {
50 // Older versions of libstdcxx don't have is_trivially_constructible.
51 #if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
52 static_assert(std::is_trivially_constructible<Message>::value,
53 "Message must be trivially constructible");
54 #endif
55
56 static_assert(std::is_trivially_destructible<Message>::value,
57 "Message must be trivially destructible");
58 stream_writer_ = stream_writer;
59 arena_ = arena;
60 size_ = 0;
61 size_field_ = nullptr;
62 nested_message_ = nullptr;
63 message_state_ = MessageState::kNotFinalized;
64 #if PERFETTO_DCHECK_IS_ON()
65 handle_ = nullptr;
66 generation_ = g_generation.fetch_add(1, std::memory_order_relaxed);
67 #endif
68 }
69
AppendString(uint32_t field_id,const char * str)70 void Message::AppendString(uint32_t field_id, const char* str) {
71 AppendBytes(field_id, str, strlen(str));
72 }
73
AppendBytes(uint32_t field_id,const void * src,size_t size)74 void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
75 PERFETTO_DCHECK(field_id);
76 if (nested_message_)
77 EndNestedMessage();
78
79 PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
80 // Write the proto preamble (field id, type and length of the field).
81 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
82 uint8_t* pos = buffer;
83 pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
84 pos);
85 pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
86 WriteToStream(buffer, pos);
87
88 const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
89 WriteToStream(src_u8, src_u8 + size);
90 }
91
AppendScatteredBytes(uint32_t field_id,ContiguousMemoryRange * ranges,size_t num_ranges)92 size_t Message::AppendScatteredBytes(uint32_t field_id,
93 ContiguousMemoryRange* ranges,
94 size_t num_ranges) {
95 PERFETTO_DCHECK(field_id);
96 if (nested_message_)
97 EndNestedMessage();
98
99 size_t size = 0;
100 for (size_t i = 0; i < num_ranges; ++i) {
101 size += ranges[i].size();
102 }
103
104 PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
105
106 uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
107 uint8_t* pos = buffer;
108 pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
109 pos);
110 pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
111 WriteToStream(buffer, pos);
112
113 for (size_t i = 0; i < num_ranges; ++i) {
114 auto& range = ranges[i];
115 WriteToStream(range.begin, range.end);
116 }
117
118 return size;
119 }
120
Finalize()121 uint32_t Message::Finalize() {
122 if (is_finalized())
123 return size_;
124
125 if (nested_message_)
126 EndNestedMessage();
127
128 // Write the length of the nested message a posteriori, using a leading-zero
129 // redundant varint encoding. This can be nullptr for the root message, among
130 // many reasons, because the TraceWriterImpl delegate is keeping track of the
131 // root fragment size independently.
132 if (size_field_) {
133 PERFETTO_DCHECK(!is_finalized());
134 PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
135 //
136 // Normally the size of a protozero message is written with 4 bytes just
137 // before the contents of the message itself:
138 //
139 // size message data
140 // [aa bb cc dd] [01 23 45 67 ...]
141 //
142 // We always reserve 4 bytes for the size, because the real size of the
143 // message isn't known until the call to Finalize(). This is possible
144 // because we can use leading zero redundant varint coding to expand any
145 // size smaller than 256 MiB to 4 bytes.
146 //
147 // However this is wasteful for short, frequently written messages, so the
148 // code below uses a 1 byte size field when possible. This is done by
149 // shifting the already-written data (which should still be in the cache)
150 // back by 3 bytes, resulting in this layout:
151 //
152 // size message data
153 // [aa] [01 23 45 67 ...]
154 //
155 // We can only do this optimization if the message is contained in a single
156 // chunk (since we can't modify previously committed chunks). We can check
157 // this by verifying that the size field is immediately before the message
158 // in memory and is fully contained by the current chunk.
159 //
160 if (PERFETTO_LIKELY(size_ <= proto_utils::kMaxOneByteMessageLength &&
161 size_field_ ==
162 stream_writer_->write_ptr() - size_ -
163 proto_utils::kMessageLengthFieldSize &&
164 size_field_ >= stream_writer_->cur_range().begin)) {
165 stream_writer_->Rewind(size_, kBytesToCompact);
166 PERFETTO_DCHECK(size_field_ == stream_writer_->write_ptr() - size_ - 1u);
167 *size_field_ = static_cast<uint8_t>(size_);
168 message_state_ = MessageState::kFinalizedWithCompaction;
169 } else {
170 proto_utils::WriteRedundantVarInt(size_, size_field_);
171 message_state_ = MessageState::kFinalized;
172 }
173 size_field_ = nullptr;
174 } else {
175 message_state_ = MessageState::kFinalized;
176 }
177
178 #if PERFETTO_DCHECK_IS_ON()
179 if (handle_)
180 handle_->reset_message();
181 #endif
182
183 return size_;
184 }
185
BeginNestedMessageInternal(uint32_t field_id)186 Message* Message::BeginNestedMessageInternal(uint32_t field_id) {
187 PERFETTO_DCHECK(field_id);
188 if (nested_message_)
189 EndNestedMessage();
190
191 // Write the proto preamble for the nested message.
192 uint8_t data[proto_utils::kMaxTagEncodedSize];
193 uint8_t* data_end = proto_utils::WriteVarInt(
194 proto_utils::MakeTagLengthDelimited(field_id), data);
195 WriteToStream(data, data_end);
196
197 Message* message = arena_->NewMessage();
198 message->Reset(stream_writer_, arena_);
199
200 // The length of the nested message cannot be known upfront. So right now
201 // just reserve the bytes to encode the size after the nested message is done.
202 message->set_size_field(
203 stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
204 size_ += proto_utils::kMessageLengthFieldSize;
205
206 nested_message_ = message;
207 return message;
208 }
209
EndNestedMessage()210 void Message::EndNestedMessage() {
211 size_ += nested_message_->Finalize();
212 if (nested_message_->message_state_ ==
213 MessageState::kFinalizedWithCompaction) {
214 size_ -= kBytesToCompact;
215 }
216 arena_->DeleteLastMessage(nested_message_);
217 nested_message_ = nullptr;
218 }
219
220 } // namespace protozero
221