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 
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.
39 class ScopedStringAppender {
40  public:
ScopedStringAppender(const std::string & append,std::string * dest)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   }
~ScopedStringAppender()51   ~ScopedStringAppender() { dest_->erase(old_size_); }
52 
53  private:
54   size_t old_size_;
55   std::string* dest_;
56 };
57 
58 }  // namespace
59 
60 ProtoToArgsParser::Key::Key() = default;
Key(const std::string & k)61 ProtoToArgsParser::Key::Key(const std::string& k) : flat_key(k), key(k) {}
Key(const std::string & fk,const std::string & k)62 ProtoToArgsParser::Key::Key(const std::string& fk, const std::string& k)
63     : flat_key(fk), key(k) {}
64 ProtoToArgsParser::Key::~Key() = default;
65 
66 ProtoToArgsParser::Delegate::~Delegate() = default;
67 
ProtoToArgsParser(const DescriptorPool & pool)68 ProtoToArgsParser::ProtoToArgsParser(const DescriptorPool& pool) : pool_(pool) {
69   constexpr int kDefaultSize = 64;
70   key_prefix_.key.reserve(kDefaultSize);
71   key_prefix_.flat_key.reserve(kDefaultSize);
72 }
73 
ParseMessage(const protozero::ConstBytes & cb,const std::string & type,const std::vector<uint16_t> * allowed_fields,Delegate & delegate)74 base::Status ProtoToArgsParser::ParseMessage(
75     const protozero::ConstBytes& cb,
76     const std::string& type,
77     const std::vector<uint16_t>* allowed_fields,
78     Delegate& delegate) {
79   auto idx = pool_.FindDescriptorIdx(type);
80   if (!idx) {
81     return base::Status("Failed to find proto descriptor");
82   }
83 
84   auto& descriptor = pool_.descriptors()[*idx];
85 
86   std::unordered_map<size_t, int> repeated_field_index;
87 
88   protozero::ProtoDecoder decoder(cb);
89   for (protozero::Field f = decoder.ReadField(); f.valid();
90        f = decoder.ReadField()) {
91     auto field = descriptor.FindFieldByTag(f.id());
92     if (!field) {
93       // Unknown field, possibly an unknown extension.
94       continue;
95     }
96 
97     // If allowlist is not provided, reflect all fields. Otherwise, check if the
98     // current field either an extension or is in allowlist.
99     bool is_allowed = field->is_extension() || !allowed_fields ||
100                       std::find(allowed_fields->begin(), allowed_fields->end(),
101                                 f.id()) != allowed_fields->end();
102 
103     if (!is_allowed) {
104       // Field is neither an extension, nor is allowed to be
105       // reflected.
106       continue;
107     }
108     RETURN_IF_ERROR(
109         ParseField(*field, repeated_field_index[f.id()], f, delegate));
110     if (field->is_repeated()) {
111       repeated_field_index[f.id()]++;
112     }
113   }
114 
115   return base::OkStatus();
116 }
117 
ParseField(const FieldDescriptor & field_descriptor,int repeated_field_number,protozero::Field field,Delegate & delegate)118 base::Status ProtoToArgsParser::ParseField(
119     const FieldDescriptor& field_descriptor,
120     int repeated_field_number,
121     protozero::Field field,
122     Delegate& delegate) {
123   std::string prefix_part = field_descriptor.name();
124   if (field_descriptor.is_repeated()) {
125     std::string number = std::to_string(repeated_field_number);
126     prefix_part.reserve(prefix_part.length() + number.length() + 2);
127     prefix_part.append("[");
128     prefix_part.append(number);
129     prefix_part.append("]");
130   }
131 
132   // In the args table we build up message1.message2.field1 as the column
133   // name. This will append the ".field1" suffix to |key_prefix| and then
134   // remove it when it goes out of scope.
135   ScopedStringAppender scoped_prefix(prefix_part, &key_prefix_.key);
136   ScopedStringAppender scoped_flat_key_prefix(field_descriptor.name(),
137                                               &key_prefix_.flat_key);
138 
139   // If we have an override parser then use that instead and move onto the
140   // next loop.
141   if (base::Optional<base::Status> status =
142           MaybeApplyOverride(field, delegate)) {
143     return *status;
144   }
145 
146   // If this is not a message we can just immediately add the column name and
147   // get the value out of |field|. However if it is a message we need to
148   // recurse into it.
149   if (field_descriptor.type() ==
150       protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
151     return ParseMessage(field.as_bytes(), field_descriptor.resolved_type_name(),
152                         nullptr, delegate);
153   }
154 
155   return ParseSimpleField(field_descriptor, field, delegate);
156 }
157 
AddParsingOverride(std::string field,ParsingOverride func)158 void ProtoToArgsParser::AddParsingOverride(std::string field,
159                                            ParsingOverride func) {
160   overrides_[std::move(field)] = std::move(func);
161 }
162 
MaybeApplyOverride(const protozero::Field & field,Delegate & delegate)163 base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverride(
164     const protozero::Field& field,
165     Delegate& delegate) {
166   auto it = overrides_.find(key_prefix_.flat_key);
167   if (it == overrides_.end())
168     return base::nullopt;
169   return it->second(field, delegate);
170 }
171 
ParseSimpleField(const FieldDescriptor & descriptor,const protozero::Field & field,Delegate & delegate)172 base::Status ProtoToArgsParser::ParseSimpleField(
173     const FieldDescriptor& descriptor,
174     const protozero::Field& field,
175     Delegate& delegate) {
176   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
177   switch (descriptor.type()) {
178     case FieldDescriptorProto::TYPE_INT32:
179     case FieldDescriptorProto::TYPE_SFIXED32:
180     case FieldDescriptorProto::TYPE_FIXED32:
181       delegate.AddInteger(key_prefix_, field.as_int32());
182       return base::OkStatus();
183     case FieldDescriptorProto::TYPE_SINT32:
184       delegate.AddInteger(key_prefix_, field.as_sint32());
185       return base::OkStatus();
186     case FieldDescriptorProto::TYPE_INT64:
187     case FieldDescriptorProto::TYPE_SFIXED64:
188     case FieldDescriptorProto::TYPE_FIXED64:
189       delegate.AddInteger(key_prefix_, field.as_int64());
190       return base::OkStatus();
191     case FieldDescriptorProto::TYPE_SINT64:
192       delegate.AddInteger(key_prefix_, field.as_sint64());
193       return base::OkStatus();
194     case FieldDescriptorProto::TYPE_UINT32:
195       delegate.AddUnsignedInteger(key_prefix_, field.as_uint32());
196       return base::OkStatus();
197     case FieldDescriptorProto::TYPE_UINT64:
198       delegate.AddUnsignedInteger(key_prefix_, field.as_uint64());
199       return base::OkStatus();
200     case FieldDescriptorProto::TYPE_BOOL:
201       delegate.AddBoolean(key_prefix_, field.as_bool());
202       return base::OkStatus();
203     case FieldDescriptorProto::TYPE_DOUBLE:
204       delegate.AddDouble(key_prefix_, field.as_double());
205       return base::OkStatus();
206     case FieldDescriptorProto::TYPE_FLOAT:
207       delegate.AddDouble(key_prefix_, static_cast<double>(field.as_float()));
208       return base::OkStatus();
209     case FieldDescriptorProto::TYPE_STRING:
210       delegate.AddString(key_prefix_, field.as_string());
211       return base::OkStatus();
212     case FieldDescriptorProto::TYPE_ENUM: {
213       auto opt_enum_descriptor_idx =
214           pool_.FindDescriptorIdx(descriptor.resolved_type_name());
215       if (!opt_enum_descriptor_idx) {
216         delegate.AddInteger(key_prefix_, field.as_int32());
217         return base::OkStatus();
218       }
219       auto opt_enum_string =
220           pool_.descriptors()[*opt_enum_descriptor_idx].FindEnumString(
221               field.as_int32());
222       if (!opt_enum_string) {
223         // Fall back to the integer representation of the field.
224         delegate.AddInteger(key_prefix_, field.as_int32());
225         return base::OkStatus();
226       }
227       delegate.AddString(key_prefix_,
228                          protozero::ConstChars{opt_enum_string->data(),
229                                                opt_enum_string->size()});
230       return base::OkStatus();
231     }
232     default:
233       return base::ErrStatus(
234           "Tried to write value of type field %s (in proto type "
235           "%s) which has type enum %d",
236           descriptor.name().c_str(), descriptor.resolved_type_name().c_str(),
237           descriptor.type());
238   }
239 }
240 
241 }  // namespace util
242 }  // namespace trace_processor
243 }  // namespace perfetto
244