1 /*
2  * Copyright (C) 2017 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 <getopt.h>
18 #include <sys/stat.h>
19 #include <fstream>
20 #include <map>
21 #include <memory>
22 #include <regex>
23 #include <set>
24 #include <sstream>
25 #include <string>
26 
27 #include <google/protobuf/descriptor.h>
28 #include <google/protobuf/descriptor.pb.h>
29 
30 #include "perfetto/base/logging.h"
31 #include "perfetto/ext/base/file_utils.h"
32 #include "src/traced/probes/ftrace/format_parser.h"
33 #include "tools/ftrace_proto_gen/ftrace_descriptor_gen.h"
34 #include "tools/ftrace_proto_gen/ftrace_proto_gen.h"
35 
36 namespace {
MakeOFStream(const std::string & filename)37 inline std::unique_ptr<std::ostream> MakeOFStream(const std::string& filename) {
38   return std::unique_ptr<std::ostream>(new std::ofstream(filename));
39 }
40 
MakeVerifyStream(const std::string & filename)41 inline std::unique_ptr<std::ostream> MakeVerifyStream(
42     const std::string& filename) {
43   return std::unique_ptr<std::ostream>(new perfetto::VerifyStream(filename));
44 }
45 
PrintUsage(const char * bin_name)46 void PrintUsage(const char* bin_name) {
47   fprintf(stderr,
48           "Usage: %s -w whitelist_dir -o output_dir -d proto_descriptor "
49           "[--check_only] input_dir...\n",
50           bin_name);
51 }
52 }  // namespace
53 
main(int argc,char ** argv)54 int main(int argc, char** argv) {
55   static struct option long_options[] = {
56       {"whitelist_path", required_argument, nullptr, 'w'},
57       {"output_dir", required_argument, nullptr, 'o'},
58       {"proto_descriptor", required_argument, nullptr, 'd'},
59       {"update_build_files", no_argument, nullptr, 'b'},
60       {"check_only", no_argument, nullptr, 'c'},
61       {nullptr, 0, nullptr, 0}};
62 
63   int option_index;
64   int c;
65 
66   std::string whitelist_path;
67   std::string output_dir;
68   std::string proto_descriptor;
69   bool update_build_files = false;
70 
71   std::unique_ptr<std::ostream> (*ostream_factory)(const std::string&) =
72       &MakeOFStream;
73 
74   while ((c = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
75     switch (c) {
76       case 'w':
77         whitelist_path = optarg;
78         break;
79       case 'o':
80         output_dir = optarg;
81         break;
82       case 'd':
83         proto_descriptor = optarg;
84         break;
85       case 'b':
86         update_build_files = true;
87         break;
88       case 'c':
89         ostream_factory = &MakeVerifyStream;
90         break;
91       default: {
92         PrintUsage(argv[0]);
93         return 1;
94       }
95     }
96   }
97 
98   if (optind >= argc) {
99     PrintUsage(argv[0]);
100     return 1;
101   }
102 
103   PERFETTO_CHECK(!whitelist_path.empty());
104   PERFETTO_CHECK(!output_dir.empty());
105   PERFETTO_CHECK(!proto_descriptor.empty());
106 
107   std::vector<perfetto::FtraceEventName> whitelist =
108       perfetto::ReadWhitelist(whitelist_path);
109   std::vector<std::string> events_info;
110 
111   google::protobuf::DescriptorPool descriptor_pool;
112   descriptor_pool.AllowUnknownDependencies();
113   {
114     google::protobuf::FileDescriptorSet file_descriptor_set;
115     std::string descriptor_bytes;
116     if (!perfetto::base::ReadFile(proto_descriptor, &descriptor_bytes)) {
117       fprintf(stderr, "Failed to open %s\n", proto_descriptor.c_str());
118       return 1;
119     }
120     file_descriptor_set.ParseFromString(descriptor_bytes);
121 
122     for (const auto& d : file_descriptor_set.file()) {
123       PERFETTO_CHECK(descriptor_pool.BuildFile(d));
124     }
125   }
126 
127   std::set<std::string> groups;
128   std::multimap<std::string, const perfetto::FtraceEventName*> group_to_event;
129   std::set<std::string> new_events;
130   for (const auto& event : whitelist) {
131     if (!event.valid())
132       continue;
133     groups.emplace(event.group());
134     group_to_event.emplace(event.group(), &event);
135     struct stat buf;
136     if (stat(
137             ("protos/perfetto/trace/ftrace/" + event.name() + ".proto").c_str(),
138             &buf) == -1) {
139       new_events.insert(event.name());
140     }
141   }
142 
143   {
144     std::unique_ptr<std::ostream> out =
145         ostream_factory(output_dir + "/ftrace_event.proto");
146     perfetto::GenerateFtraceEventProto(whitelist, groups, out.get());
147   }
148 
149   for (const std::string& group : groups) {
150     std::string proto_file_name = group + ".proto";
151     std::string output_path = output_dir + std::string("/") + proto_file_name;
152     std::unique_ptr<std::ostream> fout = ostream_factory(output_path);
153     if (!fout) {
154       fprintf(stderr, "Failed to open %s\n", output_path.c_str());
155       return 1;
156     }
157     *fout << perfetto::ProtoHeader();
158 
159     auto range = group_to_event.equal_range(group);
160     for (auto it = range.first; it != range.second; ++it) {
161       const auto& event = *it->second;
162       if (!event.valid())
163         continue;
164 
165       std::string proto_name =
166           perfetto::EventNameToProtoName(group, event.name());
167       perfetto::Proto proto;
168       proto.name = proto_name;
169       proto.event_name = event.name();
170       const google::protobuf::Descriptor* d =
171           descriptor_pool.FindMessageTypeByName("perfetto.protos." +
172                                                 proto_name);
173       if (d)
174         proto = perfetto::Proto(event.name(), *d);
175       else
176         PERFETTO_LOG("Did not find %s", proto_name.c_str());
177       for (int i = optind; i < argc; ++i) {
178         std::string input_dir = argv[i];
179         std::string input_path = input_dir + event.group() + "/" +
180                                  event.name() + std::string("/format");
181 
182         std::string contents;
183         if (!perfetto::base::ReadFile(input_path, &contents)) {
184           continue;
185         }
186 
187         perfetto::FtraceEvent format;
188         if (!perfetto::ParseFtraceEvent(contents, &format)) {
189           fprintf(stderr, "Could not parse file %s.\n", input_path.c_str());
190           return 1;
191         }
192 
193         perfetto::Proto event_proto;
194         if (!perfetto::GenerateProto(group, format, &event_proto)) {
195           fprintf(stderr, "Could not generate proto for file %s\n",
196                   input_path.c_str());
197           return 1;
198         }
199         proto.MergeFrom(event_proto);
200       }
201 
202       uint32_t i = 0;
203       for (; it->second != &whitelist[i]; i++)
204         ;
205 
206       // The first id used for events in FtraceEvent proto is 3.
207       uint32_t proto_field = i + 3;
208 
209       // The generic event has field id 327 so any event with a id higher
210       // than that has to be incremented by 1.
211       if (proto_field >= 327)
212         proto_field++;
213 
214       events_info.push_back(
215           perfetto::SingleEventInfo(proto, event.group(), proto_field));
216 
217       *fout << proto.ToString();
218       PERFETTO_CHECK(!fout->fail());
219     }
220   }
221 
222   {
223     std::unique_ptr<std::ostream> out = ostream_factory(
224         "src/trace_processor/importers/ftrace/ftrace_descriptors.cc");
225     perfetto::GenerateFtraceDescriptors(descriptor_pool, out.get());
226     PERFETTO_CHECK(!out->fail());
227   }
228 
229   {
230     std::unique_ptr<std::ostream> out =
231         ostream_factory("src/traced/probes/ftrace/event_info.cc");
232     perfetto::GenerateEventInfo(events_info, out.get());
233     PERFETTO_CHECK(!out->fail());
234   }
235 
236   if (update_build_files) {
237     std::unique_ptr<std::ostream> f =
238         ostream_factory(output_dir + "/all_protos.gni");
239 
240     *f << R"(# Copyright (C) 2018 The Android Open Source Project
241 #
242 # Licensed under the Apache License, Version 2.0 (the "License");
243 # you may not use this file except in compliance with the License.
244 # You may obtain a copy of the License at
245 #
246 #      http://www.apache.org/licenses/LICENSE-2.0
247 #
248 # Unless required by applicable law or agreed to in writing, software
249 # distributed under the License is distributed on an "AS IS" BASIS,
250 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
251 # See the License for the specific language governing permissions and
252 # limitations under the License.
253 
254 # Autogenerated by ftrace_proto_gen.
255 
256 ftrace_proto_names = [
257   "ftrace_event.proto",
258   "ftrace_event_bundle.proto",
259   "ftrace_stats.proto",
260   "test_bundle_wrapper.proto",
261   "generic.proto",
262 )";
263     for (const std::string& group : groups) {
264       *f << "  \"" << group << ".proto\",\n";
265     }
266     *f << "]\n";
267     PERFETTO_CHECK(!f->fail());
268   }
269 }
270