blob: 8e4eb22863abc790bfa56188eb7b57aecac3393e [file] [log] [blame]
Alexander Timin5a99b5c2021-05-11 22:48:07 +00001/*
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
23namespace perfetto {
24namespace trace_processor {
25namespace util {
26
27namespace {
28
29// ScopedStringAppender will add |append| to |dest| when constructed and
30// erases the appended suffix from |dest| when it goes out of scope. Thus
31// |dest| must be valid for the entire lifetime of ScopedStringAppender.
32//
33// This is useful as we descend into a proto since the column names just
34// appended with ".field_name" as we go lower.
35//
36// I.E. message1.message2.field_name1 is a column, but we'll then need to
37// append message1.message2.field_name2 afterwards so we only need to append
38// "field_name1" within some scope.
39class ScopedStringAppender {
40 public:
41 ScopedStringAppender(const std::string& append, std::string* dest)
42 : old_size_(dest->size()), dest_(dest) {
43 if (dest->empty()) {
44 dest_->reserve(append.size());
45 } else {
46 dest_->reserve(old_size_ + 1 + append.size());
47 dest_->append(".");
48 }
49 dest_->append(append);
50 }
51 ~ScopedStringAppender() { dest_->erase(old_size_); }
52
53 private:
54 size_t old_size_;
55 std::string* dest_;
56};
57
58} // namespace
59
Alexander Timin99653b12021-05-11 22:50:00 +000060ProtoToArgsParser::Key::Key() = default;
61ProtoToArgsParser::Key::Key(const std::string& k) : flat_key(k), key(k) {}
62ProtoToArgsParser::Key::Key(const std::string& fk, const std::string& k)
63 : flat_key(fk), key(k) {}
64ProtoToArgsParser::Key::~Key() = default;
65
Alexander Timin97d87852021-05-17 18:01:33 +000066ProtoToArgsParser::ScopedNestedKeyContext::ScopedNestedKeyContext(Key& key)
67 : key_(key),
68 old_flat_key_length_(key.flat_key.length()),
69 old_key_length_(key.key.length()) {}
70
71ProtoToArgsParser::ScopedNestedKeyContext::ScopedNestedKeyContext(
72 ProtoToArgsParser::ScopedNestedKeyContext&& other)
73 : key_(other.key_),
74 old_flat_key_length_(other.old_flat_key_length_),
75 old_key_length_(other.old_key_length_) {
76 other.old_flat_key_length_ = base::nullopt;
77 other.old_key_length_ = base::nullopt;
78}
79
80ProtoToArgsParser::ScopedNestedKeyContext::~ScopedNestedKeyContext() {
81 Reset();
82}
83
84void ProtoToArgsParser::ScopedNestedKeyContext::Reset() {
85 if (old_flat_key_length_)
86 key_.flat_key.resize(old_flat_key_length_.value());
87 if (old_key_length_)
88 key_.key.resize(old_key_length_.value());
89 old_flat_key_length_ = base::nullopt;
90 old_key_length_ = base::nullopt;
91}
92
Alexander Timin5a99b5c2021-05-11 22:48:07 +000093ProtoToArgsParser::Delegate::~Delegate() = default;
94
95ProtoToArgsParser::ProtoToArgsParser(const DescriptorPool& pool) : pool_(pool) {
96 constexpr int kDefaultSize = 64;
97 key_prefix_.key.reserve(kDefaultSize);
98 key_prefix_.flat_key.reserve(kDefaultSize);
99}
100
101base::Status ProtoToArgsParser::ParseMessage(
102 const protozero::ConstBytes& cb,
103 const std::string& type,
104 const std::vector<uint16_t>* allowed_fields,
105 Delegate& delegate) {
106 auto idx = pool_.FindDescriptorIdx(type);
107 if (!idx) {
108 return base::Status("Failed to find proto descriptor");
109 }
110
111 auto& descriptor = pool_.descriptors()[*idx];
112
113 std::unordered_map<size_t, int> repeated_field_index;
114
115 protozero::ProtoDecoder decoder(cb);
116 for (protozero::Field f = decoder.ReadField(); f.valid();
117 f = decoder.ReadField()) {
118 auto field = descriptor.FindFieldByTag(f.id());
119 if (!field) {
120 // Unknown field, possibly an unknown extension.
121 continue;
122 }
123
124 // If allowlist is not provided, reflect all fields. Otherwise, check if the
125 // current field either an extension or is in allowlist.
126 bool is_allowed = field->is_extension() || !allowed_fields ||
127 std::find(allowed_fields->begin(), allowed_fields->end(),
128 f.id()) != allowed_fields->end();
129
130 if (!is_allowed) {
131 // Field is neither an extension, nor is allowed to be
132 // reflected.
133 continue;
134 }
135 RETURN_IF_ERROR(
136 ParseField(*field, repeated_field_index[f.id()], f, delegate));
137 if (field->is_repeated()) {
138 repeated_field_index[f.id()]++;
139 }
140 }
141
142 return base::OkStatus();
143}
144
145base::Status ProtoToArgsParser::ParseField(
146 const FieldDescriptor& field_descriptor,
147 int repeated_field_number,
148 protozero::Field field,
149 Delegate& delegate) {
150 std::string prefix_part = field_descriptor.name();
151 if (field_descriptor.is_repeated()) {
152 std::string number = std::to_string(repeated_field_number);
153 prefix_part.reserve(prefix_part.length() + number.length() + 2);
154 prefix_part.append("[");
155 prefix_part.append(number);
156 prefix_part.append("]");
157 }
158
159 // In the args table we build up message1.message2.field1 as the column
160 // name. This will append the ".field1" suffix to |key_prefix| and then
161 // remove it when it goes out of scope.
162 ScopedStringAppender scoped_prefix(prefix_part, &key_prefix_.key);
163 ScopedStringAppender scoped_flat_key_prefix(field_descriptor.name(),
164 &key_prefix_.flat_key);
165
166 // If we have an override parser then use that instead and move onto the
167 // next loop.
168 if (base::Optional<base::Status> status =
169 MaybeApplyOverride(field, delegate)) {
170 return *status;
171 }
172
173 // If this is not a message we can just immediately add the column name and
174 // get the value out of |field|. However if it is a message we need to
175 // recurse into it.
176 if (field_descriptor.type() ==
177 protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
178 return ParseMessage(field.as_bytes(), field_descriptor.resolved_type_name(),
179 nullptr, delegate);
180 }
181
182 return ParseSimpleField(field_descriptor, field, delegate);
183}
184
185void ProtoToArgsParser::AddParsingOverride(std::string field,
186 ParsingOverride func) {
187 overrides_[std::move(field)] = std::move(func);
188}
189
190base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverride(
191 const protozero::Field& field,
192 Delegate& delegate) {
193 auto it = overrides_.find(key_prefix_.flat_key);
194 if (it == overrides_.end())
195 return base::nullopt;
196 return it->second(field, delegate);
197}
198
199base::Status ProtoToArgsParser::ParseSimpleField(
200 const FieldDescriptor& descriptor,
201 const protozero::Field& field,
202 Delegate& delegate) {
203 using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
204 switch (descriptor.type()) {
205 case FieldDescriptorProto::TYPE_INT32:
206 case FieldDescriptorProto::TYPE_SFIXED32:
207 case FieldDescriptorProto::TYPE_FIXED32:
208 delegate.AddInteger(key_prefix_, field.as_int32());
209 return base::OkStatus();
210 case FieldDescriptorProto::TYPE_SINT32:
211 delegate.AddInteger(key_prefix_, field.as_sint32());
212 return base::OkStatus();
213 case FieldDescriptorProto::TYPE_INT64:
214 case FieldDescriptorProto::TYPE_SFIXED64:
215 case FieldDescriptorProto::TYPE_FIXED64:
216 delegate.AddInteger(key_prefix_, field.as_int64());
217 return base::OkStatus();
218 case FieldDescriptorProto::TYPE_SINT64:
219 delegate.AddInteger(key_prefix_, field.as_sint64());
220 return base::OkStatus();
221 case FieldDescriptorProto::TYPE_UINT32:
222 delegate.AddUnsignedInteger(key_prefix_, field.as_uint32());
223 return base::OkStatus();
224 case FieldDescriptorProto::TYPE_UINT64:
225 delegate.AddUnsignedInteger(key_prefix_, field.as_uint64());
226 return base::OkStatus();
227 case FieldDescriptorProto::TYPE_BOOL:
228 delegate.AddBoolean(key_prefix_, field.as_bool());
229 return base::OkStatus();
230 case FieldDescriptorProto::TYPE_DOUBLE:
231 delegate.AddDouble(key_prefix_, field.as_double());
232 return base::OkStatus();
233 case FieldDescriptorProto::TYPE_FLOAT:
234 delegate.AddDouble(key_prefix_, static_cast<double>(field.as_float()));
235 return base::OkStatus();
236 case FieldDescriptorProto::TYPE_STRING:
237 delegate.AddString(key_prefix_, field.as_string());
238 return base::OkStatus();
239 case FieldDescriptorProto::TYPE_ENUM: {
240 auto opt_enum_descriptor_idx =
241 pool_.FindDescriptorIdx(descriptor.resolved_type_name());
242 if (!opt_enum_descriptor_idx) {
243 delegate.AddInteger(key_prefix_, field.as_int32());
244 return base::OkStatus();
245 }
246 auto opt_enum_string =
247 pool_.descriptors()[*opt_enum_descriptor_idx].FindEnumString(
248 field.as_int32());
249 if (!opt_enum_string) {
250 // Fall back to the integer representation of the field.
251 delegate.AddInteger(key_prefix_, field.as_int32());
252 return base::OkStatus();
253 }
254 delegate.AddString(key_prefix_,
255 protozero::ConstChars{opt_enum_string->data(),
256 opt_enum_string->size()});
257 return base::OkStatus();
258 }
259 default:
260 return base::ErrStatus(
261 "Tried to write value of type field %s (in proto type "
262 "%s) which has type enum %d",
263 descriptor.name().c_str(), descriptor.resolved_type_name().c_str(),
264 descriptor.type());
265 }
266}
267
Alexander Timin97d87852021-05-17 18:01:33 +0000268ProtoToArgsParser::ScopedNestedKeyContext ProtoToArgsParser::EnterArray(
269 size_t index) {
270 auto context = ScopedNestedKeyContext(key_prefix_);
271 key_prefix_.key += "[" + std::to_string(index) + "]";
272 return context;
273}
274
275ProtoToArgsParser::ScopedNestedKeyContext ProtoToArgsParser::EnterDictionary(
276 const std::string& name) {
277 auto context = ScopedNestedKeyContext(key_prefix_);
278 if (!key_prefix_.key.empty())
279 key_prefix_.key += '.';
280 key_prefix_.key += name;
281 if (!key_prefix_.flat_key.empty())
282 key_prefix_.flat_key += '.';
283 key_prefix_.flat_key += name;
284 return context;
285}
286
Alexander Timin5a99b5c2021-05-11 22:48:07 +0000287} // namespace util
288} // namespace trace_processor
289} // namespace perfetto