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