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