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 "Collation.h"
18
19 #include <stdio.h>
20
21 #include <map>
22
23 #include "frameworks/proto_logging/stats/atoms.pb.h"
24
25 namespace android {
26 namespace stats_log_api_gen {
27
28 using google::protobuf::OneofDescriptor;
29 using google::protobuf::EnumDescriptor;
30 using google::protobuf::FieldDescriptor;
31 using google::protobuf::FileDescriptor;
32 using google::protobuf::SourceLocation;
33 using std::make_shared;
34 using std::map;
35
36 const bool dbg = false;
37
38 //
39 // AtomDecl class
40 //
41
AtomDecl()42 AtomDecl::AtomDecl() : code(0), name() {
43 }
44
AtomDecl(const AtomDecl & that)45 AtomDecl::AtomDecl(const AtomDecl& that)
46 : code(that.code),
47 name(that.name),
48 message(that.message),
49 fields(that.fields),
50 oneOfName(that.oneOfName),
51 fieldNumberToAnnotations(that.fieldNumberToAnnotations),
52 primaryFields(that.primaryFields),
53 exclusiveField(that.exclusiveField),
54 defaultState(that.defaultState),
55 triggerStateReset(that.triggerStateReset),
56 nested(that.nested) {
57 }
58
AtomDecl(int c,const string & n,const string & m,const string & o)59 AtomDecl::AtomDecl(int c, const string& n, const string& m, const string &o)
60 : code(c), name(n), message(m), oneOfName(o) {
61 }
62
~AtomDecl()63 AtomDecl::~AtomDecl() {
64 }
65
66 /**
67 * Print an error message for a FieldDescriptor, including the file name and
68 * line number.
69 */
print_error(const FieldDescriptor * field,const char * format,...)70 static void print_error(const FieldDescriptor* field, const char* format, ...) {
71 const Descriptor* message = field->containing_type();
72 const FileDescriptor* file = message->file();
73
74 SourceLocation loc;
75 if (field->GetSourceLocation(&loc)) {
76 // TODO(b/162454173): this will work if we can figure out how to pass
77 // --include_source_info to protoc
78 fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
79 } else {
80 fprintf(stderr, "%s: ", file->name().c_str());
81 }
82 va_list args;
83 va_start(args, format);
84 vfprintf(stderr, format, args);
85 va_end(args);
86 }
87
88 /**
89 * Convert a protobuf type into a java type.
90 */
java_type(const FieldDescriptor * field)91 static java_type_t java_type(const FieldDescriptor* field) {
92 int protoType = field->type();
93 switch (protoType) {
94 case FieldDescriptor::TYPE_DOUBLE:
95 return JAVA_TYPE_DOUBLE;
96 case FieldDescriptor::TYPE_FLOAT:
97 return JAVA_TYPE_FLOAT;
98 case FieldDescriptor::TYPE_INT64:
99 return JAVA_TYPE_LONG;
100 case FieldDescriptor::TYPE_UINT64:
101 return JAVA_TYPE_LONG;
102 case FieldDescriptor::TYPE_INT32:
103 return JAVA_TYPE_INT;
104 case FieldDescriptor::TYPE_FIXED64:
105 return JAVA_TYPE_LONG;
106 case FieldDescriptor::TYPE_FIXED32:
107 return JAVA_TYPE_INT;
108 case FieldDescriptor::TYPE_BOOL:
109 return JAVA_TYPE_BOOLEAN;
110 case FieldDescriptor::TYPE_STRING:
111 return JAVA_TYPE_STRING;
112 case FieldDescriptor::TYPE_GROUP:
113 return JAVA_TYPE_UNKNOWN;
114 case FieldDescriptor::TYPE_MESSAGE:
115 if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") {
116 return JAVA_TYPE_ATTRIBUTION_CHAIN;
117 } else if (field->message_type()->full_name() == "android.os.statsd.KeyValuePair") {
118 return JAVA_TYPE_KEY_VALUE_PAIR;
119 } else if (field->options().GetExtension(os::statsd::log_mode) ==
120 os::statsd::LogMode::MODE_BYTES) {
121 return JAVA_TYPE_BYTE_ARRAY;
122 } else {
123 return JAVA_TYPE_OBJECT;
124 }
125 case FieldDescriptor::TYPE_BYTES:
126 return JAVA_TYPE_BYTE_ARRAY;
127 case FieldDescriptor::TYPE_UINT32:
128 return JAVA_TYPE_INT;
129 case FieldDescriptor::TYPE_ENUM:
130 return JAVA_TYPE_ENUM;
131 case FieldDescriptor::TYPE_SFIXED32:
132 return JAVA_TYPE_INT;
133 case FieldDescriptor::TYPE_SFIXED64:
134 return JAVA_TYPE_LONG;
135 case FieldDescriptor::TYPE_SINT32:
136 return JAVA_TYPE_INT;
137 case FieldDescriptor::TYPE_SINT64:
138 return JAVA_TYPE_LONG;
139 default:
140 return JAVA_TYPE_UNKNOWN;
141 }
142 }
143
144 /**
145 * Gather the enums info.
146 */
collate_enums(const EnumDescriptor & enumDescriptor,AtomField * atomField)147 void collate_enums(const EnumDescriptor& enumDescriptor, AtomField* atomField) {
148 for (int i = 0; i < enumDescriptor.value_count(); i++) {
149 atomField->enumValues[enumDescriptor.value(i)->number()] =
150 enumDescriptor.value(i)->name();
151 }
152 }
153
addAnnotationToAtomDecl(AtomDecl * atomDecl,const int fieldNumber,const AnnotationId annotationId,const AnnotationType annotationType,const AnnotationValue annotationValue)154 static void addAnnotationToAtomDecl(AtomDecl* atomDecl, const int fieldNumber,
155 const AnnotationId annotationId,
156 const AnnotationType annotationType,
157 const AnnotationValue annotationValue) {
158 if (dbg) {
159 printf(" Adding annotation to %s: [%d] = {id: %d, type: %d}\n", atomDecl->name.c_str(),
160 fieldNumber, annotationId, annotationType);
161 }
162 atomDecl->fieldNumberToAnnotations[fieldNumber].insert(
163 make_shared<Annotation>(annotationId, atomDecl->code, annotationType, annotationValue));
164 }
165
collate_field_annotations(AtomDecl * atomDecl,const FieldDescriptor * field,const int fieldNumber,const java_type_t & javaType)166 static int collate_field_annotations(AtomDecl* atomDecl, const FieldDescriptor* field,
167 const int fieldNumber, const java_type_t& javaType) {
168 int errorCount = 0;
169
170 if (field->options().HasExtension(os::statsd::state_field_option)) {
171 const os::statsd::StateAtomFieldOption& stateFieldOption =
172 field->options().GetExtension(os::statsd::state_field_option);
173 const bool primaryField = stateFieldOption.primary_field();
174 const bool exclusiveState = stateFieldOption.exclusive_state();
175 const bool primaryFieldFirstUid = stateFieldOption.primary_field_first_uid();
176
177 // Check the field is only one of primaryField, exclusiveState, or primaryFieldFirstUid.
178 if (primaryField + primaryFieldFirstUid + exclusiveState > 1) {
179 print_error(field,
180 "Field can be max 1 of primary_field, exclusive_state, "
181 "or primary_field_first_uid: '%s'\n",
182 atomDecl->message.c_str());
183 errorCount++;
184 }
185
186 if (primaryField) {
187 if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
188 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
189 print_error(field, "Invalid primary state field: '%s'\n",
190 atomDecl->message.c_str());
191 errorCount++;
192 } else {
193 atomDecl->primaryFields.push_back(fieldNumber);
194 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_PRIMARY_FIELD,
195 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
196 }
197 }
198
199 if (primaryFieldFirstUid) {
200 if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
201 print_error(field,
202 "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: "
203 "'%s'\n",
204 atomDecl->message.c_str());
205 errorCount++;
206 } else {
207 atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
208 addAnnotationToAtomDecl(atomDecl, fieldNumber,
209 ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, ANNOTATION_TYPE_BOOL,
210 AnnotationValue(true));
211 }
212 }
213
214 if (exclusiveState) {
215 if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
216 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
217 print_error(field, "Invalid exclusive state field: '%s'\n",
218 atomDecl->message.c_str());
219 errorCount++;
220 }
221
222 if (atomDecl->exclusiveField != 0) {
223 print_error(field,
224 "Cannot have more than one exclusive state field in an "
225 "atom: '%s'\n",
226 atomDecl->message.c_str());
227 errorCount++;
228 } else {
229 atomDecl->exclusiveField = fieldNumber;
230 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_EXCLUSIVE_STATE,
231 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
232 }
233
234 if (stateFieldOption.has_default_state_value()) {
235 const int defaultState = stateFieldOption.default_state_value();
236 atomDecl->defaultState = defaultState;
237
238 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_DEFAULT_STATE,
239 ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
240 }
241
242 if (stateFieldOption.has_trigger_state_reset_value()) {
243 const int triggerStateReset = stateFieldOption.trigger_state_reset_value();
244
245 atomDecl->triggerStateReset = triggerStateReset;
246 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_TRIGGER_STATE_RESET,
247 ANNOTATION_TYPE_INT, AnnotationValue(triggerStateReset));
248 }
249
250 if (stateFieldOption.has_nested()) {
251 const bool nested = stateFieldOption.nested();
252 atomDecl->nested = nested;
253
254 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_NESTED,
255 ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
256 }
257 }
258 }
259
260 if (field->options().GetExtension(os::statsd::is_uid) == true) {
261 if (javaType != JAVA_TYPE_INT) {
262 print_error(field, "is_uid annotation can only be applied to int32 fields: '%s'\n",
263 atomDecl->message.c_str());
264 errorCount++;
265 }
266
267 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_IS_UID, ANNOTATION_TYPE_BOOL,
268 AnnotationValue(true));
269 }
270
271 return errorCount;
272 }
273
274 /**
275 * Gather the info about an atom proto.
276 */
collate_atom(const Descriptor * atom,AtomDecl * atomDecl,vector<java_type_t> * signature)277 int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature) {
278 int errorCount = 0;
279
280 // Build a sorted list of the fields. Descriptor has them in source file
281 // order.
282 map<int, const FieldDescriptor*> fields;
283 for (int j = 0; j < atom->field_count(); j++) {
284 const FieldDescriptor* field = atom->field(j);
285 fields[field->number()] = field;
286 }
287
288 // Check that the parameters start at 1 and go up sequentially.
289 int expectedNumber = 1;
290 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
291 it++) {
292 const int number = it->first;
293 const FieldDescriptor* field = it->second;
294 if (number != expectedNumber) {
295 print_error(field,
296 "Fields must be numbered consecutively starting at 1:"
297 " '%s' is %d but should be %d\n",
298 field->name().c_str(), number, expectedNumber);
299 errorCount++;
300 expectedNumber = number;
301 continue;
302 }
303 expectedNumber++;
304 }
305
306 // Check that only allowed types are present. Remove any invalid ones.
307 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
308 it++) {
309 const FieldDescriptor* field = it->second;
310 bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
311 os::statsd::LogMode::MODE_BYTES;
312
313 java_type_t javaType = java_type(field);
314
315 if (javaType == JAVA_TYPE_UNKNOWN) {
316 print_error(field, "Unknown type for field: %s\n", field->name().c_str());
317 errorCount++;
318 continue;
319 } else if (javaType == JAVA_TYPE_OBJECT && atomDecl->code < PULL_ATOM_START_ID) {
320 // Allow attribution chain, but only at position 1.
321 print_error(field, "Message type not allowed for field in pushed atoms: %s\n",
322 field->name().c_str());
323 errorCount++;
324 continue;
325 } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
326 print_error(field, "Raw bytes type not allowed for field: %s\n", field->name().c_str());
327 errorCount++;
328 continue;
329 }
330
331 if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
332 print_error(field, "Cannot mark field %s as bytes.\n", field->name().c_str());
333 errorCount++;
334 continue;
335 }
336
337 // Doubles are not supported yet.
338 if (javaType == JAVA_TYPE_DOUBLE) {
339 print_error(field,
340 "Doubles are not supported in atoms. Please change field %s "
341 "to float\n",
342 field->name().c_str());
343 errorCount++;
344 continue;
345 }
346
347 if (field->is_repeated() &&
348 !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) {
349 print_error(field,
350 "Repeated fields are not supported in atoms. Please make "
351 "field %s not "
352 "repeated.\n",
353 field->name().c_str());
354 errorCount++;
355 continue;
356 }
357 }
358
359 // Check that if there's an attribution chain, it's at position 1.
360 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
361 it++) {
362 int number = it->first;
363 if (number != 1) {
364 const FieldDescriptor* field = it->second;
365 java_type_t javaType = java_type(field);
366 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
367 print_error(field,
368 "AttributionChain fields must have field id 1, in message: '%s'\n",
369 atom->name().c_str());
370 errorCount++;
371 }
372 }
373 }
374
375 // Build the type signature and the atom data.
376 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
377 it++) {
378 const FieldDescriptor* field = it->second;
379 java_type_t javaType = java_type(field);
380 bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
381 os::statsd::LogMode::MODE_BYTES;
382
383 AtomField atField(field->name(), javaType);
384
385 if (javaType == JAVA_TYPE_ENUM) {
386 // All enums are treated as ints when it comes to function signatures.
387 collate_enums(*field->enum_type(), &atField);
388 }
389
390 // Generate signature for atom.
391 if (javaType == JAVA_TYPE_ENUM) {
392 // All enums are treated as ints when it comes to function signatures.
393 signature->push_back(JAVA_TYPE_INT);
394 } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
395 signature->push_back(JAVA_TYPE_BYTE_ARRAY);
396 } else {
397 signature->push_back(javaType);
398 }
399
400 atomDecl->fields.push_back(atField);
401
402 errorCount += collate_field_annotations(atomDecl, field, it->first, javaType);
403 }
404
405 return errorCount;
406 }
407
408 // This function flattens the fields of the AttributionNode proto in an Atom
409 // proto and generates the corresponding atom decl and signature.
get_non_chained_node(const Descriptor * atom,AtomDecl * atomDecl,vector<java_type_t> * signature)410 bool get_non_chained_node(const Descriptor* atom, AtomDecl* atomDecl,
411 vector<java_type_t>* signature) {
412 // Build a sorted list of the fields. Descriptor has them in source file
413 // order.
414 map<int, const FieldDescriptor*> fields;
415 for (int j = 0; j < atom->field_count(); j++) {
416 const FieldDescriptor* field = atom->field(j);
417 fields[field->number()] = field;
418 }
419
420 AtomDecl attributionDecl;
421 vector<java_type_t> attributionSignature;
422 collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
423 &attributionSignature);
424
425 // Build the type signature and the atom data.
426 bool has_attribution_node = false;
427 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
428 it++) {
429 const FieldDescriptor* field = it->second;
430 java_type_t javaType = java_type(field);
431 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
432 atomDecl->fields.insert(atomDecl->fields.end(), attributionDecl.fields.begin(),
433 attributionDecl.fields.end());
434 signature->insert(signature->end(), attributionSignature.begin(),
435 attributionSignature.end());
436 has_attribution_node = true;
437
438 } else {
439 AtomField atField(field->name(), javaType);
440 if (javaType == JAVA_TYPE_ENUM) {
441 // All enums are treated as ints when it comes to function signatures.
442 signature->push_back(JAVA_TYPE_INT);
443 collate_enums(*field->enum_type(), &atField);
444 } else {
445 signature->push_back(javaType);
446 }
447 atomDecl->fields.push_back(atField);
448 }
449 }
450 return has_attribution_node;
451 }
452
populateFieldNumberToAtomDeclSet(const shared_ptr<AtomDecl> & atomDecl,FieldNumberToAtomDeclSet * fieldNumberToAtomDeclSet)453 static void populateFieldNumberToAtomDeclSet(const shared_ptr<AtomDecl>& atomDecl,
454 FieldNumberToAtomDeclSet* fieldNumberToAtomDeclSet) {
455 for (FieldNumberToAnnotations::const_iterator it = atomDecl->fieldNumberToAnnotations.begin();
456 it != atomDecl->fieldNumberToAnnotations.end(); it++) {
457 const int fieldNumber = it->first;
458 (*fieldNumberToAtomDeclSet)[fieldNumber].insert(atomDecl);
459 }
460 }
461
462 /**
463 * Gather the info about the atoms.
464 */
collate_atoms(const Descriptor * descriptor,const string & moduleName,Atoms * atoms)465 int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms) {
466 int errorCount = 0;
467
468 for (int i = 0; i < descriptor->field_count(); i++) {
469 const FieldDescriptor* atomField = descriptor->field(i);
470
471 if (moduleName != DEFAULT_MODULE_NAME) {
472 const int moduleCount = atomField->options().ExtensionSize(os::statsd::module);
473 int j;
474 for (j = 0; j < moduleCount; ++j) {
475 const string atomModuleName =
476 atomField->options().GetExtension(os::statsd::module, j);
477 if (atomModuleName == moduleName) {
478 break;
479 }
480 }
481
482 // This atom is not in the module we're interested in; skip it.
483 if (moduleCount == j) {
484 if (dbg) {
485 printf(" Skipping %s (%d)\n", atomField->name().c_str(), atomField->number());
486 }
487 continue;
488 }
489 }
490
491 if (dbg) {
492 printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
493 }
494
495 // StatsEvent only has one oneof, which contains only messages. Don't allow
496 // other types.
497 if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
498 print_error(atomField,
499 "Bad type for atom. StatsEvent can only have message type "
500 "fields: %s\n",
501 atomField->name().c_str());
502 errorCount++;
503 continue;
504 }
505
506 const OneofDescriptor* oneofAtom = atomField->containing_oneof();
507 if (oneofAtom == nullptr) {
508 print_error(atomField, "Atom is not declared in a `oneof` field: %s\n",
509 atomField->name().c_str());
510 errorCount++;
511 continue;
512 }
513
514 const Descriptor* atom = atomField->message_type();
515 shared_ptr<AtomDecl> atomDecl =
516 make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name(),
517 oneofAtom->name());
518
519 if (atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
520 addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
521 ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
522 AnnotationValue(true));
523 if (dbg) {
524 printf("%s can have timestamp truncated\n", atomField->name().c_str());
525 }
526 }
527
528 vector<java_type_t> signature;
529 errorCount += collate_atom(atom, atomDecl.get(), &signature);
530 if (!atomDecl->primaryFields.empty() && atomDecl->exclusiveField == 0) {
531 print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n",
532 atomField->name().c_str());
533 errorCount++;
534 continue;
535 }
536
537 if ((oneofAtom->name() != ONEOF_PUSHED_ATOM_NAME) &&
538 (oneofAtom->name() != ONEOF_PULLED_ATOM_NAME)) {
539 print_error(atomField, "Atom is neither a pushed nor pulled atom: %s\n",
540 atomField->name().c_str());
541 errorCount++;
542 continue;
543 }
544
545 FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = oneofAtom->name() ==
546 ONEOF_PUSHED_ATOM_NAME ? atoms->signatureInfoMap[signature] :
547 atoms->pulledAtomsSignatureInfoMap[signature];
548 populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet);
549
550 atoms->decls.insert(atomDecl);
551
552 shared_ptr<AtomDecl> nonChainedAtomDecl =
553 make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name(),
554 oneofAtom->name());
555 vector<java_type_t> nonChainedSignature;
556 if (get_non_chained_node(atom, nonChainedAtomDecl.get(), &nonChainedSignature)) {
557 FieldNumberToAtomDeclSet& nonChainedFieldNumberToAtomDeclSet =
558 atoms->nonChainedSignatureInfoMap[nonChainedSignature];
559 populateFieldNumberToAtomDeclSet(nonChainedAtomDecl,
560 &nonChainedFieldNumberToAtomDeclSet);
561
562 atoms->non_chained_decls.insert(nonChainedAtomDecl);
563 }
564 }
565
566 if (dbg) {
567 // Signatures for pushed atoms.
568 printf("signatures = [\n");
569 for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin();
570 it != atoms->signatureInfoMap.end(); it++) {
571 printf(" ");
572 for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
573 jt++) {
574 printf(" %d", static_cast<int>(*jt));
575 }
576 printf("\n");
577 }
578
579 // Signatures for pull atoms.
580 for (SignatureInfoMap::const_iterator it = atoms->pulledAtomsSignatureInfoMap.begin();
581 it != atoms->pulledAtomsSignatureInfoMap.end(); it++) {
582 printf(" ");
583 for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
584 jt++) {
585 printf(" %d", static_cast<int>(*jt));
586 }
587 printf("\n");
588 }
589 printf("]\n");
590 }
591
592 return errorCount;
593 }
594
595 } // namespace stats_log_api_gen
596 } // namespace android
597