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 return FieldDescriptor(
44 base::StringView(f_decoder.name()).ToStdString(),
45 static_cast<uint32_t>(f_decoder.number()), type, std::move(type_name),
46 f_decoder.label() == FieldDescriptorProto::LABEL_REPEATED, is_extension);
47 }
48
ResolveShortType(const std::string & parent_path,const std::string & short_type)49 base::Optional<uint32_t> DescriptorPool::ResolveShortType(
50 const std::string& parent_path,
51 const std::string& short_type) {
52 PERFETTO_DCHECK(!short_type.empty());
53
54 std::string search_path = short_type[0] == '.'
55 ? parent_path + short_type
56 : parent_path + '.' + short_type;
57 auto opt_idx = FindDescriptorIdx(search_path);
58 if (opt_idx)
59 return opt_idx;
60
61 if (parent_path.empty())
62 return base::nullopt;
63
64 auto parent_dot_idx = parent_path.rfind('.');
65 auto parent_substr = parent_dot_idx == std::string::npos
66 ? ""
67 : parent_path.substr(0, parent_dot_idx);
68 return ResolveShortType(parent_substr, short_type);
69 }
70
AddExtensionField(const std::string & package_name,protozero::ConstBytes field_desc_proto)71 util::Status DescriptorPool::AddExtensionField(
72 const std::string& package_name,
73 protozero::ConstBytes field_desc_proto) {
74 using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
75 FieldDescriptorProto::Decoder f_decoder(field_desc_proto);
76 auto field = CreateFieldFromDecoder(f_decoder, true);
77
78 auto extendee_name = base::StringView(f_decoder.extendee()).ToStdString();
79 if (extendee_name.empty()) {
80 return util::ErrStatus("Extendee name is empty");
81 }
82
83 if (extendee_name[0] != '.') {
84 // Only prepend if the extendee is not fully qualified
85 extendee_name = package_name + "." + extendee_name;
86 }
87 auto extendee = FindDescriptorIdx(extendee_name);
88 if (!extendee.has_value()) {
89 return util::ErrStatus("Extendee does not exist %s", extendee_name.c_str());
90 }
91 descriptors_[extendee.value()].AddField(field);
92 return util::OkStatus();
93 }
94
AddNestedProtoDescriptors(const std::string & file_name,const std::string & package_name,base::Optional<uint32_t> parent_idx,protozero::ConstBytes descriptor_proto,std::vector<ExtensionInfo> * extensions,bool merge_existing_messages)95 util::Status DescriptorPool::AddNestedProtoDescriptors(
96 const std::string& file_name,
97 const std::string& package_name,
98 base::Optional<uint32_t> parent_idx,
99 protozero::ConstBytes descriptor_proto,
100 std::vector<ExtensionInfo>* extensions,
101 bool merge_existing_messages) {
102 protos::pbzero::DescriptorProto::Decoder decoder(descriptor_proto);
103
104 auto parent_name =
105 parent_idx ? descriptors_[*parent_idx].full_name() : package_name;
106 auto full_name =
107 parent_name + "." + base::StringView(decoder.name()).ToStdString();
108
109 auto prev_idx = FindDescriptorIdx(full_name);
110 if (prev_idx.has_value() && !merge_existing_messages) {
111 const auto& existing_descriptor = descriptors_[*prev_idx];
112 return util::ErrStatus("%s: %s was already defined in file %s",
113 file_name.c_str(), full_name.c_str(),
114 existing_descriptor.file_name().c_str());
115 }
116 if (!prev_idx.has_value()) {
117 ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
118 ProtoDescriptor::Type::kMessage,
119 parent_idx);
120 prev_idx = AddProtoDescriptor(std::move(proto_descriptor));
121 }
122 ProtoDescriptor& proto_descriptor = descriptors_[*prev_idx];
123 if (proto_descriptor.type() != ProtoDescriptor::Type::kMessage) {
124 return util::ErrStatus("%s was enum, redefined as message",
125 full_name.c_str());
126 }
127
128 using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
129 for (auto it = decoder.field(); it; ++it) {
130 FieldDescriptorProto::Decoder f_decoder(*it);
131 auto field = CreateFieldFromDecoder(f_decoder, /*is_extension=*/false);
132 auto existing_field = proto_descriptor.FindFieldByTag(field.number());
133 if (!existing_field) {
134 proto_descriptor.AddField(std::move(field));
135 } else {
136 if (field.type() != existing_field->type()) {
137 return util::ErrStatus("Field %s is re-introduced with different type",
138 field.name().c_str());
139 }
140 if ((field.type() == FieldDescriptorProto::TYPE_MESSAGE ||
141 field.type() == FieldDescriptorProto::TYPE_ENUM) &&
142 field.raw_type_name() != existing_field->raw_type_name()) {
143 return util::ErrStatus(
144 "Field %s is re-introduced with different type %s (was %s)",
145 field.name().c_str(), field.raw_type_name().c_str(),
146 existing_field->raw_type_name().c_str());
147 }
148 }
149 }
150
151 auto idx = static_cast<uint32_t>(descriptors_.size()) - 1;
152 for (auto it = decoder.enum_type(); it; ++it) {
153 RETURN_IF_ERROR(AddEnumProtoDescriptors(file_name, package_name, idx, *it,
154 merge_existing_messages));
155 }
156 for (auto it = decoder.nested_type(); it; ++it) {
157 RETURN_IF_ERROR(AddNestedProtoDescriptors(file_name, package_name, idx, *it,
158 extensions,
159 merge_existing_messages));
160 }
161 for (auto ext_it = decoder.extension(); ext_it; ++ext_it) {
162 extensions->emplace_back(package_name, *ext_it);
163 }
164 return util::OkStatus();
165 }
166
AddEnumProtoDescriptors(const std::string & file_name,const std::string & package_name,base::Optional<uint32_t> parent_idx,protozero::ConstBytes descriptor_proto,bool merge_existing_messages)167 util::Status DescriptorPool::AddEnumProtoDescriptors(
168 const std::string& file_name,
169 const std::string& package_name,
170 base::Optional<uint32_t> parent_idx,
171 protozero::ConstBytes descriptor_proto,
172 bool merge_existing_messages) {
173 protos::pbzero::EnumDescriptorProto::Decoder decoder(descriptor_proto);
174
175 auto parent_name =
176 parent_idx ? descriptors_[*parent_idx].full_name() : package_name;
177 auto full_name =
178 parent_name + "." + base::StringView(decoder.name()).ToStdString();
179
180 auto prev_idx = FindDescriptorIdx(full_name);
181 if (prev_idx.has_value() && !merge_existing_messages) {
182 const auto& existing_descriptor = descriptors_[*prev_idx];
183 return util::ErrStatus("%s: %s was already defined in file %s",
184 file_name.c_str(), full_name.c_str(),
185 existing_descriptor.file_name().c_str());
186 }
187 if (!prev_idx.has_value()) {
188 ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
189 ProtoDescriptor::Type::kEnum,
190 base::nullopt);
191 prev_idx = AddProtoDescriptor(std::move(proto_descriptor));
192 }
193 ProtoDescriptor& proto_descriptor = descriptors_[*prev_idx];
194 if (proto_descriptor.type() != ProtoDescriptor::Type::kEnum) {
195 return util::ErrStatus("%s was message, redefined as enum",
196 full_name.c_str());
197 }
198
199 for (auto it = decoder.value(); it; ++it) {
200 protos::pbzero::EnumValueDescriptorProto::Decoder enum_value(it->data(),
201 it->size());
202 proto_descriptor.AddEnumValue(enum_value.number(),
203 enum_value.name().ToStdString());
204 }
205
206 return util::OkStatus();
207 }
208
AddFromFileDescriptorSet(const uint8_t * file_descriptor_set_proto,size_t size,const std::vector<std::string> & skip_prefixes,bool merge_existing_messages)209 util::Status DescriptorPool::AddFromFileDescriptorSet(
210 const uint8_t* file_descriptor_set_proto,
211 size_t size,
212 const std::vector<std::string>& skip_prefixes,
213 bool merge_existing_messages) {
214 protos::pbzero::FileDescriptorSet::Decoder proto(file_descriptor_set_proto,
215 size);
216 std::vector<ExtensionInfo> extensions;
217 for (auto it = proto.file(); it; ++it) {
218 protos::pbzero::FileDescriptorProto::Decoder file(*it);
219 const std::string file_name = file.name().ToStdString();
220 if (base::StartsWithAny(file_name, skip_prefixes))
221 continue;
222 if (processed_files_.find(file_name) != processed_files_.end()) {
223 // This file has been loaded once already. Skip.
224 continue;
225 }
226 processed_files_.insert(file_name);
227 std::string package = "." + base::StringView(file.package()).ToStdString();
228 for (auto message_it = file.message_type(); message_it; ++message_it) {
229 RETURN_IF_ERROR(AddNestedProtoDescriptors(
230 file_name, package, base::nullopt, *message_it, &extensions,
231 merge_existing_messages));
232 }
233 for (auto enum_it = file.enum_type(); enum_it; ++enum_it) {
234 RETURN_IF_ERROR(AddEnumProtoDescriptors(file_name, package, base::nullopt,
235 *enum_it,
236 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 base::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 base::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,base::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 base::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_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_extension)
334 : name_(std::move(name)),
335 number_(number),
336 type_(type),
337 raw_type_name_(std::move(raw_type_name)),
338 is_repeated_(is_repeated),
339 is_extension_(is_extension) {}
340
341 } // namespace trace_processor
342 } // namespace perfetto
343