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 #include "frameworks/base/cmds/statsd/src/atoms.pb.h" 19 20 #include <stdio.h> 21 #include <map> 22 23 namespace android { 24 namespace stats_log_api_gen { 25 26 using google::protobuf::EnumDescriptor; 27 using google::protobuf::FieldDescriptor; 28 using google::protobuf::FileDescriptor; 29 using google::protobuf::SourceLocation; 30 using std::map; 31 32 33 // 34 // AtomDecl class 35 // 36 37 AtomDecl::AtomDecl() 38 :code(0), 39 name() 40 { 41 } 42 43 AtomDecl::AtomDecl(const AtomDecl& that) 44 : code(that.code), 45 name(that.name), 46 message(that.message), 47 fields(that.fields), 48 primaryFields(that.primaryFields), 49 exclusiveField(that.exclusiveField), 50 uidField(that.uidField) {} 51 52 AtomDecl::AtomDecl(int c, const string& n, const string& m) 53 :code(c), 54 name(n), 55 message(m) 56 { 57 } 58 59 AtomDecl::~AtomDecl() 60 { 61 } 62 63 64 /** 65 * Print an error message for a FieldDescriptor, including the file name and line number. 66 */ 67 static void 68 print_error(const FieldDescriptor* field, const char* format, ...) 69 { 70 const Descriptor* message = field->containing_type(); 71 const FileDescriptor* file = message->file(); 72 73 SourceLocation loc; 74 if (field->GetSourceLocation(&loc)) { 75 // TODO: this will work if we can figure out how to pass --include_source_info to protoc 76 fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line); 77 } else { 78 fprintf(stderr, "%s: ", file->name().c_str()); 79 } 80 va_list args; 81 va_start(args, format); 82 vfprintf(stderr, format, args); 83 va_end (args); 84 } 85 86 /** 87 * Convert a protobuf type into a java type. 88 */ 89 static java_type_t 90 java_type(const FieldDescriptor* field) 91 { 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 // TODO: not the final package name 116 if (field->message_type()->full_name() == 117 "android.os.statsd.AttributionNode") { 118 return JAVA_TYPE_ATTRIBUTION_CHAIN; 119 } else { 120 return JAVA_TYPE_OBJECT; 121 } 122 case FieldDescriptor::TYPE_BYTES: 123 return JAVA_TYPE_BYTE_ARRAY; 124 case FieldDescriptor::TYPE_UINT32: 125 return JAVA_TYPE_INT; 126 case FieldDescriptor::TYPE_ENUM: 127 return JAVA_TYPE_ENUM; 128 case FieldDescriptor::TYPE_SFIXED32: 129 return JAVA_TYPE_INT; 130 case FieldDescriptor::TYPE_SFIXED64: 131 return JAVA_TYPE_LONG; 132 case FieldDescriptor::TYPE_SINT32: 133 return JAVA_TYPE_INT; 134 case FieldDescriptor::TYPE_SINT64: 135 return JAVA_TYPE_LONG; 136 default: 137 return JAVA_TYPE_UNKNOWN; 138 } 139 } 140 141 /** 142 * Gather the enums info. 143 */ 144 void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) { 145 for (int i = 0; i < enumDescriptor.value_count(); i++) { 146 atomField->enumValues[enumDescriptor.value(i)->number()] = 147 enumDescriptor.value(i)->name().c_str(); 148 } 149 } 150 151 /** 152 * Gather the info about an atom proto. 153 */ 154 int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, 155 vector<java_type_t> *signature) { 156 157 int errorCount = 0; 158 // Build a sorted list of the fields. Descriptor has them in source file 159 // order. 160 map<int, const FieldDescriptor *> fields; 161 for (int j = 0; j < atom->field_count(); j++) { 162 const FieldDescriptor *field = atom->field(j); 163 fields[field->number()] = field; 164 } 165 166 // Check that the parameters start at 1 and go up sequentially. 167 int expectedNumber = 1; 168 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); 169 it != fields.end(); it++) { 170 const int number = it->first; 171 const FieldDescriptor *field = it->second; 172 if (number != expectedNumber) { 173 print_error(field, 174 "Fields must be numbered consecutively starting at 1:" 175 " '%s' is %d but should be %d\n", 176 field->name().c_str(), number, expectedNumber); 177 errorCount++; 178 expectedNumber = number; 179 continue; 180 } 181 expectedNumber++; 182 } 183 184 // Check that only allowed types are present. Remove any invalid ones. 185 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); 186 it != fields.end(); it++) { 187 const FieldDescriptor *field = it->second; 188 189 java_type_t javaType = java_type(field); 190 191 if (javaType == JAVA_TYPE_UNKNOWN) { 192 print_error(field, "Unkown type for field: %s\n", field->name().c_str()); 193 errorCount++; 194 continue; 195 } else if (javaType == JAVA_TYPE_OBJECT) { 196 // Allow attribution chain, but only at position 1. 197 print_error(field, "Message type not allowed for field: %s\n", 198 field->name().c_str()); 199 errorCount++; 200 continue; 201 } else if (javaType == JAVA_TYPE_BYTE_ARRAY) { 202 print_error(field, "Raw bytes type not allowed for field: %s\n", 203 field->name().c_str()); 204 errorCount++; 205 continue; 206 } 207 } 208 209 // Check that if there's an attribution chain, it's at position 1. 210 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); 211 it != fields.end(); it++) { 212 int number = it->first; 213 if (number != 1) { 214 const FieldDescriptor *field = it->second; 215 java_type_t javaType = java_type(field); 216 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { 217 print_error( 218 field, 219 "AttributionChain fields must have field id 1, in message: '%s'\n", 220 atom->name().c_str()); 221 errorCount++; 222 } 223 } 224 } 225 226 // Build the type signature and the atom data. 227 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); 228 it != fields.end(); it++) { 229 const FieldDescriptor *field = it->second; 230 java_type_t javaType = java_type(field); 231 232 AtomField atField(field->name(), javaType); 233 if (javaType == JAVA_TYPE_ENUM) { 234 // All enums are treated as ints when it comes to function signatures. 235 signature->push_back(JAVA_TYPE_INT); 236 collate_enums(*field->enum_type(), &atField); 237 } else { 238 signature->push_back(javaType); 239 } 240 atomDecl->fields.push_back(atField); 241 242 if (field->options().GetExtension(os::statsd::stateFieldOption).option() == 243 os::statsd::StateField::PRIMARY) { 244 if (javaType == JAVA_TYPE_UNKNOWN || 245 javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || 246 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) { 247 errorCount++; 248 } 249 atomDecl->primaryFields.push_back(it->first); 250 } 251 252 if (field->options().GetExtension(os::statsd::stateFieldOption).option() == 253 os::statsd::StateField::EXCLUSIVE) { 254 if (javaType == JAVA_TYPE_UNKNOWN || 255 javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || 256 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) { 257 errorCount++; 258 } 259 260 if (atomDecl->exclusiveField == 0) { 261 atomDecl->exclusiveField = it->first; 262 } else { 263 errorCount++; 264 } 265 } 266 267 if (field->options().GetExtension(os::statsd::is_uid) == true) { 268 if (javaType != JAVA_TYPE_INT) { 269 errorCount++; 270 } 271 272 if (atomDecl->uidField == 0) { 273 atomDecl->uidField = it->first; 274 } else { 275 errorCount++; 276 } 277 } 278 } 279 280 return errorCount; 281 } 282 283 // This function flattens the fields of the AttributionNode proto in an Atom proto and generates 284 // the corresponding atom decl and signature. 285 bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl, 286 vector<java_type_t> *signature) { 287 // Build a sorted list of the fields. Descriptor has them in source file 288 // order. 289 map<int, const FieldDescriptor *> fields; 290 for (int j = 0; j < atom->field_count(); j++) { 291 const FieldDescriptor *field = atom->field(j); 292 fields[field->number()] = field; 293 } 294 295 AtomDecl attributionDecl; 296 vector<java_type_t> attributionSignature; 297 collate_atom(android::os::statsd::AttributionNode::descriptor(), 298 &attributionDecl, &attributionSignature); 299 300 // Build the type signature and the atom data. 301 bool has_attribution_node = false; 302 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); 303 it != fields.end(); it++) { 304 const FieldDescriptor *field = it->second; 305 java_type_t javaType = java_type(field); 306 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { 307 atomDecl->fields.insert( 308 atomDecl->fields.end(), 309 attributionDecl.fields.begin(), attributionDecl.fields.end()); 310 signature->insert( 311 signature->end(), 312 attributionSignature.begin(), attributionSignature.end()); 313 has_attribution_node = true; 314 315 } else { 316 AtomField atField(field->name(), javaType); 317 if (javaType == JAVA_TYPE_ENUM) { 318 // All enums are treated as ints when it comes to function signatures. 319 signature->push_back(JAVA_TYPE_INT); 320 collate_enums(*field->enum_type(), &atField); 321 } else { 322 signature->push_back(javaType); 323 } 324 atomDecl->fields.push_back(atField); 325 } 326 } 327 return has_attribution_node; 328 } 329 330 /** 331 * Gather the info about the atoms. 332 */ 333 int collate_atoms(const Descriptor *descriptor, Atoms *atoms) { 334 int errorCount = 0; 335 const bool dbg = false; 336 337 for (int i = 0; i < descriptor->field_count(); i++) { 338 const FieldDescriptor *atomField = descriptor->field(i); 339 340 if (dbg) { 341 printf(" %s (%d)\n", atomField->name().c_str(), atomField->number()); 342 } 343 344 // StatsEvent only has one oneof, which contains only messages. Don't allow 345 // other types. 346 if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) { 347 print_error(atomField, 348 "Bad type for atom. StatsEvent can only have message type " 349 "fields: %s\n", 350 atomField->name().c_str()); 351 errorCount++; 352 continue; 353 } 354 355 const Descriptor *atom = atomField->message_type(); 356 AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name()); 357 vector<java_type_t> signature; 358 errorCount += collate_atom(atom, &atomDecl, &signature); 359 if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) { 360 errorCount++; 361 } 362 atoms->signatures.insert(signature); 363 atoms->decls.insert(atomDecl); 364 365 AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name()); 366 vector<java_type_t> nonChainedSignature; 367 if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) { 368 atoms->non_chained_signatures.insert(nonChainedSignature); 369 atoms->non_chained_decls.insert(nonChainedAtomDecl); 370 } 371 } 372 373 if (dbg) { 374 printf("signatures = [\n"); 375 for (set<vector<java_type_t>>::const_iterator it = 376 atoms->signatures.begin(); 377 it != atoms->signatures.end(); it++) { 378 printf(" "); 379 for (vector<java_type_t>::const_iterator jt = it->begin(); 380 jt != it->end(); jt++) { 381 printf(" %d", (int)*jt); 382 } 383 printf("\n"); 384 } 385 printf("]\n"); 386 } 387 388 return errorCount; 389 } 390 391 } // namespace stats_log_api_gen 392 } // namespace android 393