• 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/ftrace_proto_gen.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 using base::StartsWith;
35 using base::Contains;
36 
EventNameToProtoFieldName(const std::string & name)37 std::string EventNameToProtoFieldName(const std::string& name) {
38   return (name == "0") ? "zero" : name;
39 }
40 
EventNameToProtoName(const std::string & name)41 std::string EventNameToProtoName(const std::string& name) {
42   return ToCamelCase(EventNameToProtoFieldName(name)) + "FtraceEvent";
43 }
44 
ReadWhitelist(const std::string & filename)45 std::vector<FtraceEventName> ReadWhitelist(const std::string& filename) {
46   std::string line;
47   std::vector<FtraceEventName> lines;
48 
49   std::ifstream fin(filename, std::ios::in);
50   if (!fin) {
51     fprintf(stderr, "failed to open whitelist %s\n", filename.c_str());
52     return lines;
53   }
54   while (std::getline(fin, line)) {
55     if (!StartsWith(line, "#"))
56       lines.emplace_back(FtraceEventName(line));
57   }
58   return lines;
59 }
60 
PrintEventFormatterMain(const std::set<std::string> & events)61 void PrintEventFormatterMain(const std::set<std::string>& events) {
62   printf(
63       "\nAdd output to FormatEventText in "
64       "tools/trace_to_text/ftrace_event_formatter.cc\n");
65   for (auto event : events) {
66     printf(
67         "else if (event.has_%s()) {\nconst auto& inner = event.%s();\nreturn "
68         "Format%s(inner);\n} ",
69         event.c_str(), event.c_str(), ToCamelCase(event).c_str());
70   }
71 }
72 
73 // Add output to ParseInode in ftrace_inode_handler
PrintInodeHandlerMain(const std::string & event_name,const perfetto::Proto & proto)74 void PrintInodeHandlerMain(const std::string& event_name,
75                            const perfetto::Proto& proto) {
76   for (const auto& p : proto.fields) {
77     const Proto::Field& field = p.second;
78     if (Contains(field.name, "ino") && !Contains(field.name, "minor"))
79       printf(
80           "else if (event.has_%s() && event.%s().%s()) {\n*inode = "
81           "static_cast<uint64_t>(event.%s().%s());\n return true;\n} ",
82           event_name.c_str(), event_name.c_str(), field.name.c_str(),
83           event_name.c_str(), field.name.c_str());
84   }
85 }
86 
PrintEventFormatterUsingStatements(const std::set<std::string> & events)87 void PrintEventFormatterUsingStatements(const std::set<std::string>& events) {
88   printf("\nAdd output to tools/trace_to_text/ftrace_event_formatter.cc\n");
89   for (auto event : events) {
90     printf("using protos::%sFtraceEvent;\n", ToCamelCase(event).c_str());
91   }
92 }
93 
PrintEventFormatterFunctions(const std::set<std::string> & events)94 void PrintEventFormatterFunctions(const std::set<std::string>& events) {
95   printf(
96       "\nAdd output to tools/trace_to_text/ftrace_event_formatter.cc and "
97       "then manually go through format files to match fields\n");
98   for (auto event : events) {
99     printf(
100         "std::string Format%s(const %sFtraceEvent& event) {"
101         "\nchar line[2048];"
102         "\nsprintf(line,\"%s: );\nreturn std::string(line);\n}\n",
103         ToCamelCase(event).c_str(), ToCamelCase(event).c_str(), event.c_str());
104   }
105 }
106 
GenerateProto(const FtraceEvent & format,Proto * proto_out)107 bool GenerateProto(const FtraceEvent& format, Proto* proto_out) {
108   proto_out->name = EventNameToProtoName(format.name);
109   proto_out->event_name = format.name;
110   std::set<std::string> seen;
111   // TODO(hjd): We should be cleverer about id assignment.
112   uint32_t i = 1;
113   for (const FtraceEvent::Field& field : format.fields) {
114     std::string name = GetNameFromTypeAndName(field.type_and_name);
115     // TODO(hjd): Handle dup names.
116     // sa_handler is problematic because glib headers redefine it at the
117     // preprocessor level. It's impossible to have a variable or a function
118     // called sa_handler. On the good side, we realistically don't care about
119     // this field, it's just easier to skip it.
120     if (name == "" || seen.count(name) || name == "sa_handler" ||
121         name == "errno")
122       continue;
123     seen.insert(name);
124     ProtoType type = InferProtoType(field);
125     // Check we managed to infer a type.
126     if (type.type == ProtoType::INVALID)
127       continue;
128     Proto::Field protofield{std::move(type), name, i};
129     proto_out->AddField(std::move(protofield));
130     i++;
131   }
132 
133   return true;
134 }
135 
GenerateFtraceEventProto(const std::vector<FtraceEventName> & raw_whitelist,const std::set<std::string> & groups,std::ostream * fout)136 void GenerateFtraceEventProto(const std::vector<FtraceEventName>& raw_whitelist,
137                               const std::set<std::string>& groups,
138                               std::ostream* fout) {
139   *fout << "// Autogenerated by:\n";
140   *fout << std::string("// ") + __FILE__ + "\n";
141   *fout << "// Do not edit.\n\n";
142   *fout << R"(syntax = "proto2";)"
143         << "\n";
144   *fout << "option optimize_for = LITE_RUNTIME;\n\n";
145 
146   for (const std::string& group : groups) {
147     *fout << R"(import "perfetto/trace/ftrace/)" << group << R"(.proto";)"
148           << "\n";
149   }
150   *fout << "import \"perfetto/trace/ftrace/generic.proto\";\n";
151   *fout << "\n";
152   *fout << "package perfetto.protos;\n\n";
153   *fout << R"(message FtraceEvent {
154   // Nanoseconds since an epoch.
155   // Epoch is configurable by writing into trace_clock.
156   // By default this timestamp is CPU local.
157   // TODO: Figure out a story for reconciling the various clocks.
158   optional uint64 timestamp = 1;
159 
160   // Kernel pid (do not confuse with userspace pid aka tgid)
161   optional uint32 pid = 2;
162 
163   oneof event {
164 )";
165 
166   int i = 3;
167   for (const FtraceEventName& event : raw_whitelist) {
168     if (!event.valid()) {
169       *fout << "    // removed field with id " << i << ";\n";
170       ++i;
171       continue;
172     }
173 
174     std::string field_name = EventNameToProtoFieldName(event.name());
175     std::string type_name = EventNameToProtoName(event.name());
176 
177     // "    " (indent) + TypeName + " " + field_name + " = " + 123 + ";"
178     if (4 + type_name.size() + 1 + field_name.size() + 3 + 3 + 1 <= 80) {
179       // Everything fits in one line:
180       *fout << "    " << type_name << " " << field_name << " = " << i << ";\n";
181     } else if (4 + type_name.size() + 1 + field_name.size() + 2 <= 80) {
182       // Everything fits except the field id:
183       *fout << "    " << type_name << " " << field_name << " =\n        " << i
184             << ";\n";
185     } else {
186       // Nothing fits:
187       *fout << "    " << type_name << "\n        " << field_name << " = " << i
188             << ";\n";
189     }
190     ++i;
191     // We cannot depend on the proto file to get this number because
192     // it would cause a dependency cycle between this generator and the
193     // generated code.
194     if (i == 327) {
195       *fout << "    GenericFtraceEvent generic = " << i << ";\n";
196       ++i;
197     }
198   }
199   *fout << "  }\n";
200   *fout << "}\n";
201 }
202 
203 // Generates section of event_info.cc for a single event.
SingleEventInfo(perfetto::Proto proto,const std::string & group,const uint32_t proto_field_id)204 std::string SingleEventInfo(perfetto::Proto proto,
205                             const std::string& group,
206                             const uint32_t proto_field_id) {
207   std::string s = "";
208   s += "    event->name = \"" + proto.event_name + "\";\n";
209   s += "    event->group = \"" + group + "\";\n";
210   s += "    event->proto_field_id = " + std::to_string(proto_field_id) + ";\n";
211 
212   for (const auto& field : proto.SortedFields()) {
213     s += "    event->fields.push_back(MakeField(\"" + field->name + "\", " +
214          std::to_string(field->number) + ", ProtoSchemaType::k" +
215          ToCamelCase(field->type.ToString()) + "));\n";
216   }
217   return s;
218 }
219 
220 // This will generate the event_info.cc file for the whitelisted protos.
GenerateEventInfo(const std::vector<std::string> & events_info,std::ostream * fout)221 void GenerateEventInfo(const std::vector<std::string>& events_info,
222                        std::ostream* fout) {
223   std::string s = "// Autogenerated by:\n";
224   s += std::string("// ") + __FILE__ + "\n";
225   s += "// Do not edit.\n";
226   s += R"(
227 #include "perfetto/protozero/proto_utils.h"
228 #include "src/traced/probes/ftrace/event_info.h"
229 
230 namespace perfetto {
231 
232 using protozero::proto_utils::ProtoSchemaType;
233 
234 std::vector<Event> GetStaticEventInfo() {
235   std::vector<Event> events;
236 )";
237 
238   for (const auto& event : events_info) {
239     s += "\n";
240     s += "  {\n";
241     s += "    events.emplace_back(Event{});\n";
242     s += "    Event* event = &events.back();\n";
243     s += event;
244     s += "  }\n";
245   }
246 
247   s += R"(
248   return events;
249 }
250 
251 }  // namespace perfetto
252 )";
253 
254   *fout << s;
255 }
256 
ProtoHeader()257 std::string ProtoHeader() {
258   std::string s = "// Autogenerated by:\n";
259   s += std::string("// ") + __FILE__ + "\n";
260   s += "// Do not edit.\n";
261 
262   s += R"(
263 syntax = "proto2";
264 option optimize_for = LITE_RUNTIME;
265 package perfetto.protos;
266 
267 )";
268   return s;
269 }
270 
271 }  // namespace perfetto
272