1 /*
2 * Copyright (C) 2019 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 "stats_event.h"
18 #include <gtest/gtest.h>
19 #include <utils/SystemClock.h>
20
21 // Keep in sync with stats_event.c. Consider moving to separate header file to avoid duplication.
22 /* ERRORS */
23 #define ERROR_NO_TIMESTAMP 0x1
24 #define ERROR_NO_ATOM_ID 0x2
25 #define ERROR_OVERFLOW 0x4
26 #define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
27 #define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
28 #define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
29 #define ERROR_INVALID_ANNOTATION_ID 0x40
30 #define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
31 #define ERROR_TOO_MANY_ANNOTATIONS 0x100
32 #define ERROR_TOO_MANY_FIELDS 0x200
33 #define ERROR_INVALID_VALUE_TYPE 0x400
34 #define ERROR_STRING_NOT_NULL_TERMINATED 0x800
35 #define ERROR_ATOM_ID_INVALID_POSITION 0x2000
36
37 /* TYPE IDS */
38 #define INT32_TYPE 0x00
39 #define INT64_TYPE 0x01
40 #define STRING_TYPE 0x02
41 #define LIST_TYPE 0x03
42 #define FLOAT_TYPE 0x04
43 #define BOOL_TYPE 0x05
44 #define BYTE_ARRAY_TYPE 0x06
45 #define OBJECT_TYPE 0x07
46 #define KEY_VALUE_PAIRS_TYPE 0x08
47 #define ATTRIBUTION_CHAIN_TYPE 0x09
48 #define ERROR_TYPE 0x0F
49
50 using std::string;
51 using std::vector;
52
53 // Side-effect: this function moves the start of the buffer past the read value
54 template <class T>
readNext(uint8_t ** buffer)55 T readNext(uint8_t** buffer) {
56 T value;
57 if ((reinterpret_cast<uintptr_t>(*buffer) % alignof(T)) == 0) {
58 value = *(T*)(*buffer);
59 } else {
60 memcpy(&value, *buffer, sizeof(T));
61 }
62 *buffer += sizeof(T);
63 return value;
64 }
65
checkTypeHeader(uint8_t ** buffer,uint8_t typeId,uint8_t numAnnotations=0)66 void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
67 uint8_t typeHeader = (numAnnotations << 4) | typeId;
68 EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
69 }
70
71 template <class T>
checkScalar(uint8_t ** buffer,T expectedValue)72 void checkScalar(uint8_t** buffer, T expectedValue) {
73 EXPECT_EQ(readNext<T>(buffer), expectedValue);
74 }
75
checkString(uint8_t ** buffer,const string & expectedString)76 void checkString(uint8_t** buffer, const string& expectedString) {
77 uint32_t size = readNext<uint32_t>(buffer);
78 string parsedString((char*)(*buffer), size);
79 EXPECT_EQ(parsedString, expectedString);
80 *buffer += size; // move buffer past string we just read
81 }
82
checkByteArray(uint8_t ** buffer,const vector<uint8_t> & expectedByteArray)83 void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
84 uint32_t size = readNext<uint32_t>(buffer);
85 vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
86 EXPECT_EQ(parsedByteArray, expectedByteArray);
87 *buffer += size; // move buffer past byte array we just read
88 }
89
90 template <class T>
checkAnnotation(uint8_t ** buffer,uint8_t annotationId,uint8_t typeId,T annotationValue)91 void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
92 EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
93 EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
94 checkScalar<T>(buffer, annotationValue);
95 }
96
checkMetadata(uint8_t ** buffer,uint8_t numElements,int64_t startTime,int64_t endTime,uint32_t atomId,uint8_t numAtomLevelAnnotations=0)97 void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
98 uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) {
99 // All events start with OBJECT_TYPE id.
100 checkTypeHeader(buffer, OBJECT_TYPE);
101
102 // We increment by 2 because the number of elements listed in the
103 // serialization accounts for the timestamp and atom id as well.
104 checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
105
106 // Check timestamp
107 checkTypeHeader(buffer, INT64_TYPE);
108 int64_t timestamp = readNext<int64_t>(buffer);
109 EXPECT_GE(timestamp, startTime);
110 EXPECT_LE(timestamp, endTime);
111
112 // Check atom id
113 checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations);
114 checkScalar(buffer, atomId);
115 }
116
TEST(StatsEventTest,TestScalars)117 TEST(StatsEventTest, TestScalars) {
118 uint32_t atomId = 100;
119 int32_t int32Value = -5;
120 int64_t int64Value = -2 * android::elapsedRealtimeNano();
121 float floatValue = 2.0;
122 bool boolValue = false;
123
124 int64_t startTime = android::elapsedRealtimeNano();
125 AStatsEvent* event = AStatsEvent_obtain();
126 AStatsEvent_setAtomId(event, atomId);
127 AStatsEvent_writeInt32(event, int32Value);
128 AStatsEvent_writeInt64(event, int64Value);
129 AStatsEvent_writeFloat(event, floatValue);
130 AStatsEvent_writeBool(event, boolValue);
131 AStatsEvent_build(event);
132 int64_t endTime = android::elapsedRealtimeNano();
133
134 size_t bufferSize;
135 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
136 uint8_t* bufferEnd = buffer + bufferSize;
137
138 checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
139
140 // check int32 element
141 checkTypeHeader(&buffer, INT32_TYPE);
142 checkScalar(&buffer, int32Value);
143
144 // check int64 element
145 checkTypeHeader(&buffer, INT64_TYPE);
146 checkScalar(&buffer, int64Value);
147
148 // check float element
149 checkTypeHeader(&buffer, FLOAT_TYPE);
150 checkScalar(&buffer, floatValue);
151
152 // check bool element
153 checkTypeHeader(&buffer, BOOL_TYPE);
154 checkScalar(&buffer, boolValue);
155
156 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
157 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
158 AStatsEvent_release(event);
159 }
160
TEST(StatsEventTest,TestStrings)161 TEST(StatsEventTest, TestStrings) {
162 uint32_t atomId = 100;
163 string str = "test_string";
164
165 int64_t startTime = android::elapsedRealtimeNano();
166 AStatsEvent* event = AStatsEvent_obtain();
167 AStatsEvent_setAtomId(event, atomId);
168 AStatsEvent_writeString(event, str.c_str());
169 AStatsEvent_build(event);
170 int64_t endTime = android::elapsedRealtimeNano();
171
172 size_t bufferSize;
173 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
174 uint8_t* bufferEnd = buffer + bufferSize;
175
176 checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
177
178 checkTypeHeader(&buffer, STRING_TYPE);
179 checkString(&buffer, str);
180
181 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
182 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
183 AStatsEvent_release(event);
184 }
185
TEST(StatsEventTest,TestNullString)186 TEST(StatsEventTest, TestNullString) {
187 uint32_t atomId = 100;
188 char* str = nullptr;
189
190 int64_t startTime = android::elapsedRealtimeNano();
191 AStatsEvent* event = AStatsEvent_obtain();
192 AStatsEvent_setAtomId(event, atomId);
193 AStatsEvent_writeString(event, str);
194 AStatsEvent_build(event);
195 int64_t endTime = android::elapsedRealtimeNano();
196
197 size_t bufferSize;
198 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
199 uint8_t* bufferEnd = buffer + bufferSize;
200
201 checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
202
203 checkTypeHeader(&buffer, STRING_TYPE);
204 checkString(&buffer, "");
205
206 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
207 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
208 AStatsEvent_release(event);
209 }
210
TEST(StatsEventTest,TestByteArrays)211 TEST(StatsEventTest, TestByteArrays) {
212 uint32_t atomId = 100;
213 vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
214
215 int64_t startTime = android::elapsedRealtimeNano();
216 AStatsEvent* event = AStatsEvent_obtain();
217 AStatsEvent_setAtomId(event, atomId);
218 AStatsEvent_writeByteArray(event, message.data(), message.size());
219 AStatsEvent_build(event);
220 int64_t endTime = android::elapsedRealtimeNano();
221
222 size_t bufferSize;
223 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
224 uint8_t* bufferEnd = buffer + bufferSize;
225
226 checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
227
228 checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
229 checkByteArray(&buffer, message);
230
231 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
232 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
233 AStatsEvent_release(event);
234 }
235
TEST(StatsEventTest,TestNullByteArrays)236 TEST(StatsEventTest, TestNullByteArrays) {
237 uint32_t atomId = 100;
238 uint8_t* buf = nullptr;
239 vector<uint8_t> message;
240
241 int64_t startTime = android::elapsedRealtimeNano();
242 AStatsEvent* event = AStatsEvent_obtain();
243 AStatsEvent_setAtomId(event, atomId);
244 AStatsEvent_writeByteArray(event, buf, 2);
245 AStatsEvent_build(event);
246 int64_t endTime = android::elapsedRealtimeNano();
247
248 size_t bufferSize;
249 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
250 uint8_t* bufferEnd = buffer + bufferSize;
251
252 checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
253
254 checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
255 checkByteArray(&buffer, message);
256
257 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
258 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
259 AStatsEvent_release(event);
260 }
261
TEST(StatsEventTest,TestAttributionChains)262 TEST(StatsEventTest, TestAttributionChains) {
263 uint32_t atomId = 100;
264
265 uint8_t numNodes = 50;
266 uint32_t uids[numNodes];
267 vector<string> tags(numNodes); // storage that cTag elements point to
268 const char* cTags[numNodes];
269 for (int i = 0; i < (int)numNodes; i++) {
270 uids[i] = i;
271 if (0 == i) {
272 tags.push_back("");
273 cTags[i] = nullptr;
274 } else {
275 tags.push_back("test" + std::to_string(i));
276 cTags[i] = tags[i].c_str();
277 }
278 }
279
280 int64_t startTime = android::elapsedRealtimeNano();
281 AStatsEvent* event = AStatsEvent_obtain();
282 AStatsEvent_setAtomId(event, atomId);
283 AStatsEvent_writeAttributionChain(event, uids, cTags, numNodes);
284 AStatsEvent_build(event);
285 int64_t endTime = android::elapsedRealtimeNano();
286
287 size_t bufferSize;
288 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
289 uint8_t* bufferEnd = buffer + bufferSize;
290
291 checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
292
293 checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
294 checkScalar(&buffer, numNodes);
295 for (int i = 0; i < numNodes; i++) {
296 checkScalar(&buffer, uids[i]);
297 checkString(&buffer, tags[i]);
298 }
299
300 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
301 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
302 AStatsEvent_release(event);
303 }
304
TEST(StatsEventTest,TestFieldAnnotations)305 TEST(StatsEventTest, TestFieldAnnotations) {
306 uint32_t atomId = 100;
307
308 // first element information
309 bool boolValue = false;
310 uint8_t boolAnnotation1Id = 1;
311 uint8_t boolAnnotation2Id = 2;
312 bool boolAnnotation1Value = true;
313 int32_t boolAnnotation2Value = 3;
314
315 // second element information
316 float floatValue = -5.0;
317 uint8_t floatAnnotation1Id = 3;
318 uint8_t floatAnnotation2Id = 4;
319 int32_t floatAnnotation1Value = 8;
320 bool floatAnnotation2Value = false;
321
322 int64_t startTime = android::elapsedRealtimeNano();
323 AStatsEvent* event = AStatsEvent_obtain();
324 AStatsEvent_setAtomId(event, atomId);
325 AStatsEvent_writeBool(event, boolValue);
326 AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value);
327 AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value);
328 AStatsEvent_writeFloat(event, floatValue);
329 AStatsEvent_addInt32Annotation(event, floatAnnotation1Id, floatAnnotation1Value);
330 AStatsEvent_addBoolAnnotation(event, floatAnnotation2Id, floatAnnotation2Value);
331 AStatsEvent_build(event);
332 int64_t endTime = android::elapsedRealtimeNano();
333
334 size_t bufferSize;
335 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
336 uint8_t* bufferEnd = buffer + bufferSize;
337
338 checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
339
340 // check first element
341 checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
342 checkScalar(&buffer, boolValue);
343 checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
344 checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
345
346 // check second element
347 checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
348 checkScalar(&buffer, floatValue);
349 checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
350 checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
351
352 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
353 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
354 AStatsEvent_release(event);
355 }
356
TEST(StatsEventTest,TestAtomLevelAnnotations)357 TEST(StatsEventTest, TestAtomLevelAnnotations) {
358 uint32_t atomId = 100;
359 // atom-level annotation information
360 uint8_t boolAnnotationId = 1;
361 uint8_t int32AnnotationId = 2;
362 bool boolAnnotationValue = false;
363 int32_t int32AnnotationValue = 5;
364
365 float fieldValue = -3.5;
366
367 int64_t startTime = android::elapsedRealtimeNano();
368 AStatsEvent* event = AStatsEvent_obtain();
369 AStatsEvent_setAtomId(event, atomId);
370 AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue);
371 AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue);
372 AStatsEvent_writeFloat(event, fieldValue);
373 AStatsEvent_build(event);
374 int64_t endTime = android::elapsedRealtimeNano();
375
376 size_t bufferSize;
377 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
378 uint8_t* bufferEnd = buffer + bufferSize;
379
380 checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId,
381 /*numAtomLevelAnnotations=*/2);
382
383 // check atom-level annotations
384 checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue);
385 checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue);
386
387 // check first element
388 checkTypeHeader(&buffer, FLOAT_TYPE);
389 checkScalar(&buffer, fieldValue);
390
391 EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
392 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
393 AStatsEvent_release(event);
394 }
395
TEST(StatsEventTest,TestNoAtomIdError)396 TEST(StatsEventTest, TestNoAtomIdError) {
397 AStatsEvent* event = AStatsEvent_obtain();
398 // Don't set the atom id in order to trigger the error.
399 AStatsEvent_build(event);
400
401 uint32_t errors = AStatsEvent_getErrors(event);
402 EXPECT_EQ(errors & ERROR_NO_ATOM_ID, ERROR_NO_ATOM_ID);
403
404 AStatsEvent_release(event);
405 }
406
TEST(StatsEventTest,TestPushOverflowError)407 TEST(StatsEventTest, TestPushOverflowError) {
408 const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
409 const int writeCount = 120; // Number of times to write str in the event.
410
411 AStatsEvent* event = AStatsEvent_obtain();
412 AStatsEvent_setAtomId(event, 100);
413
414 // Add str to the event 120 times. Each str takes >35 bytes so this will
415 // overflow the 4068 byte buffer.
416 // We want to keep writeCount less than 127 to avoid hitting
417 // ERROR_TOO_MANY_FIELDS.
418 for (int i = 0; i < writeCount; i++) {
419 AStatsEvent_writeString(event, str);
420 }
421 AStatsEvent_write(event);
422
423 uint32_t errors = AStatsEvent_getErrors(event);
424 EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
425
426 AStatsEvent_release(event);
427 }
428
TEST(StatsEventTest,TestPullOverflowError)429 TEST(StatsEventTest, TestPullOverflowError) {
430 const uint32_t atomId = 10100;
431 const vector<uint8_t> bytes(430 /* number of elements */, 1 /* value of each element */);
432 const int writeCount = 120; // Number of times to write bytes in the event.
433
434 AStatsEvent* event = AStatsEvent_obtain();
435 AStatsEvent_setAtomId(event, atomId);
436
437 // Add bytes to the event 120 times. Size of bytes is 430 so this will
438 // overflow the 50 KB pulled event buffer.
439 // We want to keep writeCount less than 127 to avoid hitting
440 // ERROR_TOO_MANY_FIELDS.
441 for (int i = 0; i < writeCount; i++) {
442 AStatsEvent_writeByteArray(event, bytes.data(), bytes.size());
443 }
444 AStatsEvent_build(event);
445
446 uint32_t errors = AStatsEvent_getErrors(event);
447 EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
448
449 AStatsEvent_release(event);
450 }
451
TEST(StatsEventTest,TestLargePull)452 TEST(StatsEventTest, TestLargePull) {
453 const uint32_t atomId = 100;
454 const string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
455 const int writeCount = 120; // Number of times to write str in the event.
456 const int64_t startTime = android::elapsedRealtimeNano();
457
458 AStatsEvent* event = AStatsEvent_obtain();
459 AStatsEvent_setAtomId(event, atomId);
460
461 // Add str to the event 120 times.
462 // We want to keep writeCount less than 127 to avoid hitting
463 // ERROR_TOO_MANY_FIELDS.
464 for (int i = 0; i < writeCount; i++) {
465 AStatsEvent_writeString(event, str.c_str());
466 }
467 AStatsEvent_build(event);
468 int64_t endTime = android::elapsedRealtimeNano();
469
470 size_t bufferSize;
471 uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
472 uint8_t* bufferEnd = buffer + bufferSize;
473
474 checkMetadata(&buffer, writeCount, startTime, endTime, atomId);
475
476 // Check all instances of str have been written.
477 for (int i = 0; i < writeCount; i++) {
478 checkTypeHeader(&buffer, STRING_TYPE);
479 checkString(&buffer, str);
480 }
481
482 EXPECT_EQ(buffer, bufferEnd); // Ensure that we have read the entire buffer.
483 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
484 AStatsEvent_release(event);
485 }
486
TEST(StatsEventTest,TestAtomIdInvalidPositionError)487 TEST(StatsEventTest, TestAtomIdInvalidPositionError) {
488 AStatsEvent* event = AStatsEvent_obtain();
489 AStatsEvent_writeInt32(event, 0);
490 AStatsEvent_setAtomId(event, 100);
491 AStatsEvent_writeBool(event, true);
492 AStatsEvent_build(event);
493
494 uint32_t errors = AStatsEvent_getErrors(event);
495 EXPECT_EQ(errors & ERROR_ATOM_ID_INVALID_POSITION, ERROR_ATOM_ID_INVALID_POSITION);
496
497 AStatsEvent_release(event);
498 }
499
TEST(StatsEventTest,TestOverwriteTimestamp)500 TEST(StatsEventTest, TestOverwriteTimestamp) {
501 uint32_t atomId = 100;
502 int64_t expectedTimestamp = 0x123456789;
503 AStatsEvent* event = AStatsEvent_obtain();
504 AStatsEvent_setAtomId(event, atomId);
505 AStatsEvent_overwriteTimestamp(event, expectedTimestamp);
506 AStatsEvent_build(event);
507
508 uint8_t* buffer = AStatsEvent_getBuffer(event, NULL);
509
510 // Make sure that the timestamp is being overwritten.
511 checkMetadata(&buffer, /*numElements=*/0, /*startTime=*/expectedTimestamp,
512 /*endTime=*/expectedTimestamp, atomId);
513
514 EXPECT_EQ(AStatsEvent_getErrors(event), 0);
515 AStatsEvent_release(event);
516 }
517