• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AtomDecl()37 AtomDecl::AtomDecl()
38     :code(0),
39      name()
40 {
41 }
42 
AtomDecl(const AtomDecl & that)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 
AtomDecl(int c,const string & n,const string & m)52 AtomDecl::AtomDecl(int c, const string& n, const string& m)
53     :code(c),
54      name(n),
55      message(m)
56 {
57 }
58 
~AtomDecl()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
print_error(const FieldDescriptor * field,const char * format,...)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
java_type(const FieldDescriptor * field)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  */
collate_enums(const EnumDescriptor & enumDescriptor,AtomField * atomField)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  */
collate_atom(const Descriptor * atom,AtomDecl * atomDecl,vector<java_type_t> * signature)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.
get_non_chained_node(const Descriptor * atom,AtomDecl * atomDecl,vector<java_type_t> * 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  */
collate_atoms(const Descriptor * descriptor,Atoms * atoms)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