1 /*
2  * Copyright (C) 2019 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/descriptors.h"
18 #include "perfetto/ext/base/string_view.h"
19 #include "perfetto/protozero/field.h"
20 
21 #include "protos/perfetto/common/descriptor.pbzero.h"
22 
23 namespace perfetto {
24 namespace trace_processor {
25 
26 namespace {
27 
CreateFieldFromDecoder(const protos::pbzero::FieldDescriptorProto::Decoder & f_decoder)28 FieldDescriptor CreateFieldFromDecoder(
29     const protos::pbzero::FieldDescriptorProto::Decoder& f_decoder) {
30   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
31   std::string type_name =
32       f_decoder.has_type_name()
33           ? base::StringView(f_decoder.type_name()).ToStdString()
34           : "";
35   // TODO(lalitm): add support for enums here.
36   uint32_t type =
37       f_decoder.has_type()
38           ? static_cast<uint32_t>(f_decoder.type())
39           : static_cast<uint32_t>(FieldDescriptorProto::TYPE_MESSAGE);
40   return FieldDescriptor(
41       base::StringView(f_decoder.name()).ToStdString(),
42       static_cast<uint32_t>(f_decoder.number()), type, std::move(type_name),
43       f_decoder.label() == FieldDescriptorProto::LABEL_REPEATED);
44 }
45 
46 }  // namespace
47 
ResolveShortType(const std::string & parent_path,const std::string & short_type)48 base::Optional<uint32_t> DescriptorPool::ResolveShortType(
49     const std::string& parent_path,
50     const std::string& short_type) {
51   PERFETTO_DCHECK(!short_type.empty());
52 
53   std::string search_path = short_type[0] == '.'
54                                 ? parent_path + short_type
55                                 : parent_path + '.' + short_type;
56   auto opt_idx = FindDescriptorIdx(search_path);
57   if (opt_idx)
58     return opt_idx;
59 
60   if (parent_path.empty())
61     return base::nullopt;
62 
63   auto parent_dot_idx = parent_path.rfind('.');
64   auto parent_substr = parent_dot_idx == std::string::npos
65                            ? ""
66                            : parent_path.substr(0, parent_dot_idx);
67   return ResolveShortType(parent_substr, short_type);
68 }
69 
AddExtensionField(const std::string & package_name,protozero::ConstBytes field_desc_proto)70 util::Status DescriptorPool::AddExtensionField(
71     const std::string& package_name,
72     protozero::ConstBytes field_desc_proto) {
73   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
74   FieldDescriptorProto::Decoder f_decoder(field_desc_proto);
75   auto field = CreateFieldFromDecoder(f_decoder);
76 
77   auto extendee_name =
78       package_name + "." + base::StringView(f_decoder.extendee()).ToStdString();
79   auto extendee = FindDescriptorIdx(extendee_name);
80   if (!extendee.has_value()) {
81     return util::ErrStatus("Extendee does not exist %s", extendee_name.c_str());
82   }
83   descriptors_[extendee.value()].AddField(field);
84   return util::OkStatus();
85 }
86 
AddNestedProtoDescriptors(const std::string & package_name,base::Optional<uint32_t> parent_idx,protozero::ConstBytes descriptor_proto)87 void DescriptorPool::AddNestedProtoDescriptors(
88     const std::string& package_name,
89     base::Optional<uint32_t> parent_idx,
90     protozero::ConstBytes descriptor_proto) {
91   protos::pbzero::DescriptorProto::Decoder decoder(descriptor_proto);
92 
93   auto parent_name =
94       parent_idx ? descriptors_[*parent_idx].full_name() : package_name;
95   auto full_name =
96       parent_name + "." + base::StringView(decoder.name()).ToStdString();
97 
98   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
99   ProtoDescriptor proto_descriptor(package_name, full_name,
100                                    ProtoDescriptor::Type::kMessage, parent_idx);
101   for (auto it = decoder.field(); it; ++it) {
102     FieldDescriptorProto::Decoder f_decoder(*it);
103     proto_descriptor.AddField(CreateFieldFromDecoder(f_decoder));
104   }
105   descriptors_.emplace_back(std::move(proto_descriptor));
106 
107   auto idx = static_cast<uint32_t>(descriptors_.size()) - 1;
108   for (auto it = decoder.enum_type(); it; ++it) {
109     AddEnumProtoDescriptors(package_name, idx, *it);
110   }
111   for (auto it = decoder.nested_type(); it; ++it) {
112     AddNestedProtoDescriptors(package_name, idx, *it);
113   }
114 }
115 
AddEnumProtoDescriptors(const std::string & package_name,base::Optional<uint32_t> parent_idx,protozero::ConstBytes descriptor_proto)116 void DescriptorPool::AddEnumProtoDescriptors(
117     const std::string& package_name,
118     base::Optional<uint32_t> parent_idx,
119     protozero::ConstBytes descriptor_proto) {
120   protos::pbzero::EnumDescriptorProto::Decoder decoder(descriptor_proto);
121 
122   auto parent_name =
123       parent_idx ? descriptors_[*parent_idx].full_name() : package_name;
124   auto full_name =
125       parent_name + "." + base::StringView(decoder.name()).ToStdString();
126 
127   ProtoDescriptor proto_descriptor(package_name, full_name,
128                                    ProtoDescriptor::Type::kEnum, base::nullopt);
129   for (auto it = decoder.value(); it; ++it) {
130     protos::pbzero::EnumValueDescriptorProto::Decoder enum_value(it->data(),
131                                                                  it->size());
132     proto_descriptor.AddEnumValue(enum_value.number(),
133                                   enum_value.name().ToStdString());
134   }
135   descriptors_.emplace_back(std::move(proto_descriptor));
136 }
137 
AddFromFileDescriptorSet(const uint8_t * file_descriptor_set_proto,size_t size)138 util::Status DescriptorPool::AddFromFileDescriptorSet(
139     const uint8_t* file_descriptor_set_proto,
140     size_t size) {
141   // First pass: extract all the message descriptors from the file and add them
142   // to the pool.
143   protos::pbzero::FileDescriptorSet::Decoder proto(file_descriptor_set_proto,
144                                                    size);
145   for (auto it = proto.file(); it; ++it) {
146     protos::pbzero::FileDescriptorProto::Decoder file(*it);
147     std::string package = "." + base::StringView(file.package()).ToStdString();
148     for (auto message_it = file.message_type(); message_it; ++message_it) {
149       AddNestedProtoDescriptors(package, base::nullopt, *message_it);
150     }
151     for (auto enum_it = file.enum_type(); enum_it; ++enum_it) {
152       AddEnumProtoDescriptors(package, base::nullopt, *enum_it);
153     }
154   }
155 
156   // Second pass: extract all the extension protos and add them to the real
157   // protos.
158   for (auto it = proto.file(); it; ++it) {
159     protos::pbzero::FileDescriptorProto::Decoder file(*it);
160 
161     std::string package = "." + base::StringView(file.package()).ToStdString();
162     for (auto ext_it = file.extension(); ext_it; ++ext_it) {
163       auto status = AddExtensionField(package, *ext_it);
164       if (!status.ok())
165         return status;
166     }
167 
168     // TODO(lalitm): we don't currently support nested extensions as they are
169     // relatively niche and probably shouldn't be used in metrics because they
170     // are confusing. Add the code for it here if we find a use for them in
171     // the future.
172   }
173 
174   // Third pass: resolve the types of all the fields to the correct indiices.
175   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
176   for (auto& descriptor : descriptors_) {
177     for (auto& field : *descriptor.mutable_fields()) {
178       if (!field.resolved_type_name().empty())
179         continue;
180 
181       if (field.type() == FieldDescriptorProto::TYPE_MESSAGE ||
182           field.type() == FieldDescriptorProto::TYPE_ENUM) {
183         auto opt_desc =
184             ResolveShortType(descriptor.full_name(), field.raw_type_name());
185         if (!opt_desc.has_value()) {
186           return util::ErrStatus(
187               "Unable to find short type %s in field inside message %s",
188               field.raw_type_name().c_str(), descriptor.full_name().c_str());
189         }
190         field.set_resolved_type_name(
191             descriptors_[opt_desc.value()].full_name());
192       }
193     }
194   }
195   return util::OkStatus();
196 }
197 
FindDescriptorIdx(const std::string & full_name) const198 base::Optional<uint32_t> DescriptorPool::FindDescriptorIdx(
199     const std::string& full_name) const {
200   auto it = std::find_if(descriptors_.begin(), descriptors_.end(),
201                          [full_name](const ProtoDescriptor& desc) {
202                            return desc.full_name() == full_name;
203                          });
204   auto idx = static_cast<uint32_t>(std::distance(descriptors_.begin(), it));
205   return idx < descriptors_.size() ? base::Optional<uint32_t>(idx)
206                                    : base::nullopt;
207 }
208 
ProtoDescriptor(std::string package_name,std::string full_name,Type type,base::Optional<uint32_t> parent_id)209 ProtoDescriptor::ProtoDescriptor(std::string package_name,
210                                  std::string full_name,
211                                  Type type,
212                                  base::Optional<uint32_t> parent_id)
213     : package_name_(std::move(package_name)),
214       full_name_(std::move(full_name)),
215       type_(type),
216       parent_id_(parent_id) {}
217 
FieldDescriptor(std::string name,uint32_t number,uint32_t type,std::string raw_type_name,bool is_repeated)218 FieldDescriptor::FieldDescriptor(std::string name,
219                                  uint32_t number,
220                                  uint32_t type,
221                                  std::string raw_type_name,
222                                  bool is_repeated)
223     : name_(std::move(name)),
224       number_(number),
225       type_(type),
226       raw_type_name_(std::move(raw_type_name)),
227       is_repeated_(is_repeated) {}
228 
229 }  // namespace trace_processor
230 }  // namespace perfetto
231