blob: 80853b13a7ccb254a9396d157864ef87f0b03140 [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"
18
19#include <stdio.h>
20#include <map>
21
22namespace android {
23namespace stats_log_api_gen {
24
Stefan Lafon9478f352017-10-30 21:20:20 -070025using google::protobuf::EnumDescriptor;
Yao Chend54f9dd2017-10-17 17:37:48 +000026using google::protobuf::FieldDescriptor;
27using google::protobuf::FileDescriptor;
28using google::protobuf::SourceLocation;
29using std::map;
30
31
32//
33// AtomDecl class
34//
35
36AtomDecl::AtomDecl()
37 :code(0),
38 name()
39{
40}
41
42AtomDecl::AtomDecl(const AtomDecl& that)
43 :code(that.code),
44 name(that.name),
45 message(that.message),
46 fields(that.fields)
47{
48}
49
50AtomDecl::AtomDecl(int c, const string& n, const string& m)
51 :code(c),
52 name(n),
53 message(m)
54{
55}
56
57AtomDecl::~AtomDecl()
58{
59}
60
61
62/**
63 * Print an error message for a FieldDescriptor, including the file name and line number.
64 */
65static void
66print_error(const FieldDescriptor* field, const char* format, ...)
67{
68 const Descriptor* message = field->containing_type();
69 const FileDescriptor* file = message->file();
70
71 SourceLocation loc;
72 if (field->GetSourceLocation(&loc)) {
73 // TODO: this will work if we can figure out how to pass --include_source_info to protoc
74 fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
75 } else {
76 fprintf(stderr, "%s: ", file->name().c_str());
77 }
78 va_list args;
79 va_start(args, format);
80 vfprintf(stderr, format, args);
81 va_end (args);
82}
83
84/**
85 * Convert a protobuf type into a java type.
86 */
87static java_type_t
88java_type(const FieldDescriptor* field)
89{
90 int protoType = field->type();
91 switch (protoType) {
92 case FieldDescriptor::TYPE_DOUBLE:
93 return JAVA_TYPE_DOUBLE;
94 case FieldDescriptor::TYPE_FLOAT:
95 return JAVA_TYPE_FLOAT;
96 case FieldDescriptor::TYPE_INT64:
97 return JAVA_TYPE_LONG;
98 case FieldDescriptor::TYPE_UINT64:
99 return JAVA_TYPE_LONG;
100 case FieldDescriptor::TYPE_INT32:
101 return JAVA_TYPE_INT;
102 case FieldDescriptor::TYPE_FIXED64:
103 return JAVA_TYPE_LONG;
104 case FieldDescriptor::TYPE_FIXED32:
105 return JAVA_TYPE_INT;
106 case FieldDescriptor::TYPE_BOOL:
107 return JAVA_TYPE_BOOLEAN;
108 case FieldDescriptor::TYPE_STRING:
109 return JAVA_TYPE_STRING;
110 case FieldDescriptor::TYPE_GROUP:
111 return JAVA_TYPE_UNKNOWN;
112 case FieldDescriptor::TYPE_MESSAGE:
113 // TODO: not the final package name
Yangster-mac7604aea2017-12-11 22:55:49 -0800114 if (field->message_type()->full_name() ==
Yangster-mac20877162017-12-22 17:19:39 -0800115 "android.os.statsd.AttributionNode") {
Yangster-mac7604aea2017-12-11 22:55:49 -0800116 return JAVA_TYPE_ATTRIBUTION_CHAIN;
Yao Chend54f9dd2017-10-17 17:37:48 +0000117 } else {
118 return JAVA_TYPE_OBJECT;
119 }
120 case FieldDescriptor::TYPE_BYTES:
121 return JAVA_TYPE_BYTE_ARRAY;
122 case FieldDescriptor::TYPE_UINT32:
123 return JAVA_TYPE_INT;
124 case FieldDescriptor::TYPE_ENUM:
Stefan Lafon9478f352017-10-30 21:20:20 -0700125 return JAVA_TYPE_ENUM;
Yao Chend54f9dd2017-10-17 17:37:48 +0000126 case FieldDescriptor::TYPE_SFIXED32:
127 return JAVA_TYPE_INT;
128 case FieldDescriptor::TYPE_SFIXED64:
129 return JAVA_TYPE_LONG;
130 case FieldDescriptor::TYPE_SINT32:
131 return JAVA_TYPE_INT;
132 case FieldDescriptor::TYPE_SINT64:
133 return JAVA_TYPE_LONG;
134 default:
135 return JAVA_TYPE_UNKNOWN;
136 }
137}
138
139/**
Yangster-mac7604aea2017-12-11 22:55:49 -0800140 * Gather the info about an atom proto.
141 */
142int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
143 vector<java_type_t> *signature) {
144
145 int errorCount = 0;
146 // Build a sorted list of the fields. Descriptor has them in source file
147 // order.
148 map<int, const FieldDescriptor *> fields;
149 for (int j = 0; j < atom->field_count(); j++) {
150 const FieldDescriptor *field = atom->field(j);
151 fields[field->number()] = field;
152 }
153
154 // Check that the parameters start at 1 and go up sequentially.
155 int expectedNumber = 1;
156 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
157 it != fields.end(); it++) {
158 const int number = it->first;
159 const FieldDescriptor *field = it->second;
160 if (number != expectedNumber) {
161 print_error(field,
162 "Fields must be numbered consecutively starting at 1:"
163 " '%s' is %d but should be %d\n",
164 field->name().c_str(), number, expectedNumber);
165 errorCount++;
166 expectedNumber = number;
167 continue;
168 }
169 expectedNumber++;
170 }
171
172 // Check that only allowed types are present. Remove any invalid ones.
173 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
174 it != fields.end(); it++) {
175 const FieldDescriptor *field = it->second;
176
177 java_type_t javaType = java_type(field);
178
179 if (javaType == JAVA_TYPE_UNKNOWN) {
180 print_error(field, "Unkown type for field: %s\n", field->name().c_str());
181 errorCount++;
182 continue;
183 } else if (javaType == JAVA_TYPE_OBJECT) {
184 // Allow attribution chain, but only at position 1.
185 print_error(field, "Message type not allowed for field: %s\n",
186 field->name().c_str());
187 errorCount++;
188 continue;
189 } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
190 print_error(field, "Raw bytes type not allowed for field: %s\n",
191 field->name().c_str());
192 errorCount++;
193 continue;
194 }
195 }
196
197 // Check that if there's an attribution chain, it's at position 1.
198 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
199 it != fields.end(); it++) {
200 int number = it->first;
201 if (number != 1) {
202 const FieldDescriptor *field = it->second;
203 java_type_t javaType = java_type(field);
204 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
205 print_error(
206 field,
207 "AttributionChain fields must have field id 1, in message: '%s'\n",
208 atom->name().c_str());
209 errorCount++;
210 }
211 }
212 }
213
214 // Build the type signature and the atom data.
215 for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
216 it != fields.end(); it++) {
217 const FieldDescriptor *field = it->second;
218 java_type_t javaType = java_type(field);
219
220 AtomField atField(field->name(), javaType);
221 if (javaType == JAVA_TYPE_ENUM) {
222 // All enums are treated as ints when it comes to function signatures.
223 signature->push_back(JAVA_TYPE_INT);
224 const EnumDescriptor *enumDescriptor = field->enum_type();
225 for (int i = 0; i < enumDescriptor->value_count(); i++) {
226 atField.enumValues[enumDescriptor->value(i)->number()] =
227 enumDescriptor->value(i)->name().c_str();
228 }
229 } else {
230 signature->push_back(javaType);
231 }
232 atomDecl->fields.push_back(atField);
233 }
234
235 return errorCount;
236}
237
238/**
Yao Chend54f9dd2017-10-17 17:37:48 +0000239 * Gather the info about the atoms.
240 */
Yangster-mac7604aea2017-12-11 22:55:49 -0800241int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
242 int errorCount = 0;
243 const bool dbg = false;
Yao Chend54f9dd2017-10-17 17:37:48 +0000244
Yangster-mac7604aea2017-12-11 22:55:49 -0800245 for (int i = 0; i < descriptor->field_count(); i++) {
246 const FieldDescriptor *atomField = descriptor->field(i);
Yao Chend54f9dd2017-10-17 17:37:48 +0000247
248 if (dbg) {
Yangster-mac7604aea2017-12-11 22:55:49 -0800249 printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
Yao Chend54f9dd2017-10-17 17:37:48 +0000250 }
251
Yangster-mac7604aea2017-12-11 22:55:49 -0800252 // StatsEvent only has one oneof, which contains only messages. Don't allow
253 // other types.
254 if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
255 print_error(atomField,
256 "Bad type for atom. StatsEvent can only have message type "
257 "fields: %s\n",
258 atomField->name().c_str());
259 errorCount++;
260 continue;
261 }
262
263 const Descriptor *atom = atomField->message_type();
264 AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
265 vector<java_type_t> signature;
266 errorCount += collate_atom(atom, &atomDecl, &signature);
267 atoms->signatures.insert(signature);
268 atoms->decls.insert(atomDecl);
269 }
270
271 if (dbg) {
272 printf("signatures = [\n");
273 for (set<vector<java_type_t>>::const_iterator it =
274 atoms->signatures.begin();
275 it != atoms->signatures.end(); it++) {
276 printf(" ");
277 for (vector<java_type_t>::const_iterator jt = it->begin();
278 jt != it->end(); jt++) {
279 printf(" %d", (int)*jt);
280 }
281 printf("\n");
282 }
283 printf("]\n");
284 }
285
286 return errorCount;
Yao Chend54f9dd2017-10-17 17:37:48 +0000287}
288
289} // namespace stats_log_api_gen
290} // namespace android