• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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/proto_decoder.h"
18 
19 #include <string.h>
20 
21 #include "perfetto/base/logging.h"
22 #include "perfetto/base/utils.h"
23 #include "perfetto/protozero/proto_utils.h"
24 
25 namespace protozero {
26 
27 using namespace proto_utils;
28 
29 #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
30 #error Unimplemented for big endian archs.
31 #endif
32 
33 namespace {
34 
35 struct ParseFieldResult {
36   const uint8_t* next;
37   Field field;
38 };
39 
40 // Parses one field and returns the field itself and a pointer to the next
41 // field to parse. If parsing fails, the returned |next| == |buffer|.
42 PERFETTO_ALWAYS_INLINE ParseFieldResult
ParseOneField(const uint8_t * const buffer,const uint8_t * const end)43 ParseOneField(const uint8_t* const buffer, const uint8_t* const end) {
44   ParseFieldResult res{buffer, Field{}};
45 
46   // The first byte of a proto field is structured as follows:
47   // The least 3 significant bits determine the field type.
48   // The most 5 significant bits determine the field id. If MSB == 1, the
49   // field id continues on the next bytes following the VarInt encoding.
50   const uint8_t kFieldTypeNumBits = 3;
51   const uint64_t kFieldTypeMask = (1 << kFieldTypeNumBits) - 1;  // 0000 0111;
52   const uint8_t* pos = buffer;
53 
54   // If we've already hit the end, just return an invalid field.
55   if (PERFETTO_UNLIKELY(pos >= end))
56     return res;
57 
58   uint64_t preamble = 0;
59   if (PERFETTO_LIKELY(*pos < 0x80)) {  // Fastpath for fields with ID < 32.
60     preamble = *(pos++);
61   } else {
62     pos = ParseVarInt(pos, end, &preamble);
63   }
64 
65   uint32_t field_id = static_cast<uint32_t>(preamble >> kFieldTypeNumBits);
66   if (field_id == 0 || pos >= end)
67     return res;
68 
69   auto field_type = static_cast<uint8_t>(preamble & kFieldTypeMask);
70   const uint8_t* new_pos = pos;
71   uint64_t int_value = 0;
72   uint32_t size = 0;
73 
74   switch (field_type) {
75     case static_cast<uint8_t>(ProtoWireType::kVarInt): {
76       new_pos = ParseVarInt(pos, end, &int_value);
77 
78       // new_pos not being greater than pos means ParseVarInt could not fully
79       // parse the number. This is because we are out of space in the buffer.
80       // Set the id to zero and return but don't update the offset so a future
81       // read can read this field.
82       if (PERFETTO_UNLIKELY(new_pos == pos))
83         return res;
84 
85       break;
86     }
87 
88     case static_cast<uint8_t>(ProtoWireType::kLengthDelimited): {
89       uint64_t payload_length;
90       new_pos = ParseVarInt(pos, end, &payload_length);
91       if (PERFETTO_UNLIKELY(new_pos == pos))
92         return res;
93 
94       // ParseVarInt guarantees that |new_pos| <= |end| when it succeeds;
95       if (payload_length > static_cast<uint64_t>(end - new_pos))
96         return res;
97 
98       // If the message is larger than 256 MiB silently skip it.
99       if (PERFETTO_LIKELY(payload_length <= proto_utils::kMaxMessageLength)) {
100         const uintptr_t payload_start = reinterpret_cast<uintptr_t>(new_pos);
101         int_value = payload_start;
102         size = static_cast<uint32_t>(payload_length);
103       }
104 
105       new_pos += payload_length;
106       break;
107     }
108 
109     case static_cast<uint8_t>(ProtoWireType::kFixed64): {
110       new_pos = pos + sizeof(uint64_t);
111       if (PERFETTO_UNLIKELY(new_pos > end))
112         return res;
113       memcpy(&int_value, pos, sizeof(uint64_t));
114       break;
115     }
116 
117     case static_cast<uint8_t>(ProtoWireType::kFixed32): {
118       new_pos = pos + sizeof(uint32_t);
119       if (PERFETTO_UNLIKELY(new_pos > end))
120         return res;
121       memcpy(&int_value, pos, sizeof(uint32_t));
122       break;
123     }
124 
125     default:
126       PERFETTO_DLOG("Invalid proto field type: %u", field_type);
127       return res;
128   }
129 
130   if (PERFETTO_UNLIKELY(field_id > std::numeric_limits<uint16_t>::max())) {
131     PERFETTO_DFATAL("Cannot parse proto field ids > 0xFFFF");
132     return res;
133   }
134 
135   res.field.initialize(static_cast<uint16_t>(field_id), field_type, int_value,
136                        size);
137   res.next = new_pos;
138   return res;
139 }
140 
141 }  // namespace
142 
FindField(uint32_t field_id)143 Field ProtoDecoder::FindField(uint32_t field_id) {
144   Field res{};
145   auto old_position = read_ptr_;
146   read_ptr_ = begin_;
147   for (auto f = ReadField(); f.valid(); f = ReadField()) {
148     if (f.id() == field_id) {
149       res = f;
150       break;
151     }
152   }
153   read_ptr_ = old_position;
154   return res;
155 }
156 
157 PERFETTO_ALWAYS_INLINE
ReadField()158 Field ProtoDecoder::ReadField() {
159   ParseFieldResult res = ParseOneField(read_ptr_, end_);
160   read_ptr_ = res.next;
161   return res.field;
162 }
163 
ParseAllFields()164 void TypedProtoDecoderBase::ParseAllFields() {
165   const uint8_t* cur = begin_;
166   ParseFieldResult res;
167   for (;;) {
168     res = ParseOneField(cur, end_);
169     if (res.next == cur)
170       break;
171     PERFETTO_DCHECK(res.field.valid());
172     cur = res.next;
173 
174     auto field_id = res.field.id();
175     if (PERFETTO_UNLIKELY(field_id >= num_fields_))
176       continue;
177 
178     Field* fld = &fields_[field_id];
179     if (PERFETTO_LIKELY(!fld->valid())) {
180       // This is the first time we see this field.
181       *fld = std::move(res.field);
182     } else {
183       // Repeated field case.
184       // In this case we need to:
185       // 1. Append the last value of the field to end of the repeated field
186       //    storage.
187       // 2. Replace the default instance at offset |field_id| with the current
188       //    value. This is because in case of repeated field a call to Get(X) is
189       //    supposed to return the last value of X, not the first one.
190       // This is so that the RepeatedFieldIterator will iterate in the right
191       // order, see comments on RepeatedFieldIterator.
192       if (PERFETTO_UNLIKELY(size_ >= capacity_)) {
193         ExpandHeapStorage();
194         // ExpandHeapStorage moves fields_ so we need to update the ptr to fld:
195         fld = &fields_[field_id];
196         PERFETTO_DCHECK(size_ < capacity_);
197       }
198       fields_[size_++] = *fld;
199       *fld = std::move(res.field);
200     }
201   }
202   read_ptr_ = res.next;
203 }
204 
ExpandHeapStorage()205 void TypedProtoDecoderBase::ExpandHeapStorage() {
206   uint32_t new_capacity = capacity_ * 2;
207   PERFETTO_CHECK(new_capacity > size_);
208   std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
209 
210   static_assert(PERFETTO_IS_TRIVIALLY_COPYABLE(Field),
211                 "Field must be trivially copyable");
212   memcpy(&new_storage[0], fields_, sizeof(Field) * size_);
213 
214   heap_storage_ = std::move(new_storage);
215   fields_ = &heap_storage_[0];
216   capacity_ = new_capacity;
217 }
218 
219 }  // namespace protozero
220