blob: 4245700ed90ddc1ba8e44e169f1f1aedcca69cc7 [file] [log] [blame]
Yao Chend54f9dd2017-10-17 17:37:48 +00001/*
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"
Yangster-macba5b9e42018-01-10 21:31:59 -080018#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
Yao Chend54f9dd2017-10-17 17:37:48 +000019
20#include <stdio.h>
21#include <map>
22
23namespace android {
24namespace stats_log_api_gen {
25
Stefan Lafon9478f352017-10-30 21:20:20 -070026using google::protobuf::EnumDescriptor;
Yao Chend54f9dd2017-10-17 17:37:48 +000027using google::protobuf::FieldDescriptor;
28using google::protobuf::FileDescriptor;
29using google::protobuf::SourceLocation;
30using std::map;
31
32
33//
34// AtomDecl class
35//
36
37AtomDecl::AtomDecl()
38 :code(0),
39 name()
40{
41}
42
43AtomDecl::AtomDecl(const AtomDecl& that)
Yao Chen9c1debe2018-02-19 14:39:19 -080044 : code(that.code),
45 name(that.name),
46 message(that.message),
47 fields(that.fields),
48 primaryFields(that.primaryFields),
Yao Chenc40a19d2018-03-15 16:48:25 -070049 exclusiveField(that.exclusiveField),
50 uidField(that.uidField) {}
Yao Chend54f9dd2017-10-17 17:37:48 +000051
52AtomDecl::AtomDecl(int c, const string& n, const string& m)
53 :code(c),
54 name(n),
55 message(m)
56{
57}
58
59AtomDecl::~AtomDecl()
60{
61}
62
63
64/**
65 * Print an error message for a FieldDescriptor, including the file name and line number.
66 */
67static void
68print_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 */
89static java_type_t
90java_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
Yangster-mac7604aea2017-12-11 22:55:49 -0800116 if (field->message_type()->full_name() ==
Yangster-mac20877162017-12-22 17:19:39 -0800117 "android.os.statsd.AttributionNode") {
Yangster-mac7604aea2017-12-11 22:55:49 -0800118 return JAVA_TYPE_ATTRIBUTION_CHAIN;
Yangster-mac48b3d622018-08-18 12:38:11 -0700119 } else if (field->message_type()->full_name() ==
120 "android.os.statsd.KeyValuePair") {
121 return JAVA_TYPE_KEY_VALUE_PAIR;
Yao Chend54f9dd2017-10-17 17:37:48 +0000122 } else {
123 return JAVA_TYPE_OBJECT;
124 }
125 case FieldDescriptor::TYPE_BYTES:
126 return JAVA_TYPE_BYTE_ARRAY;
127 case FieldDescriptor::TYPE_UINT32:
128 return JAVA_TYPE_INT;
129 case FieldDescriptor::TYPE_ENUM:
Stefan Lafon9478f352017-10-30 21:20:20 -0700130 return JAVA_TYPE_ENUM;
Yao Chend54f9dd2017-10-17 17:37:48 +0000131 case FieldDescriptor::TYPE_SFIXED32:
132 return JAVA_TYPE_INT;
133 case FieldDescriptor::TYPE_SFIXED64:
134 return JAVA_TYPE_LONG;
135 case FieldDescriptor::TYPE_SINT32:
136 return JAVA_TYPE_INT;
137 case FieldDescriptor::TYPE_SINT64:
138 return JAVA_TYPE_LONG;
139 default:
140 return JAVA_TYPE_UNKNOWN;
141 }
142}
143
144/**
Yangster-macba5b9e42018-01-10 21:31:59 -0800145 * Gather the enums info.
146 */
147void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) {
148 for (int i = 0; i < enumDescriptor.value_count(); i++) {
149 atomField->enumValues[enumDescriptor.value(i)->number()] =
150 enumDescriptor.value(i)->name().c_str();
151 }
152}
153
154/**
Yangster-mac7604aea2017-12-11 22:55:49 -0800155 * Gather the info about an atom proto.
156 */
157int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
158 vector<java_type_t> *signature) {
159
160 int errorCount = 0;
161 // Build a sorted list of the fields. Descriptor has them in source file
162 // order.
163 map<int, const FieldDescriptor *> fields;
164 for (int j = 0; j < atom->field_count(); j++) {
165 const FieldDescriptor *field = atom->field(j);
166 fields[field->number()] = field;
167 }
168
169 // Check that the parameters start at 1 and go up sequentially.
170 int expectedNumber = 1;
171 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
172 it != fields.end(); it++) {
173 const int number = it->first;
174 const FieldDescriptor *field = it->second;
175 if (number != expectedNumber) {
176 print_error(field,
177 "Fields must be numbered consecutively starting at 1:"
178 " '%s' is %d but should be %d\n",
179 field->name().c_str(), number, expectedNumber);
180 errorCount++;
181 expectedNumber = number;
182 continue;
183 }
184 expectedNumber++;
185 }
186
187 // Check that only allowed types are present. Remove any invalid ones.
188 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
189 it != fields.end(); it++) {
190 const FieldDescriptor *field = it->second;
191
192 java_type_t javaType = java_type(field);
193
194 if (javaType == JAVA_TYPE_UNKNOWN) {
195 print_error(field, "Unkown type for field: %s\n", field->name().c_str());
196 errorCount++;
197 continue;
Chenjie Yu159e4f82018-08-29 11:49:11 -0700198 } else if (javaType == JAVA_TYPE_OBJECT &&
199 atomDecl->code < PULL_ATOM_START_ID) {
Yangster-mac7604aea2017-12-11 22:55:49 -0800200 // Allow attribution chain, but only at position 1.
Chenjie Yu159e4f82018-08-29 11:49:11 -0700201 print_error(field,
202 "Message type not allowed for field in pushed atoms: %s\n",
Yangster-mac7604aea2017-12-11 22:55:49 -0800203 field->name().c_str());
204 errorCount++;
205 continue;
206 } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
207 print_error(field, "Raw bytes type not allowed for field: %s\n",
208 field->name().c_str());
209 errorCount++;
210 continue;
211 }
212 }
213
214 // Check that if there's an attribution chain, it's at position 1.
215 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
216 it != fields.end(); it++) {
217 int number = it->first;
218 if (number != 1) {
219 const FieldDescriptor *field = it->second;
220 java_type_t javaType = java_type(field);
221 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
222 print_error(
223 field,
224 "AttributionChain fields must have field id 1, in message: '%s'\n",
225 atom->name().c_str());
226 errorCount++;
227 }
228 }
229 }
230
231 // Build the type signature and the atom data.
232 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
233 it != fields.end(); it++) {
234 const FieldDescriptor *field = it->second;
235 java_type_t javaType = java_type(field);
236
237 AtomField atField(field->name(), javaType);
Chenjie Yu159e4f82018-08-29 11:49:11 -0700238 // Generate signature for pushed atoms
239 if (atomDecl->code < PULL_ATOM_START_ID) {
240 if (javaType == JAVA_TYPE_ENUM) {
241 // All enums are treated as ints when it comes to function signatures.
242 signature->push_back(JAVA_TYPE_INT);
243 collate_enums(*field->enum_type(), &atField);
244 } else {
245 signature->push_back(javaType);
246 }
247 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800248 if (javaType == JAVA_TYPE_ENUM) {
249 // All enums are treated as ints when it comes to function signatures.
Yangster-macba5b9e42018-01-10 21:31:59 -0800250 collate_enums(*field->enum_type(), &atField);
Yangster-mac7604aea2017-12-11 22:55:49 -0800251 }
252 atomDecl->fields.push_back(atField);
Yao Chen9c1debe2018-02-19 14:39:19 -0800253
254 if (field->options().GetExtension(os::statsd::stateFieldOption).option() ==
255 os::statsd::StateField::PRIMARY) {
256 if (javaType == JAVA_TYPE_UNKNOWN ||
257 javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
258 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
259 errorCount++;
260 }
261 atomDecl->primaryFields.push_back(it->first);
262 }
263
264 if (field->options().GetExtension(os::statsd::stateFieldOption).option() ==
265 os::statsd::StateField::EXCLUSIVE) {
266 if (javaType == JAVA_TYPE_UNKNOWN ||
267 javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
268 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
269 errorCount++;
270 }
271
272 if (atomDecl->exclusiveField == 0) {
273 atomDecl->exclusiveField = it->first;
274 } else {
275 errorCount++;
276 }
277 }
Yao Chenc40a19d2018-03-15 16:48:25 -0700278
279 if (field->options().GetExtension(os::statsd::is_uid) == true) {
280 if (javaType != JAVA_TYPE_INT) {
281 errorCount++;
282 }
283
284 if (atomDecl->uidField == 0) {
285 atomDecl->uidField = it->first;
286 } else {
287 errorCount++;
288 }
289 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800290 }
291
292 return errorCount;
293}
294
Yangster-macba5b9e42018-01-10 21:31:59 -0800295// This function flattens the fields of the AttributionNode proto in an Atom proto and generates
296// the corresponding atom decl and signature.
297bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl,
298 vector<java_type_t> *signature) {
299 // Build a sorted list of the fields. Descriptor has them in source file
300 // order.
301 map<int, const FieldDescriptor *> fields;
302 for (int j = 0; j < atom->field_count(); j++) {
303 const FieldDescriptor *field = atom->field(j);
304 fields[field->number()] = field;
305 }
306
307 AtomDecl attributionDecl;
308 vector<java_type_t> attributionSignature;
309 collate_atom(android::os::statsd::AttributionNode::descriptor(),
310 &attributionDecl, &attributionSignature);
311
312 // Build the type signature and the atom data.
313 bool has_attribution_node = false;
314 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
315 it != fields.end(); it++) {
316 const FieldDescriptor *field = it->second;
317 java_type_t javaType = java_type(field);
318 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
319 atomDecl->fields.insert(
320 atomDecl->fields.end(),
321 attributionDecl.fields.begin(), attributionDecl.fields.end());
322 signature->insert(
323 signature->end(),
324 attributionSignature.begin(), attributionSignature.end());
325 has_attribution_node = true;
326
327 } else {
328 AtomField atField(field->name(), javaType);
329 if (javaType == JAVA_TYPE_ENUM) {
330 // All enums are treated as ints when it comes to function signatures.
331 signature->push_back(JAVA_TYPE_INT);
332 collate_enums(*field->enum_type(), &atField);
333 } else {
334 signature->push_back(javaType);
335 }
336 atomDecl->fields.push_back(atField);
337 }
338 }
339 return has_attribution_node;
340}
341
Yangster-mac7604aea2017-12-11 22:55:49 -0800342/**
Yao Chend54f9dd2017-10-17 17:37:48 +0000343 * Gather the info about the atoms.
344 */
Yangster-mac7604aea2017-12-11 22:55:49 -0800345int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
346 int errorCount = 0;
347 const bool dbg = false;
Yao Chend54f9dd2017-10-17 17:37:48 +0000348
Yangster-mac7604aea2017-12-11 22:55:49 -0800349 for (int i = 0; i < descriptor->field_count(); i++) {
350 const FieldDescriptor *atomField = descriptor->field(i);
Yao Chend54f9dd2017-10-17 17:37:48 +0000351
352 if (dbg) {
Yangster-mac7604aea2017-12-11 22:55:49 -0800353 printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
Yao Chend54f9dd2017-10-17 17:37:48 +0000354 }
355
Yangster-mac7604aea2017-12-11 22:55:49 -0800356 // StatsEvent only has one oneof, which contains only messages. Don't allow
357 // other types.
358 if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
359 print_error(atomField,
360 "Bad type for atom. StatsEvent can only have message type "
361 "fields: %s\n",
362 atomField->name().c_str());
363 errorCount++;
364 continue;
365 }
366
367 const Descriptor *atom = atomField->message_type();
368 AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
369 vector<java_type_t> signature;
370 errorCount += collate_atom(atom, &atomDecl, &signature);
Yao Chen9c1debe2018-02-19 14:39:19 -0800371 if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
372 errorCount++;
373 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800374 atoms->signatures.insert(signature);
375 atoms->decls.insert(atomDecl);
Yangster-macba5b9e42018-01-10 21:31:59 -0800376
377 AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
378 vector<java_type_t> nonChainedSignature;
379 if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
380 atoms->non_chained_signatures.insert(nonChainedSignature);
381 atoms->non_chained_decls.insert(nonChainedAtomDecl);
382 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800383 }
384
385 if (dbg) {
386 printf("signatures = [\n");
387 for (set<vector<java_type_t>>::const_iterator it =
388 atoms->signatures.begin();
389 it != atoms->signatures.end(); it++) {
390 printf(" ");
391 for (vector<java_type_t>::const_iterator jt = it->begin();
392 jt != it->end(); jt++) {
393 printf(" %d", (int)*jt);
394 }
395 printf("\n");
396 }
397 printf("]\n");
398 }
399
400 return errorCount;
Yao Chend54f9dd2017-10-17 17:37:48 +0000401}
402
403} // namespace stats_log_api_gen
404} // namespace android