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