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