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