• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/file_utils.h"
27 #include "perfetto/base/logging.h"
28 #include "perfetto/base/pipe.h"
29 #include "perfetto/base/string_splitter.h"
30 #include "perfetto/base/string_utils.h"
31 
32 namespace perfetto {
33 
34 namespace {
35 
RunClangFmt(const std::string & input)36 std::string RunClangFmt(const std::string& input) {
37   std::string output;
38   pid_t pid;
39   base::Pipe input_pipe = base::Pipe::Create(base::Pipe::kBothNonBlock);
40   base::Pipe output_pipe = base::Pipe::Create(base::Pipe::kBothNonBlock);
41   if ((pid = fork()) == 0) {
42     // Child
43     PERFETTO_CHECK(dup2(*input_pipe.rd, STDIN_FILENO) != -1);
44     PERFETTO_CHECK(dup2(*output_pipe.wr, STDOUT_FILENO) != -1);
45     input_pipe.wr.reset();
46     output_pipe.rd.reset();
47     PERFETTO_CHECK(execl("buildtools/linux64/clang-format", "clang-format",
48                          nullptr) != -1);
49   }
50   PERFETTO_CHECK(pid > 0);
51   // Parent
52   size_t written = 0;
53   size_t bytes_read = 0;
54   input_pipe.rd.reset();
55   output_pipe.wr.reset();
56   // This cannot be left uninitialized because there's as continue statement
57   // before the first assignment to this in the loop.
58   ssize_t r = -1;
59   do {
60     if (written < input.size()) {
61       ssize_t w =
62           write(*input_pipe.wr, &(input[written]), input.size() - written);
63       if (w == -1) {
64         if (errno == EAGAIN || errno == EINTR)
65           continue;
66         PERFETTO_FATAL("write failed");
67       }
68       written += static_cast<size_t>(w);
69       if (written == input.size())
70         input_pipe.wr.reset();
71     }
72 
73     if (bytes_read + base::kPageSize > output.size())
74       output.resize(output.size() + base::kPageSize);
75     r = read(*output_pipe.rd, &(output[bytes_read]), base::kPageSize);
76     if (r == -1) {
77       if (errno == EAGAIN || errno == EINTR)
78         continue;
79       PERFETTO_FATAL("read failed");
80     }
81     if (r > 0)
82       bytes_read += static_cast<size_t>(r);
83   } while (r != 0);
84   output.resize(bytes_read);
85 
86   int wstatus;
87   waitpid(pid, &wstatus, 0);
88   PERFETTO_CHECK(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0);
89   return output;
90 }
91 
92 }  // namespace
93 
94 using base::EndsWith;
95 using base::StartsWith;
96 using base::Contains;
97 
VerifyStream(std::string filename)98 VerifyStream::VerifyStream(std::string filename)
99     : filename_(std::move(filename)) {
100   PERFETTO_CHECK(base::ReadFile(filename_, &expected_));
101 }
102 
~VerifyStream()103 VerifyStream::~VerifyStream() {
104   std::string tidied = str();
105   if (EndsWith(filename_, "cc") || EndsWith(filename_, "proto"))
106     tidied = RunClangFmt(str());
107   if (expected_ != tidied) {
108     PERFETTO_FATAL("%s is out of date. Please run tools/run_ftrace_proto_gen.",
109                    filename_.c_str());
110   }
111 }
112 
FtraceEventName(const std::string & full_name)113 FtraceEventName::FtraceEventName(const std::string& full_name) {
114   if (full_name.rfind("removed", 0) != std::string::npos) {
115     valid_ = false;
116     return;
117   }
118   name_ = full_name.substr(full_name.find('/') + 1, std::string::npos);
119   group_ = full_name.substr(0, full_name.find('/'));
120   valid_ = true;
121 }
122 
valid() const123 bool FtraceEventName::valid() const {
124   return valid_;
125 }
126 
name() const127 const std::string& FtraceEventName::name() const {
128   PERFETTO_CHECK(valid_);
129   return name_;
130 }
131 
group() const132 const std::string& FtraceEventName::group() const {
133   PERFETTO_CHECK(valid_);
134   return group_;
135 }
136 
ToCamelCase(const std::string & s)137 std::string ToCamelCase(const std::string& s) {
138   std::string result;
139   result.reserve(s.size());
140   bool upperCaseNextChar = true;
141   for (size_t i = 0; i < s.size(); i++) {
142     char c = s[i];
143     if (c == '_') {
144       upperCaseNextChar = true;
145       continue;
146     }
147     if (upperCaseNextChar) {
148       upperCaseNextChar = false;
149       c = static_cast<char>(toupper(c));
150     }
151     result.push_back(c);
152   }
153   return result;
154 }
155 
GetSigned() const156 ProtoType ProtoType::GetSigned() const {
157   PERFETTO_CHECK(type == NUMERIC);
158   if (is_signed)
159     return *this;
160 
161   if (size == 64) {
162     return Numeric(64, true);
163   }
164 
165   return Numeric(2 * size, true);
166 }
167 
ToString() const168 std::string ProtoType::ToString() const {
169   switch (type) {
170     case INVALID:
171       PERFETTO_CHECK(false);
172     case STRING:
173       return "string";
174     case NUMERIC: {
175       std::string s;
176       if (!is_signed)
177         s += "u";
178       s += "int";
179       s += std::to_string(size);
180       return s;
181     }
182   }
183   PERFETTO_CHECK(false);  // for GCC.
184 }
185 
186 // static
String()187 ProtoType ProtoType::String() {
188   return {STRING, 0, false};
189 }
190 
191 // static
Invalid()192 ProtoType ProtoType::Invalid() {
193   return {INVALID, 0, false};
194 }
195 
196 // static
Numeric(uint16_t size,bool is_signed)197 ProtoType ProtoType::Numeric(uint16_t size, bool is_signed) {
198   PERFETTO_CHECK(size == 32 || size == 64);
199   return {NUMERIC, size, is_signed};
200 }
201 
202 // static
FromDescriptor(google::protobuf::FieldDescriptor::Type type)203 ProtoType ProtoType::FromDescriptor(
204     google::protobuf::FieldDescriptor::Type type) {
205   if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT64)
206     return Numeric(64, false);
207 
208   if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT64)
209     return Numeric(64, true);
210 
211   if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT32)
212     return Numeric(32, false);
213 
214   if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT32)
215     return Numeric(32, true);
216 
217   if (type == google::protobuf::FieldDescriptor::Type::TYPE_STRING)
218     return String();
219 
220   return Invalid();
221 }
222 
GetCommon(ProtoType one,ProtoType other)223 ProtoType GetCommon(ProtoType one, ProtoType other) {
224   // Always need to prefer the LHS as it is the one already present
225   // in the proto.
226   if (one.type == ProtoType::STRING)
227     return ProtoType::String();
228 
229   if (one.is_signed || other.is_signed) {
230     one = one.GetSigned();
231     other = other.GetSigned();
232   }
233 
234   return ProtoType::Numeric(std::max(one.size, other.size), one.is_signed);
235 }
236 
InferProtoType(const FtraceEvent::Field & field)237 ProtoType InferProtoType(const FtraceEvent::Field& field) {
238   // Fixed length strings: "char foo[16]"
239   if (std::regex_match(field.type_and_name, std::regex(R"(char \w+\[\d+\])")))
240     return ProtoType::String();
241 
242   // String pointers: "__data_loc char[] foo" (as in
243   // 'cpufreq_interactive_boost').
244   if (Contains(field.type_and_name, "char[] "))
245     return ProtoType::String();
246   if (Contains(field.type_and_name, "char * "))
247     return ProtoType::String();
248 
249   // Variable length strings: "char* foo"
250   if (StartsWith(field.type_and_name, "char *"))
251     return ProtoType::String();
252 
253   // Variable length strings: "char foo" + size: 0 (as in 'print').
254   if (StartsWith(field.type_and_name, "char ") && field.size == 0)
255     return ProtoType::String();
256 
257   // ino_t, i_ino and dev_t are 32bit on some devices 64bit on others. For the
258   // protos we need to choose the largest possible size.
259   if (StartsWith(field.type_and_name, "ino_t ") ||
260       StartsWith(field.type_and_name, "i_ino ") ||
261       StartsWith(field.type_and_name, "dev_t ")) {
262     return ProtoType::Numeric(64, /* is_signed= */ false);
263   }
264 
265   // Ints of various sizes:
266   if (field.size <= 4)
267     return ProtoType::Numeric(32, field.is_signed);
268   if (field.size <= 8)
269     return ProtoType::Numeric(64, field.is_signed);
270   return ProtoType::Invalid();
271 }
272 
Proto(std::string evt_name,const google::protobuf::Descriptor & desc)273 Proto::Proto(std::string evt_name, const google::protobuf::Descriptor& desc)
274     : name(desc.name()), event_name(evt_name) {
275   for (int i = 0; i < desc.field_count(); ++i) {
276     const google::protobuf::FieldDescriptor* field = desc.field(i);
277     PERFETTO_CHECK(field);
278     AddField(Field{ProtoType::FromDescriptor(field->type()), field->name(),
279                    uint32_t(field->number())});
280   }
281 }
282 
SortedFields()283 std::vector<const Proto::Field*> Proto::SortedFields() {
284   std::vector<const Proto::Field*> sorted_fields;
285 
286   for (const auto& p : fields) {
287     sorted_fields.emplace_back(&p.second);
288   }
289   std::sort(sorted_fields.begin(), sorted_fields.end(),
290             [](const Proto::Field* a, const Proto::Field* b) {
291               return a->number < b->number;
292             });
293   return sorted_fields;
294 }
295 
ToString()296 std::string Proto::ToString() {
297   std::string s;
298   s += "message " + name + " {\n";
299   for (const auto field : SortedFields()) {
300     s += "  optional " + field->type.ToString() + " " + field->name + " = " +
301          std::to_string(field->number) + ";\n";
302   }
303   s += "}\n";
304   return s;
305 }
306 
MergeFrom(const Proto & other)307 void Proto::MergeFrom(const Proto& other) {
308   // Always keep number from the left hand side.
309   PERFETTO_CHECK(name == other.name);
310   for (const auto& p : other.fields) {
311     auto it = fields.find(p.first);
312     if (it == fields.end()) {
313       Proto::Field field = p.second;
314       field.number = ++max_id;
315       AddField(std::move(field));
316     } else {
317       it->second.type = GetCommon(it->second.type, p.second.type);
318     }
319   }
320 }
321 
AddField(Proto::Field other)322 void Proto::AddField(Proto::Field other) {
323   max_id = std::max(max_id, other.number);
324   fields.emplace(other.name, std::move(other));
325 }
326 
327 }  // namespace perfetto
328