• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if PERFETTO_DCHECK_IS_ON()
38 std::atomic<uint32_t> g_generation;
39 #endif
40 
41 }  // namespace
42 
43 // Do NOT put any code in the constructor or use default initialization.
44 // Use the Reset() method below instead.
45 
46 // This method is called to initialize both root and nested messages.
Reset(ScatteredStreamWriter * stream_writer,MessageArena * arena)47 void Message::Reset(ScatteredStreamWriter* stream_writer, MessageArena* arena) {
48 // Older versions of libstdcxx don't have is_trivially_constructible.
49 #if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
50   static_assert(std::is_trivially_constructible<Message>::value,
51                 "Message must be trivially constructible");
52 #endif
53 
54   static_assert(std::is_trivially_destructible<Message>::value,
55                 "Message must be trivially destructible");
56   stream_writer_ = stream_writer;
57   arena_ = arena;
58   size_ = 0;
59   size_field_ = nullptr;
60   size_already_written_ = 0;
61   nested_message_ = nullptr;
62   finalized_ = false;
63 #if PERFETTO_DCHECK_IS_ON()
64   handle_ = nullptr;
65   generation_ = g_generation.fetch_add(1, std::memory_order_relaxed);
66 #endif
67 }
68 
AppendString(uint32_t field_id,const char * str)69 void Message::AppendString(uint32_t field_id, const char* str) {
70   AppendBytes(field_id, str, strlen(str));
71 }
72 
AppendBytes(uint32_t field_id,const void * src,size_t size)73 void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
74   if (nested_message_)
75     EndNestedMessage();
76 
77   PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
78   // Write the proto preamble (field id, type and length of the field).
79   uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
80   uint8_t* pos = buffer;
81   pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
82                                  pos);
83   pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
84   WriteToStream(buffer, pos);
85 
86   const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
87   WriteToStream(src_u8, src_u8 + size);
88 }
89 
AppendScatteredBytes(uint32_t field_id,ContiguousMemoryRange * ranges,size_t num_ranges)90 size_t Message::AppendScatteredBytes(uint32_t field_id,
91                                      ContiguousMemoryRange* ranges,
92                                      size_t num_ranges) {
93   size_t size = 0;
94   for (size_t i = 0; i < num_ranges; ++i) {
95     size += ranges[i].size();
96   }
97 
98   PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
99 
100   uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
101   uint8_t* pos = buffer;
102   pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
103                                  pos);
104   pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
105   WriteToStream(buffer, pos);
106 
107   for (size_t i = 0; i < num_ranges; ++i) {
108     auto& range = ranges[i];
109     WriteToStream(range.begin, range.end);
110   }
111 
112   return size;
113 }
114 
Finalize()115 uint32_t Message::Finalize() {
116   if (finalized_)
117     return size_;
118 
119   if (nested_message_)
120     EndNestedMessage();
121 
122   // Write the length of the nested message a posteriori, using a leading-zero
123   // redundant varint encoding.
124   if (size_field_) {
125     PERFETTO_DCHECK(!finalized_);
126     PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
127     PERFETTO_DCHECK(size_ >= size_already_written_);
128     proto_utils::WriteRedundantVarInt(size_ - size_already_written_,
129                                       size_field_);
130     size_field_ = nullptr;
131   }
132 
133   finalized_ = true;
134 #if PERFETTO_DCHECK_IS_ON()
135   if (handle_)
136     handle_->reset_message();
137 #endif
138 
139   return size_;
140 }
141 
BeginNestedMessageInternal(uint32_t field_id)142 Message* Message::BeginNestedMessageInternal(uint32_t field_id) {
143   if (nested_message_)
144     EndNestedMessage();
145 
146   // Write the proto preamble for the nested message.
147   uint8_t data[proto_utils::kMaxTagEncodedSize];
148   uint8_t* data_end = proto_utils::WriteVarInt(
149       proto_utils::MakeTagLengthDelimited(field_id), data);
150   WriteToStream(data, data_end);
151 
152   Message* message = arena_->NewMessage();
153   message->Reset(stream_writer_, arena_);
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 
161   nested_message_ = message;
162   return message;
163 }
164 
EndNestedMessage()165 void Message::EndNestedMessage() {
166   size_ += nested_message_->Finalize();
167   arena_->DeleteLastMessage(nested_message_);
168   nested_message_ = nullptr;
169 }
170 
171 }  // namespace protozero
172