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 <type_traits>
20 
21 #include "perfetto/base/logging.h"
22 #include "perfetto/protozero/message_handle.h"
23 
24 #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
25 // The memcpy() for float and double below needs to be adjusted if we want to
26 // support big endian CPUs. There doesn't seem to be a compelling need today.
27 #error Unimplemented for big endian archs.
28 #endif
29 
30 namespace protozero {
31 
32 // static
33 constexpr uint32_t Message::kMaxNestingDepth;
34 
35 // Do NOT put any code in the constructor or use default initialization.
36 // Use the Reset() method below instead. See the header for the reason why.
37 
38 // This method is called to initialize both root and nested messages.
Reset(ScatteredStreamWriter * stream_writer)39 void Message::Reset(ScatteredStreamWriter* stream_writer) {
40 // Older versions of libstdcxx don't have is_trivially_constructible.
41 #if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
42   static_assert(std::is_trivially_constructible<Message>::value,
43                 "Message must be trivially constructible");
44 #endif
45 
46   static_assert(std::is_trivially_destructible<Message>::value,
47                 "Message must be trivially destructible");
48 
49   static_assert(
50       sizeof(Message::nested_messages_arena_) >=
51           kMaxNestingDepth *
52               (sizeof(Message) - sizeof(Message::nested_messages_arena_)),
53       "Message::nested_messages_arena_ is too small");
54 
55   stream_writer_ = stream_writer;
56   size_ = 0;
57   size_field_ = nullptr;
58   size_already_written_ = 0;
59   nested_message_ = nullptr;
60   nesting_depth_ = 0;
61   finalized_ = false;
62 #if PERFETTO_DCHECK_IS_ON()
63   handle_ = nullptr;
64   generation_++;
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,Message * message)141 void Message::BeginNestedMessageInternal(uint32_t field_id, Message* message) {
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->Reset(stream_writer_);
152   PERFETTO_CHECK(nesting_depth_ < kMaxNestingDepth);
153   message->nesting_depth_ = nesting_depth_ + 1;
154 
155   // The length of the nested message cannot be known upfront. So right now
156   // just reserve the bytes to encode the size after the nested message is done.
157   message->set_size_field(
158       stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
159   size_ += proto_utils::kMessageLengthFieldSize;
160   nested_message_ = message;
161 }
162 
EndNestedMessage()163 void Message::EndNestedMessage() {
164   size_ += nested_message_->Finalize();
165   nested_message_ = nullptr;
166 }
167 
168 }  // namespace protozero
169