xref: /aosp_15_r20/external/perfetto/src/protozero/message.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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