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 using std::string;
22 using std::vector;
23 
24 // Side-effect: this function moves the start of the buffer past the read value
25 template <class T>
readNext(uint8_t ** buffer)26 T readNext(uint8_t** buffer) {
27     T value = *(T*)(*buffer);
28     *buffer += sizeof(T);
29     return value;
30 }
31 
checkTypeHeader(uint8_t ** buffer,uint8_t typeId,uint8_t numAnnotations=0)32 void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
33     uint8_t typeHeader = (numAnnotations << 4) | typeId;
34     EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
35 }
36 
37 template <class T>
checkScalar(uint8_t ** buffer,T expectedValue)38 void checkScalar(uint8_t** buffer, T expectedValue) {
39     EXPECT_EQ(readNext<T>(buffer), expectedValue);
40 }
41 
checkString(uint8_t ** buffer,const string & expectedString)42 void checkString(uint8_t** buffer, const string& expectedString) {
43     uint32_t size = readNext<uint32_t>(buffer);
44     string parsedString((char*)(*buffer), size);
45     EXPECT_EQ(parsedString, expectedString);
46     *buffer += size;  // move buffer past string we just read
47 }
48 
checkByteArray(uint8_t ** buffer,const vector<uint8_t> & expectedByteArray)49 void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
50     uint32_t size = readNext<uint32_t>(buffer);
51     vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
52     EXPECT_EQ(parsedByteArray, expectedByteArray);
53     *buffer += size;  // move buffer past byte array we just read
54 }
55 
56 template <class T>
checkAnnotation(uint8_t ** buffer,uint8_t annotationId,uint8_t typeId,T annotationValue)57 void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
58     EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
59     EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
60     checkScalar<T>(buffer, annotationValue);
61 }
62 
checkMetadata(uint8_t ** buffer,uint8_t numElements,int64_t startTime,int64_t endTime,uint32_t atomId)63 void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
64                    uint32_t atomId) {
65     // All events start with OBJECT_TYPE id.
66     checkTypeHeader(buffer, OBJECT_TYPE);
67 
68     // We increment by 2 because the number of elements listed in the
69     // serialization accounts for the timestamp and atom id as well.
70     checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
71 
72     // Check timestamp
73     checkTypeHeader(buffer, INT64_TYPE);
74     int64_t timestamp = readNext<int64_t>(buffer);
75     EXPECT_GE(timestamp, startTime);
76     EXPECT_LE(timestamp, endTime);
77 
78     // Check atom id
79     checkTypeHeader(buffer, INT32_TYPE);
80     checkScalar(buffer, atomId);
81 }
82 
TEST(StatsEventTest,TestScalars)83 TEST(StatsEventTest, TestScalars) {
84     uint32_t atomId = 100;
85     int32_t int32Value = -5;
86     int64_t int64Value = -2 * android::elapsedRealtimeNano();
87     float floatValue = 2.0;
88     bool boolValue = false;
89 
90     int64_t startTime = android::elapsedRealtimeNano();
91     struct stats_event* event = stats_event_obtain();
92     stats_event_set_atom_id(event, atomId);
93     stats_event_write_int32(event, int32Value);
94     stats_event_write_int64(event, int64Value);
95     stats_event_write_float(event, floatValue);
96     stats_event_write_bool(event, boolValue);
97     stats_event_build(event);
98     int64_t endTime = android::elapsedRealtimeNano();
99 
100     size_t bufferSize;
101     uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
102     uint8_t* bufferEnd = buffer + bufferSize;
103 
104     checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
105 
106     // check int32 element
107     checkTypeHeader(&buffer, INT32_TYPE);
108     checkScalar(&buffer, int32Value);
109 
110     // check int64 element
111     checkTypeHeader(&buffer, INT64_TYPE);
112     checkScalar(&buffer, int64Value);
113 
114     // check float element
115     checkTypeHeader(&buffer, FLOAT_TYPE);
116     checkScalar(&buffer, floatValue);
117 
118     // check bool element
119     checkTypeHeader(&buffer, BOOL_TYPE);
120     checkScalar(&buffer, boolValue);
121 
122     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
123     EXPECT_EQ(stats_event_get_errors(event), 0);
124     stats_event_release(event);
125 }
126 
TEST(StatsEventTest,TestStrings)127 TEST(StatsEventTest, TestStrings) {
128     uint32_t atomId = 100;
129     string str = "test_string";
130 
131     int64_t startTime = android::elapsedRealtimeNano();
132     struct stats_event* event = stats_event_obtain();
133     stats_event_set_atom_id(event, atomId);
134     stats_event_write_string8(event, str.c_str());
135     stats_event_build(event);
136     int64_t endTime = android::elapsedRealtimeNano();
137 
138     size_t bufferSize;
139     uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
140     uint8_t* bufferEnd = buffer + bufferSize;
141 
142     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
143 
144     checkTypeHeader(&buffer, STRING_TYPE);
145     checkString(&buffer, str);
146 
147     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
148     EXPECT_EQ(stats_event_get_errors(event), 0);
149     stats_event_release(event);
150 }
151 
TEST(StatsEventTest,TestByteArrays)152 TEST(StatsEventTest, TestByteArrays) {
153     uint32_t atomId = 100;
154     vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
155 
156     int64_t startTime = android::elapsedRealtimeNano();
157     struct stats_event* event = stats_event_obtain();
158     stats_event_set_atom_id(event, atomId);
159     stats_event_write_byte_array(event, message.data(), message.size());
160     stats_event_build(event);
161     int64_t endTime = android::elapsedRealtimeNano();
162 
163     size_t bufferSize;
164     uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
165     uint8_t* bufferEnd = buffer + bufferSize;
166 
167     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
168 
169     checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
170     checkByteArray(&buffer, message);
171 
172     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
173     EXPECT_EQ(stats_event_get_errors(event), 0);
174     stats_event_release(event);
175 }
176 
TEST(StatsEventTest,TestAttributionChains)177 TEST(StatsEventTest, TestAttributionChains) {
178     uint32_t atomId = 100;
179 
180     uint8_t numNodes = 50;
181     uint32_t uids[numNodes];
182     vector<string> tags(numNodes);  // storage that cTag elements point to
183     const char* cTags[numNodes];
184     for (int i = 0; i < (int)numNodes; i++) {
185         uids[i] = i;
186         tags.push_back("test" + std::to_string(i));
187         cTags[i] = tags[i].c_str();
188     }
189 
190     int64_t startTime = android::elapsedRealtimeNano();
191     struct stats_event* event = stats_event_obtain();
192     stats_event_set_atom_id(event, atomId);
193     stats_event_write_attribution_chain(event, uids, cTags, numNodes);
194     stats_event_build(event);
195     int64_t endTime = android::elapsedRealtimeNano();
196 
197     size_t bufferSize;
198     uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
199     uint8_t* bufferEnd = buffer + bufferSize;
200 
201     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
202 
203     checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
204     checkScalar(&buffer, numNodes);
205     for (int i = 0; i < numNodes; i++) {
206         checkScalar(&buffer, uids[i]);
207         checkString(&buffer, tags[i]);
208     }
209 
210     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
211     EXPECT_EQ(stats_event_get_errors(event), 0);
212     stats_event_release(event);
213 }
214 
TEST(StatsEventTest,TestKeyValuePairs)215 TEST(StatsEventTest, TestKeyValuePairs) {
216     uint32_t atomId = 100;
217 
218     uint8_t numPairs = 4;
219     struct key_value_pair pairs[numPairs];
220     pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = -1};
221     pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
222     pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 5.5};
223     string str = "test_key_value_pair_string";
224     pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
225 
226     int64_t startTime = android::elapsedRealtimeNano();
227     struct stats_event* event = stats_event_obtain();
228     stats_event_set_atom_id(event, atomId);
229     stats_event_write_key_value_pairs(event, pairs, numPairs);
230     stats_event_build(event);
231     int64_t endTime = android::elapsedRealtimeNano();
232 
233     size_t bufferSize;
234     uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
235     uint8_t* bufferEnd = buffer + bufferSize;
236 
237     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
238 
239     checkTypeHeader(&buffer, KEY_VALUE_PAIRS_TYPE);
240     checkScalar(&buffer, numPairs);
241 
242     // first pair
243     checkScalar(&buffer, pairs[0].key);
244     checkTypeHeader(&buffer, pairs[0].valueType);
245     checkScalar(&buffer, pairs[0].int32Value);
246 
247     // second pair
248     checkScalar(&buffer, pairs[1].key);
249     checkTypeHeader(&buffer, pairs[1].valueType);
250     checkScalar(&buffer, pairs[1].int64Value);
251 
252     // third pair
253     checkScalar(&buffer, pairs[2].key);
254     checkTypeHeader(&buffer, pairs[2].valueType);
255     checkScalar(&buffer, pairs[2].floatValue);
256 
257     // fourth pair
258     checkScalar(&buffer, pairs[3].key);
259     checkTypeHeader(&buffer, pairs[3].valueType);
260     checkString(&buffer, str);
261 
262     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
263     EXPECT_EQ(stats_event_get_errors(event), 0);
264     stats_event_release(event);
265 }
266 
TEST(StatsEventTest,TestAnnotations)267 TEST(StatsEventTest, TestAnnotations) {
268     uint32_t atomId = 100;
269 
270     // first element information
271     bool boolValue = false;
272     uint8_t boolAnnotation1Id = 1;
273     uint8_t boolAnnotation2Id = 2;
274     bool boolAnnotation1Value = true;
275     int32_t boolAnnotation2Value = 3;
276 
277     // second element information
278     float floatValue = -5.0;
279     uint8_t floatAnnotation1Id = 3;
280     uint8_t floatAnnotation2Id = 4;
281     int32_t floatAnnotation1Value = 8;
282     bool floatAnnotation2Value = false;
283 
284     int64_t startTime = android::elapsedRealtimeNano();
285     struct stats_event* event = stats_event_obtain();
286     stats_event_set_atom_id(event, 100);
287     stats_event_write_bool(event, boolValue);
288     stats_event_add_bool_annotation(event, boolAnnotation1Id, boolAnnotation1Value);
289     stats_event_add_int32_annotation(event, boolAnnotation2Id, boolAnnotation2Value);
290     stats_event_write_float(event, floatValue);
291     stats_event_add_int32_annotation(event, floatAnnotation1Id, floatAnnotation1Value);
292     stats_event_add_bool_annotation(event, floatAnnotation2Id, floatAnnotation2Value);
293     stats_event_build(event);
294     int64_t endTime = android::elapsedRealtimeNano();
295 
296     size_t bufferSize;
297     uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
298     uint8_t* bufferEnd = buffer + bufferSize;
299 
300     checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
301 
302     // check first element
303     checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
304     checkScalar(&buffer, boolValue);
305     checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
306     checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
307 
308     // check second element
309     checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
310     checkScalar(&buffer, floatValue);
311     checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
312     checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
313 
314     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
315     EXPECT_EQ(stats_event_get_errors(event), 0);
316     stats_event_release(event);
317 }
318 
TEST(StatsEventTest,TestNoAtomIdError)319 TEST(StatsEventTest, TestNoAtomIdError) {
320     struct stats_event* event = stats_event_obtain();
321     // Don't set the atom id in order to trigger the error.
322     stats_event_build(event);
323 
324     uint32_t errors = stats_event_get_errors(event);
325     EXPECT_NE(errors | ERROR_NO_ATOM_ID, 0);
326 
327     stats_event_release(event);
328 }
329 
TEST(StatsEventTest,TestOverflowError)330 TEST(StatsEventTest, TestOverflowError) {
331     struct stats_event* event = stats_event_obtain();
332     stats_event_set_atom_id(event, 100);
333     // Add 1000 int32s to the event. Each int32 takes 5 bytes so this will
334     // overflow the 4068 byte buffer.
335     for (int i = 0; i < 1000; i++) {
336         stats_event_write_int32(event, 0);
337     }
338     stats_event_build(event);
339 
340     uint32_t errors = stats_event_get_errors(event);
341     EXPECT_NE(errors | ERROR_OVERFLOW, 0);
342 
343     stats_event_release(event);
344 }
345