1
2 #include <google/protobuf/descriptor.h>
3 #include <google/protobuf/stubs/common.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <cstdlib>
9 #include <filesystem>
10
11 #include "Collation.h"
12 #include "frameworks/proto_logging/stats/atoms.pb.h"
13 #include "frameworks/proto_logging/stats/attribution_node.pb.h"
14 #include "java_writer.h"
15 #include "native_writer.h"
16 #include "rust_writer.h"
17 #include "utils.h"
18
19 #ifdef WITH_VENDOR
20 #include "java_writer_vendor.h"
21 #include "native_writer_vendor.h"
22 #endif
23
24 namespace android {
25 namespace stats_log_api_gen {
26
27 namespace fs = std::filesystem;
28 using android::os::statsd::Atom;
29
print_usage()30 static void print_usage() {
31 fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
32 fprintf(stderr, "\n");
33 fprintf(stderr, "OPTIONS\n");
34 fprintf(stderr, " --cpp FILENAME the cpp file to output for write helpers\n");
35 fprintf(stderr, " --header FILENAME the header file to output for write helpers\n");
36 fprintf(stderr, " --help this message\n");
37 fprintf(stderr, " --java FILENAME the java file to output\n");
38 fprintf(stderr, " --rust FILENAME the rust file to output\n");
39 fprintf(stderr, " --rustHeader FILENAME the rust file to output for write helpers\n");
40 fprintf(stderr,
41 " --rustHeaderCrate NAME header crate to be used while "
42 "generating the code. Note: this should be the same as the crate_name "
43 "created by rust_library for the header \n");
44 fprintf(stderr, " --module NAME optional, module name to generate outputs for\n");
45 fprintf(stderr,
46 " --namespace COMMA,SEP,NAMESPACE required for cpp/header with "
47 "module\n");
48 fprintf(stderr,
49 " comma separated namespace of "
50 "the files\n");
51 fprintf(stderr,
52 " --importHeader NAME required for cpp/jni to say which header to "
53 "import "
54 "for write helpers\n");
55 fprintf(stderr, " --javaPackage PACKAGE the package for the java file.\n");
56 fprintf(stderr, " required for java with module\n");
57 fprintf(stderr, " --javaClass CLASS the class name of the java class.\n");
58 fprintf(stderr, " --minApiLevel API_LEVEL lowest API level to support.\n");
59 fprintf(stderr, " Default is \"current\".\n");
60 fprintf(stderr,
61 " --worksource Include support for logging WorkSource "
62 "objects.\n");
63 fprintf(stderr, " Default is \"current\".\n");
64 fprintf(stderr,
65 " --bootstrap If this logging is from a bootstrap process. "
66 "Only supported for cpp. Do not use unless necessary.\n");
67 #ifdef WITH_VENDOR
68 fprintf(stderr,
69 " --vendor-proto Path to the proto file for vendor atoms logging\n"
70 "code generation.\n");
71 #endif
72 }
73
74 /**
75 * Do the argument parsing and execute the tasks.
76 */
run(int argc,char const * const * argv)77 static int run(int argc, char const* const* argv) {
78 string cppFilename;
79 string headerFilename;
80 string javaFilename;
81 string javaPackage;
82 string javaClass;
83 string rustFilename;
84 string rustHeaderFilename;
85 string rustHeaderCrate;
86 string moduleName = DEFAULT_MODULE_NAME;
87 string cppNamespace = DEFAULT_CPP_NAMESPACE;
88 string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
89 string vendorProto;
90 bool supportWorkSource = false;
91 int minApiLevel = API_LEVEL_CURRENT;
92 bool bootstrap = false;
93
94 int index = 1;
95 while (index < argc) {
96 if (0 == strcmp("--help", argv[index])) {
97 print_usage();
98 return 0;
99 } else if (0 == strcmp("--cpp", argv[index])) {
100 index++;
101 if (index >= argc) {
102 print_usage();
103 return 1;
104 }
105 cppFilename = argv[index];
106 } else if (0 == strcmp("--header", argv[index])) {
107 index++;
108 if (index >= argc) {
109 print_usage();
110 return 1;
111 }
112 headerFilename = argv[index];
113 } else if (0 == strcmp("--java", argv[index])) {
114 index++;
115 if (index >= argc) {
116 print_usage();
117 return 1;
118 }
119 javaFilename = argv[index];
120 } else if (0 == strcmp("--rust", argv[index])) {
121 index++;
122 if (index >= argc) {
123 print_usage();
124 return 1;
125 }
126 rustFilename = argv[index];
127 } else if (0 == strcmp("--rustHeader", argv[index])) {
128 index++;
129 if (index >= argc) {
130 print_usage();
131 return 1;
132 }
133 rustHeaderFilename = argv[index];
134 } else if (0 == strcmp("--rustHeaderCrate", argv[index])) {
135 index++;
136 if (index >= argc) {
137 print_usage();
138 return 1;
139 }
140 rustHeaderCrate = argv[index];
141 } else if (0 == strcmp("--module", argv[index])) {
142 index++;
143 if (index >= argc) {
144 print_usage();
145 return 1;
146 }
147 moduleName = argv[index];
148 } else if (0 == strcmp("--namespace", argv[index])) {
149 index++;
150 if (index >= argc) {
151 print_usage();
152 return 1;
153 }
154 cppNamespace = argv[index];
155 } else if (0 == strcmp("--importHeader", argv[index])) {
156 index++;
157 if (index >= argc) {
158 print_usage();
159 return 1;
160 }
161 cppHeaderImport = argv[index];
162 } else if (0 == strcmp("--javaPackage", argv[index])) {
163 index++;
164 if (index >= argc) {
165 print_usage();
166 return 1;
167 }
168 javaPackage = argv[index];
169 } else if (0 == strcmp("--javaClass", argv[index])) {
170 index++;
171 if (index >= argc) {
172 print_usage();
173 return 1;
174 }
175 javaClass = argv[index];
176 } else if (0 == strcmp("--supportQ", argv[index])) {
177 minApiLevel = API_Q;
178 } else if (0 == strcmp("--worksource", argv[index])) {
179 supportWorkSource = true;
180 } else if (0 == strcmp("--minApiLevel", argv[index])) {
181 index++;
182 if (index >= argc) {
183 print_usage();
184 return 1;
185 }
186 if (0 != strcmp("current", argv[index])) {
187 minApiLevel = atoi(argv[index]);
188 }
189 } else if (0 == strcmp("--bootstrap", argv[index])) {
190 bootstrap = true;
191 #ifdef WITH_VENDOR
192 } else if (0 == strcmp("--vendor-proto", argv[index])) {
193 index++;
194 if (index >= argc) {
195 print_usage();
196 return 1;
197 }
198
199 vendorProto = argv[index];
200 #endif
201 }
202
203 index++;
204 }
205 if (index < argc) {
206 fprintf(stderr, "Error: Unknown command line argument\n");
207 print_usage();
208 return 1;
209 }
210
211 if (cppFilename.empty() && headerFilename.empty() && javaFilename.empty() &&
212 rustFilename.empty() && rustHeaderFilename.empty()) {
213 print_usage();
214 return 1;
215 }
216 if (DEFAULT_MODULE_NAME == moduleName && minApiLevel != API_LEVEL_CURRENT) {
217 // Default module only supports current API level.
218 fprintf(stderr, "%s cannot support older API levels\n", moduleName.c_str());
219 return 1;
220 }
221
222 if (minApiLevel < API_Q) {
223 // Cannot support pre-Q.
224 fprintf(stderr, "minApiLevel must be %d or higher.\n", API_Q);
225 return 1;
226 }
227
228 if (bootstrap) {
229 if (cppFilename.empty() && headerFilename.empty()) {
230 fprintf(stderr, "Bootstrap flag can only be used for cpp/header files.\n");
231 return 1;
232 }
233 if (supportWorkSource) {
234 fprintf(stderr, "Bootstrap flag does not support worksources");
235 return 1;
236 }
237 if (minApiLevel != API_LEVEL_CURRENT) {
238 fprintf(stderr, "Bootstrap flag does not support older API levels");
239 return 1;
240 }
241 }
242
243 // Collate the parameters
244 int errorCount = 0;
245
246 Atoms atoms;
247
248 MFErrorCollector errorCollector;
249 google::protobuf::compiler::DiskSourceTree sourceTree;
250 google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector);
251
252 if (vendorProto.empty()) {
253 errorCount = collate_atoms(*Atom::descriptor(), moduleName, atoms);
254 } else {
255 const google::protobuf::FileDescriptor* fileDescriptor;
256 sourceTree.MapPath("", fs::current_path().c_str());
257
258 const char* androidBuildTop = std::getenv("ANDROID_BUILD_TOP");
259
260 fs::path protobufSrc = androidBuildTop != nullptr ? androidBuildTop : fs::current_path();
261 protobufSrc /= "external/protobuf/src";
262 sourceTree.MapPath("", protobufSrc.c_str());
263
264 if (androidBuildTop != nullptr) {
265 sourceTree.MapPath("", androidBuildTop);
266 }
267
268 fileDescriptor = importer.Import(vendorProto);
269 errorCount =
270 collate_atoms(*fileDescriptor->FindMessageTypeByName("Atom"), moduleName, atoms);
271 }
272
273 if (errorCount != 0) {
274 return 1;
275 }
276
277 AtomDecl attributionDecl;
278 vector<java_type_t> attributionSignature;
279 collate_atom(*android::os::statsd::AttributionNode::descriptor(), attributionDecl,
280 attributionSignature);
281
282 // Write the .cpp file
283 if (!cppFilename.empty()) {
284 // If this is for a specific module, the namespace must also be provided.
285 if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
286 fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
287 return 1;
288 }
289 // If this is for a specific module, the header file to import must also be
290 // provided.
291 if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
292 fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
293 return 1;
294 }
295 FILE* out = fopen(cppFilename.c_str(), "we");
296 if (out == nullptr) {
297 fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
298 return 1;
299 }
300 if (vendorProto.empty()) {
301 errorCount = android::stats_log_api_gen::write_stats_log_cpp(
302 out, atoms, attributionDecl, cppNamespace, cppHeaderImport, minApiLevel,
303 bootstrap);
304 } else {
305 #ifdef WITH_VENDOR
306 errorCount = android::stats_log_api_gen::write_stats_log_cpp_vendor(
307 out, atoms, attributionDecl, cppNamespace, cppHeaderImport);
308 #endif
309 }
310 fclose(out);
311 }
312
313 // Write the .h file
314 if (!headerFilename.empty()) {
315 // If this is for a specific module, the namespace must also be provided.
316 if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
317 fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
318 }
319 FILE* out = fopen(headerFilename.c_str(), "we");
320 if (out == nullptr) {
321 fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
322 return 1;
323 }
324
325 if (vendorProto.empty()) {
326 errorCount = android::stats_log_api_gen::write_stats_log_header(
327 out, atoms, attributionDecl, cppNamespace, minApiLevel, bootstrap);
328 } else {
329 #ifdef WITH_VENDOR
330 errorCount = android::stats_log_api_gen::write_stats_log_header_vendor(
331 out, atoms, attributionDecl, cppNamespace);
332 #endif
333 }
334 fclose(out);
335 }
336
337 // Write the .java file
338 if (!javaFilename.empty()) {
339 if (javaClass.empty()) {
340 fprintf(stderr, "Must supply --javaClass if supplying a Java filename");
341 return 1;
342 }
343
344 if (javaPackage.empty()) {
345 fprintf(stderr, "Must supply --javaPackage if supplying a Java filename");
346 return 1;
347 }
348
349 if (moduleName.empty() || moduleName == DEFAULT_MODULE_NAME) {
350 fprintf(stderr, "Must supply --module if supplying a Java filename");
351 return 1;
352 }
353
354 FILE* out = fopen(javaFilename.c_str(), "we");
355 if (out == nullptr) {
356 fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
357 return 1;
358 }
359
360 if (vendorProto.empty()) {
361 errorCount = android::stats_log_api_gen::write_stats_log_java(
362 out, atoms, attributionDecl, javaClass, javaPackage, minApiLevel,
363 supportWorkSource);
364 } else {
365 #ifdef WITH_VENDOR
366 if (supportWorkSource) {
367 fprintf(stderr, "The attribution chain is not supported for vendor atoms");
368 return 1;
369 }
370
371 errorCount = android::stats_log_api_gen::write_stats_log_java_vendor(out, atoms,
372 javaClass, javaPackage);
373 #endif
374 }
375
376 fclose(out);
377 }
378
379 // Write the main .rs file
380 if (!rustFilename.empty()) {
381 if (rustHeaderCrate.empty()) {
382 fprintf(stderr, "rustHeaderCrate flag is either not passed or is empty");
383 return 1;
384 }
385
386 FILE* out = fopen(rustFilename.c_str(), "we");
387 if (out == nullptr) {
388 fprintf(stderr, "Unable to open file for write: %s\n", rustFilename.c_str());
389 return 1;
390 }
391
392 errorCount += android::stats_log_api_gen::write_stats_log_rust(
393 out, atoms, attributionDecl, minApiLevel, rustHeaderCrate.c_str());
394
395 fclose(out);
396 }
397
398 // Write the header .rs file
399 if (!rustHeaderFilename.empty()) {
400 if (rustHeaderCrate.empty()) {
401 fprintf(stderr, "rustHeaderCrate flag is either not passed or is empty");
402 return 1;
403 }
404
405 FILE* out = fopen(rustHeaderFilename.c_str(), "we");
406 if (out == nullptr) {
407 fprintf(stderr, "Unable to open file for write: %s\n", rustHeaderFilename.c_str());
408 return 1;
409 }
410
411 android::stats_log_api_gen::write_stats_log_rust_header(out, atoms, attributionDecl,
412 rustHeaderCrate.c_str());
413
414 fclose(out);
415 }
416
417 return errorCount;
418 }
419
420 } // namespace stats_log_api_gen
421 } // namespace android
422
423 /**
424 * Main.
425 */
main(int argc,char const * const * argv)426 int main(int argc, char const* const* argv) {
427 GOOGLE_PROTOBUF_VERIFY_VERSION;
428
429 return android::stats_log_api_gen::run(argc, argv);
430 }
431