blob: cca12947d26ecc7a0cddc60c2506de75cab0799f [file] [log] [blame]
Yao Chend54f9dd2017-10-17 17:37:48 +00001
2
3#include "Collation.h"
4
Stefan Lafonae2df012017-11-14 09:17:21 -08005#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
Yao Chend54f9dd2017-10-17 17:37:48 +00006
7#include <set>
8#include <vector>
9
10#include <getopt.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15using namespace google::protobuf;
16using namespace std;
17
18namespace android {
19namespace stats_log_api_gen {
20
Stefan Lafonae2df012017-11-14 09:17:21 -080021using android::os::statsd::Atom;
Yao Chend54f9dd2017-10-17 17:37:48 +000022
23// TODO: Support WorkSources
24
25/**
26 * Turn lower and camel case into upper case with underscores.
27 */
28static string
29make_constant_name(const string& str)
30{
31 string result;
32 const int N = str.size();
33 bool underscore_next = false;
34 for (int i=0; i<N; i++) {
35 char c = str[i];
36 if (c >= 'A' && c <= 'Z') {
37 if (underscore_next) {
38 result += '_';
39 underscore_next = false;
40 }
41 } else if (c >= 'a' && c <= 'z') {
42 c = 'A' + c - 'a';
43 underscore_next = true;
44 } else if (c == '_') {
45 underscore_next = false;
46 }
47 result += c;
48 }
49 return result;
50}
51
52static const char*
53cpp_type_name(java_type_t type)
54{
55 switch (type) {
56 case JAVA_TYPE_BOOLEAN:
57 return "bool";
58 case JAVA_TYPE_INT:
Stefan Lafon9478f352017-10-30 21:20:20 -070059 case JAVA_TYPE_ENUM:
Yao Chend54f9dd2017-10-17 17:37:48 +000060 return "int32_t";
61 case JAVA_TYPE_LONG:
62 return "int64_t";
63 case JAVA_TYPE_FLOAT:
64 return "float";
65 case JAVA_TYPE_DOUBLE:
66 return "double";
67 case JAVA_TYPE_STRING:
68 return "char const*";
69 default:
70 return "UNKNOWN";
71 }
72}
73
74static const char*
75java_type_name(java_type_t type)
76{
77 switch (type) {
78 case JAVA_TYPE_BOOLEAN:
79 return "boolean";
80 case JAVA_TYPE_INT:
Stefan Lafon9478f352017-10-30 21:20:20 -070081 case JAVA_TYPE_ENUM:
Yao Chend54f9dd2017-10-17 17:37:48 +000082 return "int";
83 case JAVA_TYPE_LONG:
84 return "long";
85 case JAVA_TYPE_FLOAT:
86 return "float";
87 case JAVA_TYPE_DOUBLE:
88 return "double";
89 case JAVA_TYPE_STRING:
90 return "java.lang.String";
91 default:
92 return "UNKNOWN";
93 }
94}
95
96static int
97write_stats_log_cpp(FILE* out, const Atoms& atoms)
98{
Yao Chend54f9dd2017-10-17 17:37:48 +000099 // Print prelude
100 fprintf(out, "// This file is autogenerated\n");
101 fprintf(out, "\n");
102
103 fprintf(out, "#include <log/log_event_list.h>\n");
104 fprintf(out, "#include <log/log.h>\n");
105 fprintf(out, "#include <statslog.h>\n");
106 fprintf(out, "\n");
107
108 fprintf(out, "namespace android {\n");
109 fprintf(out, "namespace util {\n");
Yao Chen80235402017-11-13 20:42:25 -0800110 fprintf(out, "// the single event tag id for all stats logs\n");
111 fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
Yao Chend54f9dd2017-10-17 17:37:48 +0000112
113 // Print write methods
114 fprintf(out, "\n");
115 for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
116 signature != atoms.signatures.end(); signature++) {
117 int argIndex;
118
119 fprintf(out, "void\n");
Yao Chen80235402017-11-13 20:42:25 -0800120 fprintf(out, "stats_write(int32_t code");
Yao Chend54f9dd2017-10-17 17:37:48 +0000121 argIndex = 1;
122 for (vector<java_type_t>::const_iterator arg = signature->begin();
123 arg != signature->end(); arg++) {
124 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
125 argIndex++;
126 }
127 fprintf(out, ")\n");
128
129 fprintf(out, "{\n");
130 argIndex = 1;
Yao Chen80235402017-11-13 20:42:25 -0800131 fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
132 fprintf(out, " event << code;\n");
Yao Chend54f9dd2017-10-17 17:37:48 +0000133 for (vector<java_type_t>::const_iterator arg = signature->begin();
134 arg != signature->end(); arg++) {
135 if (*arg == JAVA_TYPE_STRING) {
136 fprintf(out, " if (arg%d == NULL) {\n", argIndex);
137 fprintf(out, " arg%d = \"\";\n", argIndex);
138 fprintf(out, " }\n");
139 }
140 fprintf(out, " event << arg%d;\n", argIndex);
141 argIndex++;
142 }
143
144 fprintf(out, " event.write(LOG_ID_STATS);\n");
145 fprintf(out, "}\n");
146 fprintf(out, "\n");
147 }
148
149 // Print footer
150 fprintf(out, "\n");
151 fprintf(out, "} // namespace util\n");
152 fprintf(out, "} // namespace android\n");
153
154 return 0;
155}
156
157
158static int
159write_stats_log_header(FILE* out, const Atoms& atoms)
160{
Yao Chend54f9dd2017-10-17 17:37:48 +0000161 // Print prelude
162 fprintf(out, "// This file is autogenerated\n");
163 fprintf(out, "\n");
164 fprintf(out, "#pragma once\n");
165 fprintf(out, "\n");
166 fprintf(out, "#include <stdint.h>\n");
167 fprintf(out, "\n");
168
169 fprintf(out, "namespace android {\n");
170 fprintf(out, "namespace util {\n");
171 fprintf(out, "\n");
172 fprintf(out, "/*\n");
173 fprintf(out, " * API For logging statistics events.\n");
174 fprintf(out, " */\n");
175 fprintf(out, "\n");
176 fprintf(out, "/**\n");
Stefan Lafon9478f352017-10-30 21:20:20 -0700177 fprintf(out, " * Constants for atom codes.\n");
Yao Chend54f9dd2017-10-17 17:37:48 +0000178 fprintf(out, " */\n");
179 fprintf(out, "enum {\n");
180
181 size_t i = 0;
182 // Print constants
183 for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
184 atom != atoms.decls.end(); atom++) {
185 string constant = make_constant_name(atom->name);
186 fprintf(out, "\n");
187 fprintf(out, " /**\n");
188 fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
189 fprintf(out, " * Usage: stats_write(StatsLog.%s", constant.c_str());
190 for (vector<AtomField>::const_iterator field = atom->fields.begin();
191 field != atom->fields.end(); field++) {
192 fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
193 }
194 fprintf(out, ");\n");
195 fprintf(out, " */\n");
196 char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
197 fprintf(out, " %s = %d%s\n", constant.c_str(), atom->code, comma);
198 i++;
199 }
200 fprintf(out, "\n");
201 fprintf(out, "};\n");
202 fprintf(out, "\n");
203
204 // Print write methods
205 fprintf(out, "//\n");
206 fprintf(out, "// Write methods\n");
207 fprintf(out, "//\n");
208 for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
209 signature != atoms.signatures.end(); signature++) {
Yao Chen80235402017-11-13 20:42:25 -0800210 fprintf(out, "void stats_write(int32_t code ");
Yao Chend54f9dd2017-10-17 17:37:48 +0000211 int argIndex = 1;
212 for (vector<java_type_t>::const_iterator arg = signature->begin();
213 arg != signature->end(); arg++) {
214 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
215 argIndex++;
216 }
217 fprintf(out, ");\n");
218 }
219
220 fprintf(out, "\n");
221 fprintf(out, "} // namespace util\n");
222 fprintf(out, "} // namespace android\n");
223
224 return 0;
225}
226
227static int
228write_stats_log_java(FILE* out, const Atoms& atoms)
229{
Yao Chend54f9dd2017-10-17 17:37:48 +0000230 // Print prelude
231 fprintf(out, "// This file is autogenerated\n");
232 fprintf(out, "\n");
233 fprintf(out, "package android.util;\n");
234 fprintf(out, "\n");
235 fprintf(out, "\n");
236 fprintf(out, "/**\n");
237 fprintf(out, " * API For logging statistics events.\n");
238 fprintf(out, " * @hide\n");
239 fprintf(out, " */\n");
240 fprintf(out, "public final class StatsLog {\n");
Stefan Lafon9478f352017-10-30 21:20:20 -0700241 fprintf(out, " // Constants for atom codes.\n");
Yao Chend54f9dd2017-10-17 17:37:48 +0000242
Stefan Lafon9478f352017-10-30 21:20:20 -0700243 // Print constants for the atom codes.
Yao Chend54f9dd2017-10-17 17:37:48 +0000244 for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
245 atom != atoms.decls.end(); atom++) {
246 string constant = make_constant_name(atom->name);
247 fprintf(out, "\n");
248 fprintf(out, " /**\n");
249 fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
250 fprintf(out, " * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
251 for (vector<AtomField>::const_iterator field = atom->fields.begin();
252 field != atom->fields.end(); field++) {
253 fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
254 }
255 fprintf(out, ");\n");
256 fprintf(out, " */\n");
257 fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
258 }
259 fprintf(out, "\n");
260
Stefan Lafon9478f352017-10-30 21:20:20 -0700261 // Print constants for the enum values.
262 fprintf(out, " // Constants for enum values.\n\n");
263 for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
264 atom != atoms.decls.end(); atom++) {
265 for (vector<AtomField>::const_iterator field = atom->fields.begin();
266 field != atom->fields.end(); field++) {
267 if (field->javaType == JAVA_TYPE_ENUM) {
268 fprintf(out, " // Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
269 for (map<int, string>::const_iterator value = field->enumValues.begin();
270 value != field->enumValues.end(); value++) {
271 fprintf(out, " public static final int %s__%s__%s = %d;\n",
272 make_constant_name(atom->message).c_str(),
273 make_constant_name(field->name).c_str(),
274 make_constant_name(value->second).c_str(),
275 value->first);
276 }
277 fprintf(out, "\n");
278 }
279 }
280 }
281
Yao Chend54f9dd2017-10-17 17:37:48 +0000282 // Print write methods
283 fprintf(out, " // Write methods\n");
284 for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
285 signature != atoms.signatures.end(); signature++) {
286 fprintf(out, " public static native void write(int code");
287 int argIndex = 1;
288 for (vector<java_type_t>::const_iterator arg = signature->begin();
289 arg != signature->end(); arg++) {
290 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
291 argIndex++;
292 }
293 fprintf(out, ");\n");
294 }
295
296 fprintf(out, "}\n");
297
298 return 0;
299}
300
301static const char*
302jni_type_name(java_type_t type)
303{
304 switch (type) {
305 case JAVA_TYPE_BOOLEAN:
306 return "jboolean";
307 case JAVA_TYPE_INT:
Stefan Lafon9478f352017-10-30 21:20:20 -0700308 case JAVA_TYPE_ENUM:
Yao Chend54f9dd2017-10-17 17:37:48 +0000309 return "jint";
310 case JAVA_TYPE_LONG:
311 return "jlong";
312 case JAVA_TYPE_FLOAT:
313 return "jfloat";
314 case JAVA_TYPE_DOUBLE:
315 return "jdouble";
316 case JAVA_TYPE_STRING:
317 return "jstring";
318 default:
319 return "UNKNOWN";
320 }
321}
322
323static string
324jni_function_name(const vector<java_type_t>& signature)
325{
326 string result("StatsLog_write");
327 for (vector<java_type_t>::const_iterator arg = signature.begin();
328 arg != signature.end(); arg++) {
329 switch (*arg) {
330 case JAVA_TYPE_BOOLEAN:
331 result += "_boolean";
332 break;
333 case JAVA_TYPE_INT:
Stefan Lafon9478f352017-10-30 21:20:20 -0700334 case JAVA_TYPE_ENUM:
Yao Chend54f9dd2017-10-17 17:37:48 +0000335 result += "_int";
336 break;
337 case JAVA_TYPE_LONG:
338 result += "_long";
339 break;
340 case JAVA_TYPE_FLOAT:
341 result += "_float";
342 break;
343 case JAVA_TYPE_DOUBLE:
344 result += "_double";
345 break;
346 case JAVA_TYPE_STRING:
347 result += "_String";
348 break;
349 default:
350 result += "_UNKNOWN";
351 break;
352 }
353 }
354 return result;
355}
356
357static const char*
358java_type_signature(java_type_t type)
359{
360 switch (type) {
361 case JAVA_TYPE_BOOLEAN:
362 return "Z";
363 case JAVA_TYPE_INT:
Stefan Lafon9478f352017-10-30 21:20:20 -0700364 case JAVA_TYPE_ENUM:
Yao Chend54f9dd2017-10-17 17:37:48 +0000365 return "I";
366 case JAVA_TYPE_LONG:
367 return "J";
368 case JAVA_TYPE_FLOAT:
369 return "F";
370 case JAVA_TYPE_DOUBLE:
371 return "D";
372 case JAVA_TYPE_STRING:
373 return "Ljava/lang/String;";
374 default:
375 return "UNKNOWN";
376 }
377}
378
379static string
380jni_function_signature(const vector<java_type_t>& signature)
381{
382 string result("(I");
383 for (vector<java_type_t>::const_iterator arg = signature.begin();
384 arg != signature.end(); arg++) {
385 result += java_type_signature(*arg);
386 }
387 result += ")V";
388 return result;
389}
390
391static int
392write_stats_log_jni(FILE* out, const Atoms& atoms)
393{
Yao Chend54f9dd2017-10-17 17:37:48 +0000394 // Print prelude
395 fprintf(out, "// This file is autogenerated\n");
396 fprintf(out, "\n");
397
398 fprintf(out, "#include <statslog.h>\n");
399 fprintf(out, "\n");
400 fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
401 fprintf(out, "#include \"core_jni_helpers.h\"\n");
402 fprintf(out, "#include \"jni.h\"\n");
403 fprintf(out, "\n");
404 fprintf(out, "#define UNUSED __attribute__((__unused__))\n");
405 fprintf(out, "\n");
406
407 fprintf(out, "namespace android {\n");
408 fprintf(out, "\n");
409
410 // Print write methods
411 for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
412 signature != atoms.signatures.end(); signature++) {
413 int argIndex;
414
415 fprintf(out, "static void\n");
416 fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
417 jni_function_name(*signature).c_str());
418 argIndex = 1;
419 for (vector<java_type_t>::const_iterator arg = signature->begin();
420 arg != signature->end(); arg++) {
421 fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
422 argIndex++;
423 }
424 fprintf(out, ")\n");
425
426 fprintf(out, "{\n");
427
428 // Prepare strings
429 argIndex = 1;
430 bool hadString = false;
431 for (vector<java_type_t>::const_iterator arg = signature->begin();
432 arg != signature->end(); arg++) {
433 if (*arg == JAVA_TYPE_STRING) {
434 fprintf(out, " const char* str%d;\n", argIndex);
435 fprintf(out, " if (arg%d != NULL) {\n", argIndex);
436 fprintf(out, " str%d = env->GetStringUTFChars(arg%d, NULL);\n",
437 argIndex, argIndex);
438 fprintf(out, " } else {\n");
439 fprintf(out, " str%d = NULL;\n", argIndex);
440 fprintf(out, " }\n");
441 hadString = true;
442 }
443 argIndex++;
444 }
445
446 // Emit this to quiet the unused parameter warning if there were no strings.
447 if (!hadString) {
448 fprintf(out, " (void)env;\n");
449 }
450
451 // stats_write call
452 argIndex = 1;
453 fprintf(out, " android::util::stats_write(code");
454 for (vector<java_type_t>::const_iterator arg = signature->begin();
455 arg != signature->end(); arg++) {
456 const char* argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
457 fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
458 argIndex++;
459 }
460 fprintf(out, ");\n");
461
462 // Clean up strings
463 argIndex = 1;
464 for (vector<java_type_t>::const_iterator arg = signature->begin();
465 arg != signature->end(); arg++) {
466 if (*arg == JAVA_TYPE_STRING) {
467 fprintf(out, " if (str%d != NULL) {\n", argIndex);
468 fprintf(out, " env->ReleaseStringUTFChars(arg%d, str%d);\n",
469 argIndex, argIndex);
470 fprintf(out, " }\n");
471 }
472 argIndex++;
473 }
474
475 fprintf(out, "}\n");
476 fprintf(out, "\n");
477 }
478
479 // Print registration function table
480 fprintf(out, "/*\n");
481 fprintf(out, " * JNI registration.\n");
482 fprintf(out, " */\n");
483 fprintf(out, "static const JNINativeMethod gRegisterMethods[] = {\n");
484 for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
485 signature != atoms.signatures.end(); signature++) {
486 fprintf(out, " { \"write\", \"%s\", (void*)%s },\n",
487 jni_function_signature(*signature).c_str(),
488 jni_function_name(*signature).c_str());
489 }
490 fprintf(out, "};\n");
491 fprintf(out, "\n");
492
493 // Print registration function
494 fprintf(out, "int register_android_util_StatsLog(JNIEnv* env) {\n");
495 fprintf(out, " return RegisterMethodsOrDie(\n");
496 fprintf(out, " env,\n");
497 fprintf(out, " \"android/util/StatsLog\",\n");
498 fprintf(out, " gRegisterMethods, NELEM(gRegisterMethods));\n");
499 fprintf(out, "}\n");
500
501 fprintf(out, "\n");
502 fprintf(out, "} // namespace android\n");
503
504 return 0;
505}
506
507
508static void
509print_usage()
510{
511 fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
512 fprintf(stderr, "\n");
513 fprintf(stderr, "OPTIONS\n");
514 fprintf(stderr, " --cpp FILENAME the header file to output\n");
515 fprintf(stderr, " --header FILENAME the cpp file to output\n");
516 fprintf(stderr, " --help this message\n");
517 fprintf(stderr, " --java FILENAME the java file to output\n");
518 fprintf(stderr, " --jni FILENAME the jni file to output\n");
519}
520
521/**
522 * Do the argument parsing and execute the tasks.
523 */
524static int
525run(int argc, char const*const* argv)
526{
527 string cppFilename;
528 string headerFilename;
529 string javaFilename;
530 string jniFilename;
531
532 int index = 1;
533 while (index < argc) {
534 if (0 == strcmp("--help", argv[index])) {
535 print_usage();
536 return 0;
537 } else if (0 == strcmp("--cpp", argv[index])) {
538 index++;
539 if (index >= argc) {
540 print_usage();
541 return 1;
542 }
543 cppFilename = argv[index];
544 } else if (0 == strcmp("--header", argv[index])) {
545 index++;
546 if (index >= argc) {
547 print_usage();
548 return 1;
549 }
550 headerFilename = argv[index];
551 } else if (0 == strcmp("--java", argv[index])) {
552 index++;
553 if (index >= argc) {
554 print_usage();
555 return 1;
556 }
557 javaFilename = argv[index];
558 } else if (0 == strcmp("--jni", argv[index])) {
559 index++;
560 if (index >= argc) {
561 print_usage();
562 return 1;
563 }
564 jniFilename = argv[index];
565 }
566 index++;
567 }
568
569 if (cppFilename.size() == 0
570 && headerFilename.size() == 0
571 && javaFilename.size() == 0
572 && jniFilename.size() == 0) {
573 print_usage();
574 return 1;
575 }
576
577 // Collate the parameters
578 Atoms atoms;
Stefan Lafonae2df012017-11-14 09:17:21 -0800579 int errorCount = collate_atoms(Atom::descriptor(), &atoms);
Yao Chend54f9dd2017-10-17 17:37:48 +0000580 if (errorCount != 0) {
581 return 1;
582 }
583
584 // Write the .cpp file
585 if (cppFilename.size() != 0) {
586 FILE* out = fopen(cppFilename.c_str(), "w");
587 if (out == NULL) {
588 fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
589 return 1;
590 }
591 errorCount = android::stats_log_api_gen::write_stats_log_cpp(out, atoms);
592 fclose(out);
593 }
594
595 // Write the .h file
596 if (headerFilename.size() != 0) {
597 FILE* out = fopen(headerFilename.c_str(), "w");
598 if (out == NULL) {
599 fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
600 return 1;
601 }
602 errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms);
603 fclose(out);
604 }
605
606 // Write the .java file
607 if (javaFilename.size() != 0) {
608 FILE* out = fopen(javaFilename.c_str(), "w");
609 if (out == NULL) {
610 fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
611 return 1;
612 }
613 errorCount = android::stats_log_api_gen::write_stats_log_java(out, atoms);
614 fclose(out);
615 }
616
617 // Write the jni file
618 if (jniFilename.size() != 0) {
619 FILE* out = fopen(jniFilename.c_str(), "w");
620 if (out == NULL) {
621 fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
622 return 1;
623 }
624 errorCount = android::stats_log_api_gen::write_stats_log_jni(out, atoms);
625 fclose(out);
626 }
627
628 return 0;
629}
630
631}
632}
633
634/**
635 * Main.
636 */
637int
638main(int argc, char const*const* argv)
639{
640 GOOGLE_PROTOBUF_VERIFY_VERSION;
641
642 return android::stats_log_api_gen::run(argc, argv);
643}