Home | History | Annotate | Download | only in stats_log_api_gen
      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