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_handle.h"
24 
25 namespace protozero {
26 
27 namespace {
28 
29 #if PERFETTO_DCHECK_IS_ON()
30 std::atomic<uint32_t> g_generation;
31 #endif
32 
33 }  // namespace
34 
35 // static
36 constexpr uint32_t Message::kMaxNestingDepth;
37 
38 // Do NOT put any code in the constructor or use default initialization.
39 // Use the Reset() method below instead. See the header for the reason why.
40 
41 // This method is called to initialize both root and nested messages.
Reset(ScatteredStreamWriter * stream_writer)42 void Message::Reset(ScatteredStreamWriter* stream_writer) {
43 // Older versions of libstdcxx don't have is_trivially_constructible.
44 #if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
45   static_assert(std::is_trivially_constructible<Message>::value,
46                 "Message must be trivially constructible");
47 #endif
48 
49   static_assert(std::is_trivially_destructible<Message>::value,
50                 "Message must be trivially destructible");
51 
52   static_assert(
53       sizeof(Message::nested_messages_arena_) >=
54           kMaxNestingDepth *
55               (sizeof(Message) - sizeof(Message::nested_messages_arena_)),
56       "Message::nested_messages_arena_ is too small");
57 
58   stream_writer_ = stream_writer;
59   size_ = 0;
60   size_field_ = nullptr;
61   size_already_written_ = 0;
62   nested_message_ = nullptr;
63   nesting_depth_ = 0;
64   finalized_ = false;
65 #if PERFETTO_DCHECK_IS_ON()
66   handle_ = nullptr;
67   generation_ = g_generation.fetch_add(1, std::memory_order_relaxed);
68 #endif
69 }
70 
AppendString(uint32_t field_id,const char * str)71 void Message::AppendString(uint32_t field_id, const char* str) {
72   AppendBytes(field_id, str, strlen(str));
73 }
74 
AppendBytes(uint32_t field_id,const void * src,size_t size)75 void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
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   size_t size = 0;
96   for (size_t i = 0; i < num_ranges; ++i) {
97     size += ranges[i].size();
98   }
99 
100   PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
101 
102   uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
103   uint8_t* pos = buffer;
104   pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
105                                  pos);
106   pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
107   WriteToStream(buffer, pos);
108 
109   for (size_t i = 0; i < num_ranges; ++i) {
110     auto& range = ranges[i];
111     WriteToStream(range.begin, range.end);
112   }
113 
114   return size;
115 }
116 
Finalize()117 uint32_t Message::Finalize() {
118   if (finalized_)
119     return size_;
120 
121   if (nested_message_)
122     EndNestedMessage();
123 
124   // Write the length of the nested message a posteriori, using a leading-zero
125   // redundant varint encoding.
126   if (size_field_) {
127     PERFETTO_DCHECK(!finalized_);
128     PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
129     PERFETTO_DCHECK(size_ >= size_already_written_);
130     proto_utils::WriteRedundantVarInt(size_ - size_already_written_,
131                                       size_field_);
132     size_field_ = nullptr;
133   }
134 
135   finalized_ = true;
136 #if PERFETTO_DCHECK_IS_ON()
137   if (handle_)
138     handle_->reset_message();
139 #endif
140 
141   return size_;
142 }
143 
BeginNestedMessageInternal(uint32_t field_id,Message * message)144 void Message::BeginNestedMessageInternal(uint32_t field_id, Message* message) {
145   if (nested_message_)
146     EndNestedMessage();
147 
148   // Write the proto preamble for the nested message.
149   uint8_t data[proto_utils::kMaxTagEncodedSize];
150   uint8_t* data_end = proto_utils::WriteVarInt(
151       proto_utils::MakeTagLengthDelimited(field_id), data);
152   WriteToStream(data, data_end);
153 
154   message->Reset(stream_writer_);
155   PERFETTO_CHECK(nesting_depth_ < kMaxNestingDepth);
156   message->nesting_depth_ = nesting_depth_ + 1;
157 
158   // The length of the nested message cannot be known upfront. So right now
159   // just reserve the bytes to encode the size after the nested message is done.
160   message->set_size_field(
161       stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
162   size_ += proto_utils::kMessageLengthFieldSize;
163   nested_message_ = message;
164 }
165 
EndNestedMessage()166 void Message::EndNestedMessage() {
167   size_ += nested_message_->Finalize();
168   nested_message_ = nullptr;
169 }
170 
171 }  // namespace protozero
172