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