blob: ebdcdfdd6c500fb617dc76398eaca16ca9007ec0 [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;
Yao Chend54f9dd2017-10-17 17:37:48 +0000119 } 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:
Stefan Lafon9478f352017-10-30 21:20:20 -0700127 return JAVA_TYPE_ENUM;
Yao Chend54f9dd2017-10-17 17:37:48 +0000128 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/**
Yangster-macba5b9e42018-01-10 21:31:59 -0800142 * Gather the enums info.
143 */
144void 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/**
Yangster-mac7604aea2017-12-11 22:55:49 -0800152 * Gather the info about an atom proto.
153 */
154int 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);
Yangster-macba5b9e42018-01-10 21:31:59 -0800236 collate_enums(*field->enum_type(), &atField);
Yangster-mac7604aea2017-12-11 22:55:49 -0800237 } else {
238 signature->push_back(javaType);
239 }
240 atomDecl->fields.push_back(atField);
Yao Chen9c1debe2018-02-19 14:39:19 -0800241
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 }
Yao Chenc40a19d2018-03-15 16:48:25 -0700266
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 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800278 }
279
280 return errorCount;
281}
282
Yangster-macba5b9e42018-01-10 21:31:59 -0800283// This function flattens the fields of the AttributionNode proto in an Atom proto and generates
284// the corresponding atom decl and signature.
285bool 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
Yangster-mac7604aea2017-12-11 22:55:49 -0800330/**
Yao Chend54f9dd2017-10-17 17:37:48 +0000331 * Gather the info about the atoms.
332 */
Yangster-mac7604aea2017-12-11 22:55:49 -0800333int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
334 int errorCount = 0;
335 const bool dbg = false;
Yao Chend54f9dd2017-10-17 17:37:48 +0000336
Yangster-mac7604aea2017-12-11 22:55:49 -0800337 for (int i = 0; i < descriptor->field_count(); i++) {
338 const FieldDescriptor *atomField = descriptor->field(i);
Yao Chend54f9dd2017-10-17 17:37:48 +0000339
340 if (dbg) {
Yangster-mac7604aea2017-12-11 22:55:49 -0800341 printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
Yao Chend54f9dd2017-10-17 17:37:48 +0000342 }
343
Yangster-mac7604aea2017-12-11 22:55:49 -0800344 // 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);
Yao Chen9c1debe2018-02-19 14:39:19 -0800359 if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
360 errorCount++;
361 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800362 atoms->signatures.insert(signature);
363 atoms->decls.insert(atomDecl);
Yangster-macba5b9e42018-01-10 21:31:59 -0800364
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 }
Yangster-mac7604aea2017-12-11 22:55:49 -0800371 }
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;
Yao Chend54f9dd2017-10-17 17:37:48 +0000389}
390
391} // namespace stats_log_api_gen
392} // namespace android