blob: 5bccd9bb41d428451209b902d528fcac606d93f4 [file] [log] [blame]
Ruchir Rastogi6baeca52019-10-14 16:18:34 -07001/*
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 <stdlib.h>
19#include <string.h>
Ruchir Rastogi6c7cbc42019-10-22 10:56:58 -070020#include <time.h>
Ruchir Rastogi6baeca52019-10-14 16:18:34 -070021#include "include/stats_event_list.h"
22
23#define byte unsigned char
24
25#define STATS_EVENT_TAG 1937006964
26#define LOGGER_ENTRY_MAX_PAYLOAD 4068
27// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
28// See android_util_Stats_Log.cpp
29#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - 4)
30
31/* POSITIONS */
32#define POS_NUM_ELEMENTS 1
33#define POS_TIMESTAMP (POS_NUM_ELEMENTS + 1)
34#define POS_ATOM_ID (POS_TIMESTAMP + sizeof(byte) + sizeof(uint64_t))
35#define POS_FIRST_FIELD (POS_ATOM_ID + sizeof(byte) + sizeof(uint32_t))
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_PAIR_TYPE 0x08
47#define ATTRIBUTION_CHAIN_TYPE 0x09
48#define ERROR_TYPE 0x0F
49
50/* LIMITS */
51#define MAX_ANNOTATION_COUNT 15
52#define MAX_ANNOTATION_ID 127
53#define MAX_ATTRIBUTION_NODES 127
54#define MAX_NUM_ELEMENTS 127
55
56// The stats_event struct holds the serialized encoding of an event
57// within a buf. Also includes other required fields.
58struct stats_event {
59 byte buf[MAX_EVENT_PAYLOAD];
60 size_t bufPos; // current write position within the buf
61 size_t lastFieldPos; // location of last field within the buf
62 size_t size; // number of valid bytes within buffer
63 uint32_t numElements;
64 uint32_t atomId;
65 uint64_t timestampNs;
66 uint32_t errors;
67 uint32_t tag;
68};
69
Ruchir Rastogi6c7cbc42019-10-22 10:56:58 -070070static int64_t get_elapsed_realtime_ns() {
71 struct timespec t;
72 t.tv_sec = t.tv_nsec = 0;
73 clock_gettime(CLOCK_BOOTTIME, &t);
74 return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
75}
76
Ruchir Rastogi6baeca52019-10-14 16:18:34 -070077struct stats_event* stats_event_obtain() {
78 struct stats_event* event = malloc(sizeof(struct stats_event));
79
80 memset(event->buf, 0, MAX_EVENT_PAYLOAD);
81 event->buf[0] = OBJECT_TYPE;
82
83 event->bufPos = POS_FIRST_FIELD;
84 event->lastFieldPos = 0;
85 event->size = 0;
86 event->numElements = 0;
87 event->atomId = 0;
Ruchir Rastogi6c7cbc42019-10-22 10:56:58 -070088 event->timestampNs = get_elapsed_realtime_ns();
Ruchir Rastogi6baeca52019-10-14 16:18:34 -070089 event->errors = 0;
90 event->tag = STATS_EVENT_TAG;
91 return event;
92}
93
94void stats_event_release(struct stats_event* event) {
95 free(event); // free is a no-op if event is NULL
96}
97
Ruchir Rastogi6c7cbc42019-10-22 10:56:58 -070098// Should only be used for testing
Ruchir Rastogi6baeca52019-10-14 16:18:34 -070099void stats_event_set_timestamp_ns(struct stats_event* event, uint64_t timestampNs) {
100 if (event) event->timestampNs = timestampNs;
101}
102
103void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId) {
104 if (event) event->atomId = atomId;
105}
106
107// Side-effect: modifies event->errors if the buffer would overflow
108static bool overflows(struct stats_event* event, size_t size) {
109 if (event->bufPos + size > MAX_EVENT_PAYLOAD) {
110 event->errors |= ERROR_OVERFLOW;
111 return true;
112 }
113 return false;
114}
115
116static size_t put_byte(struct stats_event* event, byte value) {
117 if (!overflows(event, sizeof(value))) {
118 event->buf[event->bufPos] = value;
119 return sizeof(byte);
120 }
121 return 0;
122}
123
124static size_t put_bool(struct stats_event* event, bool value) {
125 return put_byte(event, (byte)value);
126}
127
128static size_t put_int32(struct stats_event* event, int32_t value) {
129 if (!overflows(event, sizeof(value))) {
130 memcpy(&event->buf[event->bufPos], &value, sizeof(int32_t));
131 return sizeof(int32_t);
132 }
133 return 0;
134}
135
136static size_t put_int64(struct stats_event* event, int64_t value) {
137 if (!overflows(event, sizeof(value))) {
138 memcpy(&event->buf[event->bufPos], &value, sizeof(int64_t));
139 return sizeof(int64_t);
140 }
141 return 0;
142}
143
144static size_t put_float(struct stats_event* event, float value) {
145 if (!overflows(event, sizeof(value))) {
146 memcpy(&event->buf[event->bufPos], &value, sizeof(float));
147 return sizeof(float);
148 }
149 return 0;
150}
151
152static size_t put_byte_array(struct stats_event* event, void* buf, size_t size) {
153 if (!overflows(event, size)) {
154 memcpy(&event->buf[event->bufPos], buf, size);
155 return size;
156 }
157 return 0;
158}
159
160void stats_event_write_int32(struct stats_event* event, int32_t value) {
161 if (!event || event->errors) return;
162
163 event->lastFieldPos = event->bufPos;
164 event->bufPos += put_byte(event, INT32_TYPE);
165 event->bufPos += put_int32(event, value);
166 event->numElements++;
167}
168
169void stats_event_write_int64(struct stats_event* event, int64_t value) {
170 if (!event || event->errors) return;
171
172 event->lastFieldPos = event->bufPos;
173 event->bufPos += put_byte(event, INT64_TYPE);
174 event->bufPos += put_int64(event, value);
175 event->numElements++;
176}
177
178void stats_event_write_float(struct stats_event* event, float value) {
179 if (!event || event->errors) return;
180
181 event->lastFieldPos = event->bufPos;
182 event->bufPos += put_byte(event, FLOAT_TYPE);
183 event->bufPos += put_float(event, value);
184 event->numElements++;
185}
186
187void stats_event_write_bool(struct stats_event* event, bool value) {
188 if (!event || event->errors) return;
189
190 event->lastFieldPos = event->bufPos;
191 event->bufPos += put_byte(event, BOOL_TYPE);
192 event->bufPos += put_bool(event, value);
193 event->numElements++;
194}
195
196// Buf is assumed to be encoded using UTF8
197void stats_event_write_byte_array(struct stats_event* event, uint8_t* buf, uint32_t numBytes) {
198 if (!event || !buf || event->errors) return;
199
200 event->lastFieldPos = event->bufPos;
201 event->bufPos += put_byte(event, BYTE_ARRAY_TYPE);
202 event->bufPos += put_int32(event, numBytes);
203 event->bufPos += put_byte_array(event, buf, numBytes);
204 event->numElements++;
205}
206
207// Buf is assumed to be encoded using UTF8
208void stats_event_write_string8(struct stats_event* event, char* buf, uint32_t numBytes) {
209 if (!event || !buf || event->errors) return;
210
211 event->lastFieldPos = event->bufPos;
212 event->bufPos += put_byte(event, STRING_TYPE);
213 event->bufPos += put_int32(event, numBytes);
214 event->bufPos += put_byte_array(event, buf, numBytes);
215 event->numElements++;
216}
217
218// Side-effect: modifies event->errors if the attribution chain is too long
219static bool is_attribution_chain_too_long(struct stats_event* event, uint32_t numNodes) {
220 if (numNodes > MAX_ATTRIBUTION_NODES) {
221 event->errors |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
222 return true;
223 }
224 return false;
225}
226
227// Tags are assumed to be encoded using UTF8
228void stats_event_write_attribution_chain(struct stats_event* event, uint32_t* uids, char** tags,
229 uint32_t* tagLengths, uint32_t numNodes) {
230 if (!event || event->errors) return;
231 if (is_attribution_chain_too_long(event, numNodes)) return;
232
233 event->lastFieldPos = event->bufPos;
234 event->bufPos += put_byte(event, ATTRIBUTION_CHAIN_TYPE);
235 event->bufPos += put_byte(event, (byte)numNodes);
236
237 for (int i = 0; i < numNodes; i++) {
238 event->bufPos += put_int32(event, uids[i]);
239 event->bufPos += put_int32(event, tagLengths[i]);
240 event->bufPos += put_byte_array(event, tags[i], tagLengths[i]);
241 }
242 event->numElements++;
243}
244
245// Side-effect: modifies event->errors if annotation does not follow field
246static bool does_annotation_follow_field(struct stats_event* event) {
247 if (event->lastFieldPos == 0) {
248 event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
249 return false;
250 }
251 return true;
252}
253
254// Side-effect: modifies event->errors if annotation id is too large
255static bool is_valid_annotation_id(struct stats_event* event, uint32_t annotationId) {
256 if (annotationId > MAX_ANNOTATION_ID) {
257 event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
258 return false;
259 }
260 return true;
261}
262
263// Side-effect: modifies event->errors if field has too many annotations
264static void increment_annotation_count(struct stats_event* event) {
265 byte fieldType = event->buf[event->lastFieldPos] & 0x0F;
266 byte oldAnnotationCount = event->buf[event->lastFieldPos] & 0xF0;
267 byte newAnnotationCount = oldAnnotationCount + 1;
268
269 if (newAnnotationCount > MAX_ANNOTATION_COUNT) {
270 event->errors |= ERROR_TOO_MANY_ANNOTATIONS;
271 return;
272 }
273
274 event->buf[event->lastFieldPos] = ((newAnnotationCount << 4) & 0xF0) | fieldType;
275}
276
277void stats_event_add_bool_annotation(struct stats_event* event, uint32_t annotationId, bool value) {
278 if (!event || event->errors) return;
279 if (!does_annotation_follow_field(event)) return;
280 if (!is_valid_annotation_id(event, annotationId)) return;
281
282 event->bufPos += put_byte(event, (byte)annotationId);
283 event->bufPos += put_byte(event, BOOL_TYPE);
284 event->bufPos += put_bool(event, value);
285 increment_annotation_count(event);
286}
287
288void stats_event_add_int32_annotation(struct stats_event* event, uint32_t annotationId,
289 int32_t value) {
290 if (!event || event->errors) return;
291 if (!does_annotation_follow_field(event)) return;
292 if (!is_valid_annotation_id(event, annotationId)) return;
293
294 event->bufPos += put_byte(event, (byte)annotationId);
295 event->bufPos += put_byte(event, INT32_TYPE);
296 event->bufPos += put_int32(event, value);
297 increment_annotation_count(event);
298}
299
300uint32_t stats_event_get_errors(struct stats_event* event) {
301 return event->errors;
302}
303
304static void build(struct stats_event* event) {
305 // store size before we modify bufPos
306 event->size = event->bufPos;
307
308 if (event->numElements > MAX_NUM_ELEMENTS) {
309 event->errors |= ERROR_TOO_MANY_FIELDS;
310 } else {
311 event->bufPos = POS_NUM_ELEMENTS;
312 put_byte(event, (byte)event->numElements);
313 }
314
315 if (event->timestampNs == 0) {
316 event->errors |= ERROR_NO_TIMESTAMP;
317 } else {
318 // Don't use the write functions since they short-circuit if there was
319 // an error previously. We, regardless, want to know the timestamp and
320 // atomId.
321 event->bufPos = POS_TIMESTAMP;
322 event->bufPos += put_byte(event, INT64_TYPE);
323 event->bufPos += put_int64(event, event->timestampNs);
324 }
325
326 if (event->atomId == 0) {
327 event->errors |= ERROR_NO_ATOM_ID;
328 } else {
329 event->bufPos = POS_ATOM_ID;
330 event->bufPos += put_byte(event, INT32_TYPE);
331 event->bufPos += put_int64(event, event->atomId);
332 }
333
334 // If there are errors, rewrite buffer
335 if (event->errors) {
336 event->bufPos = POS_NUM_ELEMENTS;
337 put_byte(event, (byte)3);
338
339 event->bufPos = POS_FIRST_FIELD;
340 event->bufPos += put_byte(event, ERROR_TYPE);
341 event->bufPos += put_int32(event, event->errors);
342 event->size = event->bufPos;
343 }
344}
345
346void stats_event_write(struct stats_event* event) {
347 if (!event) return;
348
349 build(event);
350
351 // prepare iovecs for write to statsd
352 struct iovec vecs[2];
353 vecs[0].iov_base = &event->tag;
354 vecs[0].iov_len = sizeof(event->tag);
355 vecs[1].iov_base = &event->buf;
356 vecs[1].iov_len = event->size;
357 write_to_statsd(vecs, 2);
358}