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