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