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 <functional>
18 #include <memory>
19 #include <set>
20 #include <string>
21 
22 #include <google/protobuf/compiler/code_generator.h>
23 #include <google/protobuf/compiler/plugin.h>
24 #include <google/protobuf/descriptor.h>
25 #include <google/protobuf/descriptor.pb.h>
26 #include <google/protobuf/io/printer.h>
27 #include <google/protobuf/io/zero_copy_stream.h>
28 
29 #include "perfetto/ext/base/string_utils.h"
30 
31 namespace perfetto {
32 namespace ipc {
33 namespace {
34 
35 using google::protobuf::FileDescriptor;
36 using google::protobuf::MethodDescriptor;
37 using google::protobuf::ServiceDescriptor;
38 using google::protobuf::compiler::GeneratorContext;
39 using google::protobuf::io::Printer;
40 using google::protobuf::io::ZeroCopyOutputStream;
41 using perfetto::base::SplitString;
42 using perfetto::base::StripChars;
43 using perfetto::base::StripSuffix;
44 using perfetto::base::ToUpper;
45 
46 static const char kBanner[] = "// DO NOT EDIT. Autogenerated by Perfetto IPC\n";
47 
48 static const char kHeaderSvcClass[] = R"(
49 class $c$ : public ::perfetto::ipc::Service {
50  private:
51   static ::perfetto::ipc::ServiceDescriptor* NewDescriptor();
52 
53  public:
54   ~$c$() override;
55 
56   static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic();
57 
58   // Service implementation.
59   const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
60 
61   // Methods from the .proto file
62 )";
63 
64 static const char kHeaderProxyClass[] = R"(
65 class $c$Proxy : public ::perfetto::ipc::ServiceProxy {
66  public:
67    explicit $c$Proxy(::perfetto::ipc::ServiceProxy::EventListener*);
68    ~$c$Proxy() override;
69 
70   // ServiceProxy implementation.
71   const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
72 
73   // Methods from the .proto file
74 )";
75 
76 static const char kCppClassDefinitions[] = R"(
77 const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptorStatic() {
78   static auto* instance = NewDescriptor();
79   return *instance;
80 }
81 
82 // Host-side definitions.
83 $c$::~$c$() = default;
84 
85 const ::perfetto::ipc::ServiceDescriptor& $c$::GetDescriptor() {
86   return GetDescriptorStatic();
87 }
88 
89 // Client-side definitions.
90 $c$Proxy::$c$Proxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener)
91     : ::perfetto::ipc::ServiceProxy(event_listener) {}
92 
93 $c$Proxy::~$c$Proxy() = default;
94 
95 const ::perfetto::ipc::ServiceDescriptor& $c$Proxy::GetDescriptor() {
96   return $c$::GetDescriptorStatic();
97 }
98 )";
99 
100 static const char kCppMethodDescriptor[] = R"(
101   desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
102      "$m$",
103      &_IPC_Decoder<$i$>,
104      &_IPC_Decoder<$o$>,
105      &_IPC_Invoker<$c$, $i$, $o$, &$c$::$m$>});
106 )";
107 
108 static const char kCppMethod[] = R"(
109 void $c$Proxy::$m$(const $i$& request, Deferred$o$ reply, int fd) {
110   BeginInvoke("$m$", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
111               fd);
112 }
113 )";
114 
StripName(const FileDescriptor & file)115 std::string StripName(const FileDescriptor& file) {
116   return StripSuffix(file.name(), ".proto");
117 }
118 
GetStubName(const FileDescriptor & file)119 std::string GetStubName(const FileDescriptor& file) {
120   return StripName(file) + ".ipc";
121 }
122 
ForEachMethod(const ServiceDescriptor & svc,std::function<void (const MethodDescriptor &,const std::string &,const std::string &)> function)123 void ForEachMethod(const ServiceDescriptor& svc,
124                    std::function<void(const MethodDescriptor&,
125                                       const std::string&,
126                                       const std::string&)> function) {
127   for (int i = 0; i < svc.method_count(); i++) {
128     const MethodDescriptor& method = *svc.method(i);
129     // TODO if the input or output type are in a different namespace we need to
130     // emit the ::fully::qualified::name.
131     std::string input_type = method.input_type()->name();
132     std::string output_type = method.output_type()->name();
133     function(method, input_type, output_type);
134   }
135 }
136 
137 class IPCGenerator : public ::google::protobuf::compiler::CodeGenerator {
138  public:
139   explicit IPCGenerator();
140   ~IPCGenerator() override;
141 
142   // CodeGenerator implementation
143   bool Generate(const google::protobuf::FileDescriptor* file,
144                 const std::string& options,
145                 google::protobuf::compiler::GeneratorContext* context,
146                 std::string* error) const override;
147 
148   void GenerateServiceCpp(const FileDescriptor& file,
149                           const ServiceDescriptor& svc,
150                           Printer* printer) const;
151   void GenerateServiceHeader(const FileDescriptor& file,
152                              const ServiceDescriptor& svc,
153                              Printer* printer) const;
154 
GetNamespaces(const FileDescriptor & file) const155   std::vector<std::string> GetNamespaces(const FileDescriptor& file) const {
156     std::string pkg = file.package() + wrapper_namespace_;
157     return SplitString(pkg, ".");
158   }
159 
160   mutable std::string wrapper_namespace_;
161 };
162 
163 IPCGenerator::IPCGenerator() = default;
164 IPCGenerator::~IPCGenerator() = default;
165 
GenerateServiceHeader(const FileDescriptor & file,const ServiceDescriptor & svc,Printer * printer) const166 void IPCGenerator::GenerateServiceHeader(const FileDescriptor& file,
167                                          const ServiceDescriptor& svc,
168                                          Printer* printer) const {
169   printer->Print("\n");
170   for (const std::string& ns : GetNamespaces(file))
171     printer->Print("namespace $ns$ {\n", "ns", ns);
172 
173   // Generate the host-side declarations.
174   printer->Print(kHeaderSvcClass, "c", svc.name());
175   std::set<std::string> types_seen;
176   ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method,
177                                             const std::string& input_type,
178                                             const std::string& output_type) {
179     if (types_seen.count(output_type) == 0) {
180       printer->Print("  using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n",
181                      "o", output_type);
182       types_seen.insert(output_type);
183     }
184     printer->Print("  virtual void $m$(const $i$&, Deferred$o$) = 0;\n\n", "m",
185                    method.name(), "i", input_type, "o", output_type);
186   });
187   printer->Print("};\n\n");
188 
189   // Generate the client-side declarations.
190   printer->Print(kHeaderProxyClass, "c", svc.name());
191   types_seen.clear();
192   ForEachMethod(svc, [&types_seen, printer](const MethodDescriptor& method,
193                                             const std::string& input_type,
194                                             const std::string& output_type) {
195     if (types_seen.count(output_type) == 0) {
196       printer->Print("  using Deferred$o$ = ::perfetto::ipc::Deferred<$o$>;\n",
197                      "o", output_type);
198       types_seen.insert(output_type);
199     }
200     printer->Print("  void $m$(const $i$&, Deferred$o$, int fd = -1);\n\n", "m",
201                    method.name(), "i", input_type, "o", output_type);
202   });
203   printer->Print("};\n\n");
204 
205   for (const std::string& ns : GetNamespaces(file))
206     printer->Print("}  // namespace $ns$\n", "ns", ns);
207 
208   printer->Print("\n");
209 }
210 
GenerateServiceCpp(const FileDescriptor & file,const ServiceDescriptor & svc,Printer * printer) const211 void IPCGenerator::GenerateServiceCpp(const FileDescriptor& file,
212                                       const ServiceDescriptor& svc,
213                                       Printer* printer) const {
214   printer->Print("\n");
215   for (const std::string& ns : GetNamespaces(file))
216     printer->Print("namespace $ns$ {\n", "ns", ns);
217 
218   printer->Print("::perfetto::ipc::ServiceDescriptor* $c$::NewDescriptor() {\n",
219                  "c", svc.name());
220   printer->Print("  auto* desc = new ::perfetto::ipc::ServiceDescriptor();\n");
221   printer->Print("  desc->service_name = \"$c$\";\n", "c", svc.name());
222 
223   ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method,
224                                      const std::string& input_type,
225                                      const std::string& output_type) {
226     printer->Print(kCppMethodDescriptor, "c", svc.name(), "i", input_type, "o",
227                    output_type, "m", method.name());
228   });
229 
230   printer->Print("  desc->methods.shrink_to_fit();\n");
231   printer->Print("  return desc;\n");
232   printer->Print("}\n\n");
233 
234   printer->Print(kCppClassDefinitions, "c", svc.name());
235 
236   ForEachMethod(svc, [&svc, printer](const MethodDescriptor& method,
237                                      const std::string& input_type,
238                                      const std::string& output_type) {
239     printer->Print(kCppMethod, "c", svc.name(), "m", method.name(), "i",
240                    input_type, "o", output_type);
241   });
242 
243   for (const std::string& ns : GetNamespaces(file))
244     printer->Print("}  // namespace $ns$\n", "ns", ns);
245 }
246 
Generate(const FileDescriptor * file,const std::string & options,GeneratorContext * context,std::string * error) const247 bool IPCGenerator::Generate(const FileDescriptor* file,
248                             const std::string& options,
249                             GeneratorContext* context,
250                             std::string* error) const {
251   for (const std::string& option : SplitString(options, ",")) {
252     std::vector<std::string> option_pair = SplitString(option, "=");
253     if (option_pair[0] == "wrapper_namespace") {
254       wrapper_namespace_ =
255           option_pair.size() == 2 ? "." + option_pair[1] : std::string();
256     } else {
257       *error = "Unknown plugin option: " + option_pair[0];
258       return false;
259     }
260   }
261 
262   if (file->options().cc_generic_services()) {
263     *error = "Please set \"cc_generic_service = false\".";
264     return false;
265   }
266 
267   const std::unique_ptr<ZeroCopyOutputStream> h_fstream(
268       context->Open(GetStubName(*file) + ".h"));
269   const std::unique_ptr<ZeroCopyOutputStream> cc_fstream(
270       context->Open(GetStubName(*file) + ".cc"));
271 
272   // Variables are delimited by $.
273   Printer h_printer(h_fstream.get(), '$');
274   Printer cc_printer(cc_fstream.get(), '$');
275 
276   std::string guard = ToUpper(file->package() + "_" + file->name() + "_H_");
277   guard = StripChars(guard, ".-/\\", '_');
278 
279   h_printer.Print(kBanner);
280   h_printer.Print("#ifndef $guard$\n#define $guard$\n\n", "guard", guard);
281   h_printer.Print("#include \"perfetto/ext/ipc/deferred.h\"\n");
282   h_printer.Print("#include \"perfetto/ext/ipc/service.h\"\n");
283   h_printer.Print("#include \"perfetto/ext/ipc/service_descriptor.h\"\n");
284   h_printer.Print("#include \"perfetto/ext/ipc/service_proxy.h\"\n\n");
285 
286   // Add #include-s to .gen.h file for each .proto file imported, including the
287   // .proto file that defines services itself.
288   h_printer.Print("#include \"$h$\"\n", "h", StripName(*file) + ".gen.h");
289   for (int i = 0; i < file->dependency_count(); ++i) {
290     const FileDescriptor* file_dep = file->dependency(i);
291     h_printer.Print("#include \"$h$\"\n", "h", StripName(*file_dep) + ".gen.h");
292   }
293 
294   cc_printer.Print(kBanner);
295   cc_printer.Print("#include \"$h$\"\n", "h", GetStubName(*file) + ".h");
296   cc_printer.Print("#include \"perfetto/ext/ipc/codegen_helpers.h\"\n\n");
297   cc_printer.Print("#include <memory>\n");
298 
299   for (int i = 0; i < file->service_count(); i++) {
300     const ServiceDescriptor* svc = file->service(i);
301     GenerateServiceHeader(*file, *svc, &h_printer);
302     GenerateServiceCpp(*file, *svc, &cc_printer);
303   }
304 
305   h_printer.Print("#endif  // $guard$\n", "guard", guard);
306 
307   return true;
308 }
309 
310 }  // namespace
311 }  // namespace ipc
312 }  // namespace perfetto
313 
main(int argc,char * argv[])314 int main(int argc, char* argv[]) {
315   ::perfetto::ipc::IPCGenerator generator;
316   return google::protobuf::compiler::PluginMain(argc, argv, &generator);
317 }
318