• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/util/descriptors.h"
18 
19 #include "perfetto/ext/base/string_utils.h"
20 #include "perfetto/ext/base/string_view.h"
21 #include "perfetto/protozero/field.h"
22 #include "perfetto/protozero/scattered_heap_buffer.h"
23 #include "protos/perfetto/common/descriptor.pbzero.h"
24 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
25 #include "src/trace_processor/util/status_macros.h"
26 
27 namespace perfetto {
28 namespace trace_processor {
29 
CreateFieldFromDecoder(const protos::pbzero::FieldDescriptorProto::Decoder & f_decoder,bool is_extension)30 FieldDescriptor CreateFieldFromDecoder(
31     const protos::pbzero::FieldDescriptorProto::Decoder& f_decoder,
32     bool is_extension) {
33   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
34   std::string type_name =
35       f_decoder.has_type_name()
36           ? base::StringView(f_decoder.type_name()).ToStdString()
37           : "";
38   // TODO(lalitm): add support for enums here.
39   uint32_t type =
40       f_decoder.has_type()
41           ? static_cast<uint32_t>(f_decoder.type())
42           : static_cast<uint32_t>(FieldDescriptorProto::TYPE_MESSAGE);
43   protos::pbzero::FieldOptions::Decoder opt(f_decoder.options());
44   return FieldDescriptor(
45       base::StringView(f_decoder.name()).ToStdString(),
46       static_cast<uint32_t>(f_decoder.number()), type, std::move(type_name),
47       f_decoder.label() == FieldDescriptorProto::LABEL_REPEATED, opt.packed(),
48       is_extension);
49 }
50 
ResolveShortType(const std::string & parent_path,const std::string & short_type)51 std::optional<uint32_t> DescriptorPool::ResolveShortType(
52     const std::string& parent_path,
53     const std::string& short_type) {
54   PERFETTO_DCHECK(!short_type.empty());
55 
56   std::string search_path = short_type[0] == '.'
57                                 ? parent_path + short_type
58                                 : parent_path + '.' + short_type;
59   auto opt_idx = FindDescriptorIdx(search_path);
60   if (opt_idx)
61     return opt_idx;
62 
63   if (parent_path.empty())
64     return std::nullopt;
65 
66   auto parent_dot_idx = parent_path.rfind('.');
67   auto parent_substr = parent_dot_idx == std::string::npos
68                            ? ""
69                            : parent_path.substr(0, parent_dot_idx);
70   return ResolveShortType(parent_substr, short_type);
71 }
72 
AddExtensionField(const std::string & package_name,protozero::ConstBytes field_desc_proto)73 util::Status DescriptorPool::AddExtensionField(
74     const std::string& package_name,
75     protozero::ConstBytes field_desc_proto) {
76   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
77   FieldDescriptorProto::Decoder f_decoder(field_desc_proto);
78   auto field = CreateFieldFromDecoder(f_decoder, true);
79 
80   auto extendee_name = base::StringView(f_decoder.extendee()).ToStdString();
81   if (extendee_name.empty()) {
82     return util::ErrStatus("Extendee name is empty");
83   }
84 
85   if (extendee_name[0] != '.') {
86     // Only prepend if the extendee is not fully qualified
87     extendee_name = package_name + "." + extendee_name;
88   }
89   auto extendee = FindDescriptorIdx(extendee_name);
90   if (!extendee.has_value()) {
91     return util::ErrStatus("Extendee does not exist %s", extendee_name.c_str());
92   }
93   descriptors_[extendee.value()].AddField(field);
94   return util::OkStatus();
95 }
96 
AddNestedProtoDescriptors(const std::string & file_name,const std::string & package_name,std::optional<uint32_t> parent_idx,protozero::ConstBytes descriptor_proto,std::vector<ExtensionInfo> * extensions,bool merge_existing_messages)97 util::Status DescriptorPool::AddNestedProtoDescriptors(
98     const std::string& file_name,
99     const std::string& package_name,
100     std::optional<uint32_t> parent_idx,
101     protozero::ConstBytes descriptor_proto,
102     std::vector<ExtensionInfo>* extensions,
103     bool merge_existing_messages) {
104   protos::pbzero::DescriptorProto::Decoder decoder(descriptor_proto);
105 
106   auto parent_name =
107       parent_idx ? descriptors_[*parent_idx].full_name() : package_name;
108   auto full_name =
109       parent_name + "." + base::StringView(decoder.name()).ToStdString();
110 
111   auto idx = FindDescriptorIdx(full_name);
112   if (idx.has_value() && !merge_existing_messages) {
113     const auto& existing_descriptor = descriptors_[*idx];
114     return util::ErrStatus("%s: %s was already defined in file %s",
115                            file_name.c_str(), full_name.c_str(),
116                            existing_descriptor.file_name().c_str());
117   }
118   if (!idx.has_value()) {
119     ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
120                                      ProtoDescriptor::Type::kMessage,
121                                      parent_idx);
122     idx = AddProtoDescriptor(std::move(proto_descriptor));
123   }
124   ProtoDescriptor& proto_descriptor = descriptors_[*idx];
125   if (proto_descriptor.type() != ProtoDescriptor::Type::kMessage) {
126     return util::ErrStatus("%s was enum, redefined as message",
127                            full_name.c_str());
128   }
129 
130   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
131   for (auto it = decoder.field(); it; ++it) {
132     FieldDescriptorProto::Decoder f_decoder(*it);
133     auto field = CreateFieldFromDecoder(f_decoder, /*is_extension=*/false);
134     auto existing_field = proto_descriptor.FindFieldByTag(field.number());
135     if (!existing_field) {
136       proto_descriptor.AddField(std::move(field));
137     } else {
138       if (field.type() != existing_field->type()) {
139         return util::ErrStatus("Field %s is re-introduced with different type",
140                                field.name().c_str());
141       }
142       if ((field.type() == FieldDescriptorProto::TYPE_MESSAGE ||
143            field.type() == FieldDescriptorProto::TYPE_ENUM) &&
144           field.raw_type_name() != existing_field->raw_type_name()) {
145         return util::ErrStatus(
146             "Field %s is re-introduced with different type %s (was %s)",
147             field.name().c_str(), field.raw_type_name().c_str(),
148             existing_field->raw_type_name().c_str());
149       }
150     }
151   }
152 
153   for (auto it = decoder.enum_type(); it; ++it) {
154     RETURN_IF_ERROR(AddEnumProtoDescriptors(file_name, package_name, idx, *it,
155                                             merge_existing_messages));
156   }
157   for (auto it = decoder.nested_type(); it; ++it) {
158     RETURN_IF_ERROR(AddNestedProtoDescriptors(file_name, package_name, idx, *it,
159                                               extensions,
160                                               merge_existing_messages));
161   }
162   for (auto ext_it = decoder.extension(); ext_it; ++ext_it) {
163     extensions->emplace_back(package_name, *ext_it);
164   }
165   return util::OkStatus();
166 }
167 
AddEnumProtoDescriptors(const std::string & file_name,const std::string & package_name,std::optional<uint32_t> parent_idx,protozero::ConstBytes descriptor_proto,bool merge_existing_messages)168 util::Status DescriptorPool::AddEnumProtoDescriptors(
169     const std::string& file_name,
170     const std::string& package_name,
171     std::optional<uint32_t> parent_idx,
172     protozero::ConstBytes descriptor_proto,
173     bool merge_existing_messages) {
174   protos::pbzero::EnumDescriptorProto::Decoder decoder(descriptor_proto);
175 
176   auto parent_name =
177       parent_idx ? descriptors_[*parent_idx].full_name() : package_name;
178   auto full_name =
179       parent_name + "." + base::StringView(decoder.name()).ToStdString();
180 
181   auto prev_idx = FindDescriptorIdx(full_name);
182   if (prev_idx.has_value() && !merge_existing_messages) {
183     const auto& existing_descriptor = descriptors_[*prev_idx];
184     return util::ErrStatus("%s: %s was already defined in file %s",
185                            file_name.c_str(), full_name.c_str(),
186                            existing_descriptor.file_name().c_str());
187   }
188   if (!prev_idx.has_value()) {
189     ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
190                                      ProtoDescriptor::Type::kEnum,
191                                      std::nullopt);
192     prev_idx = AddProtoDescriptor(std::move(proto_descriptor));
193   }
194   ProtoDescriptor& proto_descriptor = descriptors_[*prev_idx];
195   if (proto_descriptor.type() != ProtoDescriptor::Type::kEnum) {
196     return util::ErrStatus("%s was message, redefined as enum",
197                            full_name.c_str());
198   }
199 
200   for (auto it = decoder.value(); it; ++it) {
201     protos::pbzero::EnumValueDescriptorProto::Decoder enum_value(it->data(),
202                                                                  it->size());
203     proto_descriptor.AddEnumValue(enum_value.number(),
204                                   enum_value.name().ToStdString());
205   }
206 
207   return util::OkStatus();
208 }
209 
AddFromFileDescriptorSet(const uint8_t * file_descriptor_set_proto,size_t size,const std::vector<std::string> & skip_prefixes,bool merge_existing_messages)210 util::Status DescriptorPool::AddFromFileDescriptorSet(
211     const uint8_t* file_descriptor_set_proto,
212     size_t size,
213     const std::vector<std::string>& skip_prefixes,
214     bool merge_existing_messages) {
215   protos::pbzero::FileDescriptorSet::Decoder proto(file_descriptor_set_proto,
216                                                    size);
217   std::vector<ExtensionInfo> extensions;
218   for (auto it = proto.file(); it; ++it) {
219     protos::pbzero::FileDescriptorProto::Decoder file(*it);
220     const std::string file_name = file.name().ToStdString();
221     if (base::StartsWithAny(file_name, skip_prefixes))
222       continue;
223     if (processed_files_.find(file_name) != processed_files_.end()) {
224       // This file has been loaded once already. Skip.
225       continue;
226     }
227     processed_files_.insert(file_name);
228     std::string package = "." + base::StringView(file.package()).ToStdString();
229     for (auto message_it = file.message_type(); message_it; ++message_it) {
230       RETURN_IF_ERROR(AddNestedProtoDescriptors(
231           file_name, package, std::nullopt, *message_it, &extensions,
232           merge_existing_messages));
233     }
234     for (auto enum_it = file.enum_type(); enum_it; ++enum_it) {
235       RETURN_IF_ERROR(AddEnumProtoDescriptors(
236           file_name, package, std::nullopt, *enum_it, merge_existing_messages));
237     }
238     for (auto ext_it = file.extension(); ext_it; ++ext_it) {
239       extensions.emplace_back(package, *ext_it);
240     }
241   }
242 
243   // Second pass: Add extension fields to the real protos.
244   for (const auto& extension : extensions) {
245     auto status = AddExtensionField(extension.first, extension.second);
246     if (!status.ok())
247       return status;
248   }
249 
250   // Third pass: resolve the types of all the fields to the correct indiices.
251   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
252   for (auto& descriptor : descriptors_) {
253     for (auto& entry : *descriptor.mutable_fields()) {
254       auto& field = entry.second;
255       if (!field.resolved_type_name().empty())
256         continue;
257 
258       if (field.type() == FieldDescriptorProto::TYPE_MESSAGE ||
259           field.type() == FieldDescriptorProto::TYPE_ENUM) {
260         auto opt_desc =
261             ResolveShortType(descriptor.full_name(), field.raw_type_name());
262         if (!opt_desc.has_value()) {
263           return util::ErrStatus(
264               "Unable to find short type %s in field inside message %s",
265               field.raw_type_name().c_str(), descriptor.full_name().c_str());
266         }
267         field.set_resolved_type_name(
268             descriptors_[opt_desc.value()].full_name());
269       }
270     }
271   }
272   return util::OkStatus();
273 }
274 
FindDescriptorIdx(const std::string & full_name) const275 std::optional<uint32_t> DescriptorPool::FindDescriptorIdx(
276     const std::string& full_name) const {
277   auto it = full_name_to_descriptor_index_.find(full_name);
278   if (it == full_name_to_descriptor_index_.end()) {
279     return std::nullopt;
280   }
281   return it->second;
282 }
283 
SerializeAsDescriptorSet()284 std::vector<uint8_t> DescriptorPool::SerializeAsDescriptorSet() {
285   protozero::HeapBuffered<protos::pbzero::DescriptorSet> descs;
286   for (auto& desc : descriptors()) {
287     protos::pbzero::DescriptorProto* proto_descriptor =
288         descs->add_descriptors();
289     proto_descriptor->set_name(desc.full_name());
290     for (auto& entry : desc.fields()) {
291       auto& field = entry.second;
292       protos::pbzero::FieldDescriptorProto* field_descriptor =
293           proto_descriptor->add_field();
294       field_descriptor->set_name(field.name());
295       field_descriptor->set_number(static_cast<int32_t>(field.number()));
296       // We do not support required fields. They will show up as optional
297       // after serialization.
298       field_descriptor->set_label(
299           field.is_repeated()
300               ? protos::pbzero::FieldDescriptorProto::LABEL_REPEATED
301               : protos::pbzero::FieldDescriptorProto::LABEL_OPTIONAL);
302       field_descriptor->set_type_name(field.resolved_type_name());
303       field_descriptor->set_type(
304           static_cast<protos::pbzero::FieldDescriptorProto_Type>(field.type()));
305     }
306   }
307   return descs.SerializeAsArray();
308 }
309 
AddProtoDescriptor(ProtoDescriptor descriptor)310 uint32_t DescriptorPool::AddProtoDescriptor(ProtoDescriptor descriptor) {
311   uint32_t idx = static_cast<uint32_t>(descriptors_.size());
312   full_name_to_descriptor_index_[descriptor.full_name()] = idx;
313   descriptors_.emplace_back(std::move(descriptor));
314   return idx;
315 }
316 
ProtoDescriptor(std::string file_name,std::string package_name,std::string full_name,Type type,std::optional<uint32_t> parent_id)317 ProtoDescriptor::ProtoDescriptor(std::string file_name,
318                                  std::string package_name,
319                                  std::string full_name,
320                                  Type type,
321                                  std::optional<uint32_t> parent_id)
322     : file_name_(std::move(file_name)),
323       package_name_(std::move(package_name)),
324       full_name_(std::move(full_name)),
325       type_(type),
326       parent_id_(parent_id) {}
327 
FieldDescriptor(std::string name,uint32_t number,uint32_t type,std::string raw_type_name,bool is_repeated,bool is_packed,bool is_extension)328 FieldDescriptor::FieldDescriptor(std::string name,
329                                  uint32_t number,
330                                  uint32_t type,
331                                  std::string raw_type_name,
332                                  bool is_repeated,
333                                  bool is_packed,
334                                  bool is_extension)
335     : name_(std::move(name)),
336       number_(number),
337       type_(type),
338       raw_type_name_(std::move(raw_type_name)),
339       is_repeated_(is_repeated),
340       is_packed_(is_packed),
341       is_extension_(is_extension) {}
342 
343 }  // namespace trace_processor
344 }  // namespace perfetto
345