• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 "src/trace_processor/util/proto_to_args_parser.h"
18 
19 #include "protos/perfetto/common/descriptor.pbzero.h"
20 #include "src/trace_processor/util/descriptors.h"
21 #include "src/trace_processor/util/status_macros.h"
22 
23 namespace perfetto {
24 namespace trace_processor {
25 namespace util {
26 
27 namespace {
28 
AppendProtoType(std::string & target,const std::string & value)29 void AppendProtoType(std::string& target, const std::string& value) {
30   if (!target.empty())
31     target += '.';
32   target += value;
33 }
34 
35 }  // namespace
36 
37 ProtoToArgsParser::Key::Key() = default;
Key(const std::string & k)38 ProtoToArgsParser::Key::Key(const std::string& k) : flat_key(k), key(k) {}
Key(const std::string & fk,const std::string & k)39 ProtoToArgsParser::Key::Key(const std::string& fk, const std::string& k)
40     : flat_key(fk), key(k) {}
41 ProtoToArgsParser::Key::~Key() = default;
42 
ScopedNestedKeyContext(Key & key)43 ProtoToArgsParser::ScopedNestedKeyContext::ScopedNestedKeyContext(Key& key)
44     : key_(key),
45       old_flat_key_length_(key.flat_key.length()),
46       old_key_length_(key.key.length()) {}
47 
ScopedNestedKeyContext(ProtoToArgsParser::ScopedNestedKeyContext && other)48 ProtoToArgsParser::ScopedNestedKeyContext::ScopedNestedKeyContext(
49     ProtoToArgsParser::ScopedNestedKeyContext&& other)
50     : key_(other.key_),
51       old_flat_key_length_(other.old_flat_key_length_),
52       old_key_length_(other.old_key_length_) {
53   other.old_flat_key_length_ = base::nullopt;
54   other.old_key_length_ = base::nullopt;
55 }
56 
~ScopedNestedKeyContext()57 ProtoToArgsParser::ScopedNestedKeyContext::~ScopedNestedKeyContext() {
58   RemoveFieldSuffix();
59 }
60 
RemoveFieldSuffix()61 void ProtoToArgsParser::ScopedNestedKeyContext::RemoveFieldSuffix() {
62   if (old_flat_key_length_)
63     key_.flat_key.resize(old_flat_key_length_.value());
64   if (old_key_length_)
65     key_.key.resize(old_key_length_.value());
66   old_flat_key_length_ = base::nullopt;
67   old_key_length_ = base::nullopt;
68 }
69 
70 ProtoToArgsParser::Delegate::~Delegate() = default;
71 
ProtoToArgsParser(const DescriptorPool & pool)72 ProtoToArgsParser::ProtoToArgsParser(const DescriptorPool& pool) : pool_(pool) {
73   constexpr int kDefaultSize = 64;
74   key_prefix_.key.reserve(kDefaultSize);
75   key_prefix_.flat_key.reserve(kDefaultSize);
76 }
77 
ParseMessage(const protozero::ConstBytes & cb,const std::string & type,const std::vector<uint16_t> * allowed_fields,Delegate & delegate,int * unknown_extensions)78 base::Status ProtoToArgsParser::ParseMessage(
79     const protozero::ConstBytes& cb,
80     const std::string& type,
81     const std::vector<uint16_t>* allowed_fields,
82     Delegate& delegate,
83     int* unknown_extensions) {
84   ScopedNestedKeyContext key_context(key_prefix_);
85   return ParseMessageInternal(key_context, cb, type, allowed_fields, delegate,
86                               unknown_extensions);
87 }
88 
ParseMessageInternal(ScopedNestedKeyContext & key_context,const protozero::ConstBytes & cb,const std::string & type,const std::vector<uint16_t> * allowed_fields,Delegate & delegate,int * unknown_extensions)89 base::Status ProtoToArgsParser::ParseMessageInternal(
90     ScopedNestedKeyContext& key_context,
91     const protozero::ConstBytes& cb,
92     const std::string& type,
93     const std::vector<uint16_t>* allowed_fields,
94     Delegate& delegate,
95     int* unknown_extensions) {
96   if (auto override_result =
97           MaybeApplyOverrideForType(type, key_context, cb, delegate)) {
98     return override_result.value();
99   }
100 
101   auto idx = pool_.FindDescriptorIdx(type);
102   if (!idx) {
103     return base::Status("Failed to find proto descriptor");
104   }
105 
106   auto& descriptor = pool_.descriptors()[*idx];
107 
108   std::unordered_map<size_t, int> repeated_field_index;
109 
110   bool empty_message = true;
111 
112   protozero::ProtoDecoder decoder(cb);
113   for (protozero::Field f = decoder.ReadField(); f.valid();
114        f = decoder.ReadField()) {
115     empty_message = false;
116     auto field = descriptor.FindFieldByTag(f.id());
117     if (!field) {
118       if (unknown_extensions != nullptr) {
119         (*unknown_extensions)++;
120       }
121       // Unknown field, possibly an unknown extension.
122       continue;
123     }
124 
125     // If allowlist is not provided, reflect all fields. Otherwise, check if the
126     // current field either an extension or is in allowlist.
127     bool is_allowed = field->is_extension() || !allowed_fields ||
128                       std::find(allowed_fields->begin(), allowed_fields->end(),
129                                 f.id()) != allowed_fields->end();
130 
131     if (!is_allowed) {
132       // Field is neither an extension, nor is allowed to be
133       // reflected.
134       continue;
135     }
136     RETURN_IF_ERROR(ParseField(*field, repeated_field_index[f.id()], f,
137                                delegate, unknown_extensions));
138     if (field->is_repeated()) {
139       repeated_field_index[f.id()]++;
140     }
141   }
142 
143   if (empty_message) {
144     delegate.AddNull(key_prefix_);
145   }
146 
147   return base::OkStatus();
148 }
149 
ParseField(const FieldDescriptor & field_descriptor,int repeated_field_number,protozero::Field field,Delegate & delegate,int * unknown_extensions)150 base::Status ProtoToArgsParser::ParseField(
151     const FieldDescriptor& field_descriptor,
152     int repeated_field_number,
153     protozero::Field field,
154     Delegate& delegate,
155     int* unknown_extensions) {
156   std::string prefix_part = field_descriptor.name();
157   if (field_descriptor.is_repeated()) {
158     std::string number = std::to_string(repeated_field_number);
159     prefix_part.reserve(prefix_part.length() + number.length() + 2);
160     prefix_part.append("[");
161     prefix_part.append(number);
162     prefix_part.append("]");
163   }
164 
165   // In the args table we build up message1.message2.field1 as the column
166   // name. This will append the ".field1" suffix to |key_prefix| and then
167   // remove it when it goes out of scope.
168   ScopedNestedKeyContext key_context(key_prefix_);
169   AppendProtoType(key_prefix_.flat_key, field_descriptor.name());
170   AppendProtoType(key_prefix_.key, prefix_part);
171 
172   // If we have an override parser then use that instead and move onto the
173   // next loop.
174   if (base::Optional<base::Status> status =
175           MaybeApplyOverrideForField(field, delegate)) {
176     return *status;
177   }
178 
179   // If this is not a message we can just immediately add the column name and
180   // get the value out of |field|. However if it is a message we need to
181   // recurse into it.
182   if (field_descriptor.type() ==
183       protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
184     return ParseMessageInternal(key_context, field.as_bytes(),
185                                 field_descriptor.resolved_type_name(), nullptr,
186                                 delegate, unknown_extensions);
187   }
188 
189   return ParseSimpleField(field_descriptor, field, delegate);
190 }
191 
AddParsingOverrideForField(const std::string & field,ParsingOverrideForField func)192 void ProtoToArgsParser::AddParsingOverrideForField(
193     const std::string& field,
194     ParsingOverrideForField func) {
195   field_overrides_[field] = std::move(func);
196 }
197 
AddParsingOverrideForType(const std::string & type,ParsingOverrideForType func)198 void ProtoToArgsParser::AddParsingOverrideForType(const std::string& type,
199                                                   ParsingOverrideForType func) {
200   type_overrides_[type] = std::move(func);
201 }
202 
MaybeApplyOverrideForField(const protozero::Field & field,Delegate & delegate)203 base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForField(
204     const protozero::Field& field,
205     Delegate& delegate) {
206   auto it = field_overrides_.find(key_prefix_.flat_key);
207   if (it == field_overrides_.end())
208     return base::nullopt;
209   return it->second(field, delegate);
210 }
211 
MaybeApplyOverrideForType(const std::string & message_type,ScopedNestedKeyContext & key,const protozero::ConstBytes & data,Delegate & delegate)212 base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForType(
213     const std::string& message_type,
214     ScopedNestedKeyContext& key,
215     const protozero::ConstBytes& data,
216     Delegate& delegate) {
217   auto it = type_overrides_.find(message_type);
218   if (it == type_overrides_.end())
219     return base::nullopt;
220   return it->second(key, data, delegate);
221 }
222 
ParseSimpleField(const FieldDescriptor & descriptor,const protozero::Field & field,Delegate & delegate)223 base::Status ProtoToArgsParser::ParseSimpleField(
224     const FieldDescriptor& descriptor,
225     const protozero::Field& field,
226     Delegate& delegate) {
227   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
228   switch (descriptor.type()) {
229     case FieldDescriptorProto::TYPE_INT32:
230     case FieldDescriptorProto::TYPE_SFIXED32:
231       delegate.AddInteger(key_prefix_, field.as_int32());
232       return base::OkStatus();
233     case FieldDescriptorProto::TYPE_SINT32:
234       delegate.AddInteger(key_prefix_, field.as_sint32());
235       return base::OkStatus();
236     case FieldDescriptorProto::TYPE_INT64:
237     case FieldDescriptorProto::TYPE_SFIXED64:
238       delegate.AddInteger(key_prefix_, field.as_int64());
239       return base::OkStatus();
240     case FieldDescriptorProto::TYPE_SINT64:
241       delegate.AddInteger(key_prefix_, field.as_sint64());
242       return base::OkStatus();
243     case FieldDescriptorProto::TYPE_UINT32:
244     case FieldDescriptorProto::TYPE_FIXED32:
245       delegate.AddUnsignedInteger(key_prefix_, field.as_uint32());
246       return base::OkStatus();
247     case FieldDescriptorProto::TYPE_UINT64:
248     case FieldDescriptorProto::TYPE_FIXED64:
249       delegate.AddUnsignedInteger(key_prefix_, field.as_uint64());
250       return base::OkStatus();
251     case FieldDescriptorProto::TYPE_BOOL:
252       delegate.AddBoolean(key_prefix_, field.as_bool());
253       return base::OkStatus();
254     case FieldDescriptorProto::TYPE_DOUBLE:
255       delegate.AddDouble(key_prefix_, field.as_double());
256       return base::OkStatus();
257     case FieldDescriptorProto::TYPE_FLOAT:
258       delegate.AddDouble(key_prefix_, static_cast<double>(field.as_float()));
259       return base::OkStatus();
260     case FieldDescriptorProto::TYPE_STRING:
261       delegate.AddString(key_prefix_, field.as_string());
262       return base::OkStatus();
263     case FieldDescriptorProto::TYPE_ENUM: {
264       auto opt_enum_descriptor_idx =
265           pool_.FindDescriptorIdx(descriptor.resolved_type_name());
266       if (!opt_enum_descriptor_idx) {
267         delegate.AddInteger(key_prefix_, field.as_int32());
268         return base::OkStatus();
269       }
270       auto opt_enum_string =
271           pool_.descriptors()[*opt_enum_descriptor_idx].FindEnumString(
272               field.as_int32());
273       if (!opt_enum_string) {
274         // Fall back to the integer representation of the field.
275         delegate.AddInteger(key_prefix_, field.as_int32());
276         return base::OkStatus();
277       }
278       delegate.AddString(key_prefix_,
279                          protozero::ConstChars{opt_enum_string->data(),
280                                                opt_enum_string->size()});
281       return base::OkStatus();
282     }
283     default:
284       return base::ErrStatus(
285           "Tried to write value of type field %s (in proto type "
286           "%s) which has type enum %d",
287           descriptor.name().c_str(), descriptor.resolved_type_name().c_str(),
288           descriptor.type());
289   }
290 }
291 
EnterArray(size_t index)292 ProtoToArgsParser::ScopedNestedKeyContext ProtoToArgsParser::EnterArray(
293     size_t index) {
294   auto context = ScopedNestedKeyContext(key_prefix_);
295   key_prefix_.key += "[" + std::to_string(index) + "]";
296   return context;
297 }
298 
EnterDictionary(const std::string & name)299 ProtoToArgsParser::ScopedNestedKeyContext ProtoToArgsParser::EnterDictionary(
300     const std::string& name) {
301   auto context = ScopedNestedKeyContext(key_prefix_);
302   AppendProtoType(key_prefix_.key, name);
303   AppendProtoType(key_prefix_.flat_key, name);
304   return context;
305 }
306 
307 }  // namespace util
308 }  // namespace trace_processor
309 }  // namespace perfetto
310