blob: ab106d708748edf0882965d502e731ec548f73a3 [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),
49 exclusiveField(that.exclusiveField) {}
Yao Chend54f9dd2017-10-17 17:37:48 +000050
51AtomDecl::AtomDecl(int c, const string& n, const string& m)
52 :code(c),
53 name(n),
54 message(m)
55{
56}
57
58AtomDecl::~AtomDecl()
59{
60}
61
62
63/**
64 * Print an error message for a FieldDescriptor, including the file name and line number.
65 */
66static void
67print_error(const FieldDescriptor* field, const char* format, ...)
68{
69 const Descriptor* message = field->containing_type();
70 const FileDescriptor* file = message->file();
71
72 SourceLocation loc;
73 if (field->GetSourceLocation(&loc)) {
74 // TODO: this will work if we can figure out how to pass --include_source_info to protoc
75 fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
76 } else {
77 fprintf(stderr, "%s: ", file->name().c_str());
78 }
79 va_list args;
80 va_start(args, format);
81 vfprintf(stderr, format, args);
82 va_end (args);
83}
84
85/**
86 * Convert a protobuf type into a java type.
87 */
88static java_type_t
89java_type(const FieldDescriptor* field)
90{
91 int protoType = field->type();
92 switch (protoType) {
93 case FieldDescriptor::TYPE_DOUBLE:
94 return JAVA_TYPE_DOUBLE;
95 case FieldDescriptor::TYPE_FLOAT:
96 return JAVA_TYPE_FLOAT;
97 case FieldDescriptor::TYPE_INT64:
98 return JAVA_TYPE_LONG;
99 case FieldDescriptor::TYPE_UINT64:
100 return JAVA_TYPE_LONG;
101 case FieldDescriptor::TYPE_INT32:
102 return JAVA_TYPE_INT;
103 case FieldDescriptor::TYPE_FIXED64:
104 return JAVA_TYPE_LONG;
105 case FieldDescriptor::TYPE_FIXED32:
106 return JAVA_TYPE_INT;
107 case FieldDescriptor::TYPE_BOOL:
108 return JAVA_TYPE_BOOLEAN;
109 case FieldDescriptor::TYPE_STRING:
110 return JAVA_TYPE_STRING;
111 case FieldDescriptor::TYPE_GROUP:
112 return JAVA_TYPE_UNKNOWN;
113 case FieldDescriptor::TYPE_MESSAGE:
114 // TODO: not the final package name
Yangster-mac7604aea2017-12-11 22:55:49 -0800115 if (field->message_type()->full_name() ==
Yangster-mac20877162017-12-22 17:19:39 -0800116 "android.os.statsd.AttributionNode") {
Yangster-mac7604aea2017-12-11 22:55:49 -0800117 return JAVA_TYPE_ATTRIBUTION_CHAIN;
Yao Chend54f9dd2017-10-17 17:37:48 +0000118 } else {
119 return JAVA_TYPE_OBJECT;
120 }
121 case FieldDescriptor::TYPE_BYTES:
122 return JAVA_TYPE_BYTE_ARRAY;
123 case FieldDescriptor::TYPE_UINT32:
124 return JAVA_TYPE_INT;
125 case FieldDescriptor::TYPE_ENUM:
Stefan Lafon9478f352017-10-30 21:20:20 -0700126 return JAVA_TYPE_ENUM;
Yao Chend54f9dd2017-10-17 17:37:48 +0000127 case FieldDescriptor::TYPE_SFIXED32:
128 return JAVA_TYPE_INT;
129 case FieldDescriptor::TYPE_SFIXED64:
130 return JAVA_TYPE_LONG;
131 case FieldDescriptor::TYPE_SINT32:
132 return JAVA_TYPE_INT;
133 case FieldDescriptor::TYPE_SINT64:
134 return JAVA_TYPE_LONG;
135 default:
136 return JAVA_TYPE_UNKNOWN;
137 }
138}
139
140/**
Yangster-macba5b9e42018-01-10 21:31:59 -0800141 * Gather the enums info.
142 */
143void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) {
144 for (int i = 0; i < enumDescriptor.value_count(); i++) {
145 atomField->enumValues[enumDescriptor.value(i)->number()] =
146 enumDescriptor.value(i)->name().c_str();
147 }
148}
149
150/**
Yangster-mac7604aea2017-12-11 22:55:49 -0800151 * Gather the info about an atom proto.
152 */
153int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
154 vector<java_type_t> *signature) {
155
156 int errorCount = 0;
157 // Build a sorted list of the fields. Descriptor has them in source file
158 // order.
159 map<int, const FieldDescriptor *> fields;
160 for (int j = 0; j < atom->field_count(); j++) {
161 const FieldDescriptor *field = atom->field(j);
162 fields[field->number()] = field;
163 }
164
165 // Check that the parameters start at 1 and go up sequentially.
166 int expectedNumber = 1;
167 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
168 it != fields.end(); it++) {
169 const int number = it->first;
170 const FieldDescriptor *field = it->second;
171 if (number != expectedNumber) {
172 print_error(field,
173 "Fields must be numbered consecutively starting at 1:"
174 " '%s' is %d but should be %d\n",
175 field->name().c_str(), number, expectedNumber);
176 errorCount++;
177 expectedNumber = number;
178 continue;
179 }
180 expectedNumber++;
181 }
182
183 // Check that only allowed types are present. Remove any invalid ones.
184 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
185 it != fields.end(); it++) {
186 const FieldDescriptor *field = it->second;
187
188 java_type_t javaType = java_type(field);
189
190 if (javaType == JAVA_TYPE_UNKNOWN) {
191 print_error(field, "Unkown type for field: %s\n", field->name().c_str());
192 errorCount++;
193 continue;
194 } else if (javaType == JAVA_TYPE_OBJECT) {
195 // Allow attribution chain, but only at position 1.
196 print_error(field, "Message type not allowed for field: %s\n",
197 field->name().c_str());
198 errorCount++;
199 continue;
200 } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
201 print_error(field, "Raw bytes type not allowed for field: %s\n",
202 field->name().c_str());
203 errorCount++;
204 continue;
205 }
206 }
207
208 // Check that if there's an attribution chain, it's at position 1.
209 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
210 it != fields.end(); it++) {
211 int number = it->first;
212 if (number != 1) {
213 const FieldDescriptor *field = it->second;
214 java_type_t javaType = java_type(field);
215 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
216 print_error(
217 field,
218 "AttributionChain fields must have field id 1, in message: '%s'\n",
219 atom->name().c_str());
220 errorCount++;
221 }
222 }
223 }
224
225 // Build the type signature and the atom data.
226 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
227 it != fields.end(); it++) {
228 const FieldDescriptor *field = it->second;
229 java_type_t javaType = java_type(field);
230
231 AtomField atField(field->name(), javaType);
232 if (javaType == JAVA_TYPE_ENUM) {
233 // All enums are treated as ints when it comes to function signatures.
234 signature->push_back(JAVA_TYPE_INT);
Yangster-macba5b9e42018-01-10 21:31:59 -0800235 collate_enums(*field->enum_type(), &atField);
Yangster-mac7604aea2017-12-11 22:55:49 -0800236 } else {
237 signature->push_back(javaType);
238 }
239 atomDecl->fields.push_back(atField);
Yao Chen9c1debe2018-02-19 14:39:19 -0800240
241 if (field->options().GetExtension(os::statsd::stateFieldOption).option() ==
242 os::statsd::StateField::PRIMARY) {
243 if (javaType == JAVA_TYPE_UNKNOWN ||
244 javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
245 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
246 errorCount++;
247 }
248 atomDecl->primaryFields.push_back(it->first);
249 }
250
251 if (field->options().GetExtension(os::statsd::stateFieldOption).option() ==
252 os::statsd::StateField::EXCLUSIVE) {
253 if (javaType == JAVA_TYPE_UNKNOWN ||
254 javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
255 javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
256 errorCount++;
257 }
258
259 if (atomDecl->exclusiveField == 0) {
260 atomDecl->exclusiveField = it->first;
261 } else {
262 errorCount++;
263 }
264 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800265 }
266
267 return errorCount;
268}
269
Yangster-macba5b9e42018-01-10 21:31:59 -0800270// This function flattens the fields of the AttributionNode proto in an Atom proto and generates
271// the corresponding atom decl and signature.
272bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl,
273 vector<java_type_t> *signature) {
274 // Build a sorted list of the fields. Descriptor has them in source file
275 // order.
276 map<int, const FieldDescriptor *> fields;
277 for (int j = 0; j < atom->field_count(); j++) {
278 const FieldDescriptor *field = atom->field(j);
279 fields[field->number()] = field;
280 }
281
282 AtomDecl attributionDecl;
283 vector<java_type_t> attributionSignature;
284 collate_atom(android::os::statsd::AttributionNode::descriptor(),
285 &attributionDecl, &attributionSignature);
286
287 // Build the type signature and the atom data.
288 bool has_attribution_node = false;
289 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
290 it != fields.end(); it++) {
291 const FieldDescriptor *field = it->second;
292 java_type_t javaType = java_type(field);
293 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
294 atomDecl->fields.insert(
295 atomDecl->fields.end(),
296 attributionDecl.fields.begin(), attributionDecl.fields.end());
297 signature->insert(
298 signature->end(),
299 attributionSignature.begin(), attributionSignature.end());
300 has_attribution_node = true;
301
302 } else {
303 AtomField atField(field->name(), javaType);
304 if (javaType == JAVA_TYPE_ENUM) {
305 // All enums are treated as ints when it comes to function signatures.
306 signature->push_back(JAVA_TYPE_INT);
307 collate_enums(*field->enum_type(), &atField);
308 } else {
309 signature->push_back(javaType);
310 }
311 atomDecl->fields.push_back(atField);
312 }
313 }
314 return has_attribution_node;
315}
316
Yangster-mac7604aea2017-12-11 22:55:49 -0800317/**
Yao Chend54f9dd2017-10-17 17:37:48 +0000318 * Gather the info about the atoms.
319 */
Yangster-mac7604aea2017-12-11 22:55:49 -0800320int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
321 int errorCount = 0;
322 const bool dbg = false;
Yao Chend54f9dd2017-10-17 17:37:48 +0000323
Yangster-mac7604aea2017-12-11 22:55:49 -0800324 for (int i = 0; i < descriptor->field_count(); i++) {
325 const FieldDescriptor *atomField = descriptor->field(i);
Yao Chend54f9dd2017-10-17 17:37:48 +0000326
327 if (dbg) {
Yangster-mac7604aea2017-12-11 22:55:49 -0800328 printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
Yao Chend54f9dd2017-10-17 17:37:48 +0000329 }
330
Yangster-mac7604aea2017-12-11 22:55:49 -0800331 // StatsEvent only has one oneof, which contains only messages. Don't allow
332 // other types.
333 if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
334 print_error(atomField,
335 "Bad type for atom. StatsEvent can only have message type "
336 "fields: %s\n",
337 atomField->name().c_str());
338 errorCount++;
339 continue;
340 }
341
342 const Descriptor *atom = atomField->message_type();
343 AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
344 vector<java_type_t> signature;
345 errorCount += collate_atom(atom, &atomDecl, &signature);
Yao Chen9c1debe2018-02-19 14:39:19 -0800346 if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
347 errorCount++;
348 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800349 atoms->signatures.insert(signature);
350 atoms->decls.insert(atomDecl);
Yangster-macba5b9e42018-01-10 21:31:59 -0800351
352 AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
353 vector<java_type_t> nonChainedSignature;
354 if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
355 atoms->non_chained_signatures.insert(nonChainedSignature);
356 atoms->non_chained_decls.insert(nonChainedAtomDecl);
357 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800358 }
359
360 if (dbg) {
361 printf("signatures = [\n");
362 for (set<vector<java_type_t>>::const_iterator it =
363 atoms->signatures.begin();
364 it != atoms->signatures.end(); it++) {
365 printf(" ");
366 for (vector<java_type_t>::const_iterator jt = it->begin();
367 jt != it->end(); jt++) {
368 printf(" %d", (int)*jt);
369 }
370 printf("\n");
371 }
372 printf("]\n");
373 }
374
375 return errorCount;
Yao Chend54f9dd2017-10-17 17:37:48 +0000376}
377
378} // namespace stats_log_api_gen
379} // namespace android