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