1 /*
2 * Copyright (C) 2018 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 "tools/ftrace_proto_gen/proto_gen_utils.h"
18
19 #include <fcntl.h>
20 #include <sys/wait.h>
21 #include <unistd.h>
22 #include <algorithm>
23 #include <fstream>
24 #include <regex>
25
26 #include "perfetto/base/logging.h"
27 #include "perfetto/ext/base/file_utils.h"
28 #include "perfetto/ext/base/pipe.h"
29 #include "perfetto/ext/base/string_splitter.h"
30 #include "perfetto/ext/base/string_utils.h"
31 #include "perfetto/ext/base/subprocess.h"
32
33 namespace perfetto {
34
35 namespace {
36
RunClangFmt(const std::string & input)37 std::string RunClangFmt(const std::string& input) {
38 #if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
39 const std::string platform = "mac";
40 #else
41 const std::string platform = "linux64";
42 #endif
43 base::Subprocess clang_fmt({"buildtools/" + platform + "/clang-format"});
44 clang_fmt.args.stdout_mode = base::Subprocess::kBuffer;
45 clang_fmt.args.stderr_mode = base::Subprocess::kInherit;
46 clang_fmt.args.input = input;
47 PERFETTO_CHECK(clang_fmt.Call());
48 return std::move(clang_fmt.output());
49 }
50
51 } // namespace
52
53 using base::Contains;
54 using base::EndsWith;
55 using base::StartsWith;
56 using base::Uppercase;
57
VerifyStream(std::string filename)58 VerifyStream::VerifyStream(std::string filename)
59 : filename_(std::move(filename)) {
60 PERFETTO_CHECK(base::ReadFile(filename_, &expected_));
61 }
62
~VerifyStream()63 VerifyStream::~VerifyStream() {
64 std::string tidied = str();
65 if (EndsWith(filename_, "cc") || EndsWith(filename_, "proto"))
66 tidied = RunClangFmt(str());
67 if (expected_ != tidied) {
68 PERFETTO_FATAL("%s is out of date. Please run tools/run_ftrace_proto_gen.",
69 filename_.c_str());
70 }
71 }
72
FtraceEventName(const std::string & full_name)73 FtraceEventName::FtraceEventName(const std::string& full_name) {
74 if (full_name.rfind("removed", 0) != std::string::npos) {
75 valid_ = false;
76 return;
77 }
78 name_ = full_name.substr(full_name.find('/') + 1, std::string::npos);
79 group_ = full_name.substr(0, full_name.find('/'));
80 valid_ = true;
81 }
82
valid() const83 bool FtraceEventName::valid() const {
84 return valid_;
85 }
86
name() const87 const std::string& FtraceEventName::name() const {
88 PERFETTO_CHECK(valid_);
89 return name_;
90 }
91
group() const92 const std::string& FtraceEventName::group() const {
93 PERFETTO_CHECK(valid_);
94 return group_;
95 }
96
ToCamelCase(const std::string & s)97 std::string ToCamelCase(const std::string& s) {
98 std::string result;
99 result.reserve(s.size());
100 bool upperCaseNextChar = true;
101 for (size_t i = 0; i < s.size(); i++) {
102 char c = s[i];
103 if (c == '_') {
104 upperCaseNextChar = true;
105 continue;
106 }
107 if (upperCaseNextChar) {
108 upperCaseNextChar = false;
109 c = Uppercase(c);
110 }
111 result.push_back(c);
112 }
113 return result;
114 }
115
GetSigned() const116 ProtoType ProtoType::GetSigned() const {
117 PERFETTO_CHECK(type == NUMERIC);
118 if (is_signed)
119 return *this;
120
121 if (size == 64) {
122 return Numeric(64, true);
123 }
124
125 return Numeric(2 * size, true);
126 }
127
ToString() const128 std::string ProtoType::ToString() const {
129 switch (type) {
130 case INVALID:
131 PERFETTO_CHECK(false);
132 case STRING:
133 return "string";
134 case NUMERIC: {
135 std::string s;
136 if (!is_signed)
137 s += "u";
138 s += "int";
139 s += std::to_string(size);
140 return s;
141 }
142 }
143 PERFETTO_CHECK(false); // for GCC.
144 }
145
146 // static
String()147 ProtoType ProtoType::String() {
148 return {STRING, 0, false};
149 }
150
151 // static
Invalid()152 ProtoType ProtoType::Invalid() {
153 return {INVALID, 0, false};
154 }
155
156 // static
Numeric(uint16_t size,bool is_signed)157 ProtoType ProtoType::Numeric(uint16_t size, bool is_signed) {
158 PERFETTO_CHECK(size == 32 || size == 64);
159 return {NUMERIC, size, is_signed};
160 }
161
162 // static
FromDescriptor(google::protobuf::FieldDescriptor::Type type)163 ProtoType ProtoType::FromDescriptor(
164 google::protobuf::FieldDescriptor::Type type) {
165 if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT64)
166 return Numeric(64, false);
167
168 if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT64)
169 return Numeric(64, true);
170
171 if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT32)
172 return Numeric(32, false);
173
174 if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT32)
175 return Numeric(32, true);
176
177 if (type == google::protobuf::FieldDescriptor::Type::TYPE_STRING)
178 return String();
179
180 return Invalid();
181 }
182
GetCommon(ProtoType one,ProtoType other)183 ProtoType GetCommon(ProtoType one, ProtoType other) {
184 // Always need to prefer the LHS as it is the one already present
185 // in the proto.
186 if (one.type == ProtoType::STRING)
187 return ProtoType::String();
188
189 if (one.is_signed || other.is_signed) {
190 one = one.GetSigned();
191 other = other.GetSigned();
192 }
193
194 return ProtoType::Numeric(std::max(one.size, other.size), one.is_signed);
195 }
196
InferProtoType(const FtraceEvent::Field & field)197 ProtoType InferProtoType(const FtraceEvent::Field& field) {
198 // Fixed length strings: "char foo[16]"
199 if (std::regex_match(field.type_and_name, std::regex(R"(char \w+\[\d+\])")))
200 return ProtoType::String();
201
202 // String pointers: "__data_loc char[] foo" (as in
203 // 'cpufreq_interactive_boost').
204 if (Contains(field.type_and_name, "char[] "))
205 return ProtoType::String();
206 if (Contains(field.type_and_name, "char * "))
207 return ProtoType::String();
208
209 // Variable length strings: "char* foo"
210 if (StartsWith(field.type_and_name, "char *"))
211 return ProtoType::String();
212
213 // Variable length strings: "char foo" + size: 0 (as in 'print').
214 if (StartsWith(field.type_and_name, "char ") && field.size == 0)
215 return ProtoType::String();
216
217 // ino_t, i_ino and dev_t are 32bit on some devices 64bit on others. For the
218 // protos we need to choose the largest possible size.
219 if (StartsWith(field.type_and_name, "ino_t ") ||
220 StartsWith(field.type_and_name, "i_ino ") ||
221 StartsWith(field.type_and_name, "dev_t ")) {
222 return ProtoType::Numeric(64, /* is_signed= */ false);
223 }
224
225 // Ints of various sizes:
226 if (field.size <= 4)
227 return ProtoType::Numeric(32, field.is_signed);
228 if (field.size <= 8)
229 return ProtoType::Numeric(64, field.is_signed);
230 return ProtoType::Invalid();
231 }
232
Proto(std::string evt_name,const google::protobuf::Descriptor & desc)233 Proto::Proto(std::string evt_name, const google::protobuf::Descriptor& desc)
234 : name(desc.name()), event_name(evt_name) {
235 for (int i = 0; i < desc.field_count(); ++i) {
236 const google::protobuf::FieldDescriptor* field = desc.field(i);
237 PERFETTO_CHECK(field);
238 AddField(Field{ProtoType::FromDescriptor(field->type()), field->name(),
239 uint32_t(field->number())});
240 }
241 }
242
SortedFields()243 std::vector<const Proto::Field*> Proto::SortedFields() {
244 std::vector<const Proto::Field*> sorted_fields;
245
246 for (const auto& p : fields) {
247 sorted_fields.emplace_back(&p.second);
248 }
249 std::sort(sorted_fields.begin(), sorted_fields.end(),
250 [](const Proto::Field* a, const Proto::Field* b) {
251 return a->number < b->number;
252 });
253 return sorted_fields;
254 }
255
ToString()256 std::string Proto::ToString() {
257 std::string s;
258 s += "message " + name + " {\n";
259 for (const auto field : SortedFields()) {
260 s += " optional " + field->type.ToString() + " " + field->name + " = " +
261 std::to_string(field->number) + ";\n";
262 }
263 s += "}\n";
264 return s;
265 }
266
MergeFrom(const Proto & other)267 void Proto::MergeFrom(const Proto& other) {
268 // Always keep number from the left hand side.
269 PERFETTO_CHECK(name == other.name);
270 for (const auto& p : other.fields) {
271 auto it = fields.find(p.first);
272 if (it == fields.end()) {
273 Proto::Field field = p.second;
274 field.number = ++max_id;
275 AddField(std::move(field));
276 } else {
277 it->second.type = GetCommon(it->second.type, p.second.type);
278 }
279 }
280 }
281
AddField(Proto::Field other)282 void Proto::AddField(Proto::Field other) {
283 max_id = std::max(max_id, other.number);
284 fields.emplace(other.name, std::move(other));
285 }
286
287 } // namespace perfetto
288