summaryrefslogtreecommitdiff
path: root/liblog/log_event_list.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'liblog/log_event_list.cpp')
-rw-r--r--liblog/log_event_list.cpp543
1 files changed, 543 insertions, 0 deletions
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
new file mode 100644
index 000000000..cb70d488a
--- /dev/null
+++ b/liblog/log_event_list.cpp
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log_event_list.h>
+#include <private/android_logger.h>
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+enum ReadWriteFlag {
+ kAndroidLoggerRead = 1,
+ kAndroidLoggerWrite = 2,
+};
+
+struct android_log_context_internal {
+ uint32_t tag;
+ unsigned pos; /* Read/write position into buffer */
+ unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
+ unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
+ unsigned list_nest_depth;
+ unsigned len; /* Length or raw buffer. */
+ bool overflow;
+ bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+ ReadWriteFlag read_write_flag;
+ uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+};
+
+static void init_context(android_log_context_internal* context, uint32_t tag) {
+ context->tag = tag;
+ context->read_write_flag = kAndroidLoggerWrite;
+ size_t needed = sizeof(android_event_list_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ }
+ /* Everything is a list */
+ context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+ context->list[0] = context->pos + 1;
+ context->pos += needed;
+}
+
+static void init_parser_context(android_log_context_internal* context, const char* msg,
+ size_t len) {
+ len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+ context->len = len;
+ memcpy(context->storage, msg, len);
+ context->read_write_flag = kAndroidLoggerRead;
+}
+
+android_log_context create_android_logger(uint32_t tag) {
+ android_log_context_internal* context;
+
+ context =
+ static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+ if (!context) {
+ return NULL;
+ }
+ init_context(context, tag);
+
+ return (android_log_context)context;
+}
+
+android_log_context create_android_log_parser(const char* msg, size_t len) {
+ android_log_context_internal* context;
+
+ context =
+ static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+ if (!context) {
+ return NULL;
+ }
+ init_parser_context(context, msg, len);
+
+ return (android_log_context)context;
+}
+
+int android_log_destroy(android_log_context* ctx) {
+ android_log_context_internal* context;
+
+ context = (android_log_context_internal*)*ctx;
+ if (!context) {
+ return -EBADF;
+ }
+ memset(context, 0, sizeof(*context));
+ free(context);
+ *ctx = NULL;
+ return 0;
+}
+
+int android_log_reset(android_log_context context) {
+ uint32_t tag;
+
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ tag = context->tag;
+ memset(context, 0, sizeof(*context));
+ init_context(context, tag);
+
+ return 0;
+}
+
+int android_log_parser_reset(android_log_context context, const char* msg, size_t len) {
+ if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ memset(context, 0, sizeof(*context));
+ init_parser_context(context, msg, len);
+
+ return 0;
+}
+
+int android_log_write_list_begin(android_log_context context) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ size_t needed = sizeof(android_event_list_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->list_nest_depth++;
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[context->pos]);
+ event_list->type = EVENT_TYPE_LIST;
+ event_list->element_count = 0;
+ context->list[context->list_nest_depth] = context->pos + 1;
+ context->count[context->list_nest_depth] = 0;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_int32(android_log_context context, int32_t value) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ size_t needed = sizeof(android_event_int_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[context->pos]);
+ event_int->type = EVENT_TYPE_INT;
+ event_int->data = value;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_int64(android_log_context context, int64_t value) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ size_t needed = sizeof(android_event_long_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[context->pos]);
+ event_long->type = EVENT_TYPE_LONG;
+ event_long->data = value;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_string8_len(android_log_context context, const char* value, size_t maxlen) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ if (!value) {
+ value = "";
+ }
+ int32_t len = strnlen(value, maxlen);
+ size_t needed = sizeof(android_event_string_t) + len;
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ /* Truncate string for delivery */
+ len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+ if (len <= 0) {
+ context->overflow = true;
+ return -EIO;
+ }
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[context->pos]);
+ event_string->type = EVENT_TYPE_STRING;
+ event_string->length = len;
+ if (len) {
+ memcpy(&event_string->data, value, len);
+ }
+ context->pos += needed;
+ return len;
+}
+
+int android_log_write_string8(android_log_context ctx, const char* value) {
+ return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+int android_log_write_float32(android_log_context context, float value) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ size_t needed = sizeof(android_event_float_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_float = reinterpret_cast<android_event_float_t*>(&context->storage[context->pos]);
+ event_float->type = EVENT_TYPE_FLOAT;
+ event_float->data = value;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_list_end(android_log_context context) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ context->list_nest_depth--;
+ return -EOVERFLOW;
+ }
+ if (!context->list_nest_depth) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->list[context->list_nest_depth] <= 0) {
+ context->list_nest_depth--;
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ context->storage[context->list[context->list_nest_depth]] =
+ context->count[context->list_nest_depth];
+ context->list_nest_depth--;
+ return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+int android_log_write_list(android_log_context context, log_id_t id) {
+ const char* msg;
+ ssize_t len;
+
+ if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
+ return -EINVAL;
+ }
+
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char*)context->storage;
+ /* it's not a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+ return (id == LOG_ID_EVENTS)
+ ? __android_log_bwrite(context->tag, msg, len)
+ : ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len)
+ : __android_log_security_bwrite(context->tag, msg, len));
+}
+
+int android_log_write_list_buffer(android_log_context context, const char** buffer) {
+ const char* msg;
+ ssize_t len;
+
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+ if (buffer == NULL) {
+ return -EFAULT;
+ }
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char*)context->storage;
+ /* it's not a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+ *buffer = msg;
+ return len;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(android_log_context context,
+ int peek) {
+ android_log_list_element elem;
+ unsigned pos;
+
+ memset(&elem, 0, sizeof(elem));
+
+ /* Nothing to parse from this context, so return complete. */
+ if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+ (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+ (context->count[context->list_nest_depth] >=
+ (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ if (context &&
+ (context->list_stop || ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+ !context->count[context->list_nest_depth]))) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ }
+ elem.complete = true;
+ return elem;
+ }
+
+ /*
+ * Use a different variable to update the position in case this
+ * operation is a "peek".
+ */
+ pos = context->pos;
+ if (context->list_stop) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ elem.complete = !context->count[0] && (!context->list_nest_depth ||
+ ((context->list_nest_depth == 1) && !context->count[1]));
+ if (!peek) {
+ /* Suck in superfluous stop */
+ if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+ context->pos = pos + 1;
+ }
+ if (context->list_nest_depth) {
+ --context->list_nest_depth;
+ if (context->count[context->list_nest_depth]) {
+ context->list_stop = false;
+ }
+ } else {
+ context->list_stop = false;
+ }
+ }
+ return elem;
+ }
+ if ((pos + 1) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+
+ elem.type = static_cast<AndroidEventLogType>(context->storage[pos]);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_FLOAT:
+ /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+ /* FALLTHRU */
+ case EVENT_TYPE_INT: {
+ elem.len = sizeof(int32_t);
+ if ((pos + sizeof(android_event_int_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+
+ auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_int_t);
+ elem.data.int32 = event_int->data;
+ /* common tangeable object suffix */
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+ }
+
+ case EVENT_TYPE_LONG: {
+ elem.len = sizeof(int64_t);
+ if ((pos + sizeof(android_event_long_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+
+ auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_long_t);
+ elem.data.int64 = event_long->data;
+ /* common tangeable object suffix */
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+ }
+
+ case EVENT_TYPE_STRING: {
+ if ((pos + sizeof(android_event_string_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_string_t);
+ // Wire format is int32_t, but elem.len is uint16_t...
+ if (event_string->length >= UINT16_MAX) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ elem.len = event_string->length;
+ if ((pos + elem.len) > context->len) {
+ elem.len = context->len - pos; /* truncate string */
+ elem.complete = true;
+ if (!elem.len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ }
+ elem.data.string = event_string->data;
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+ }
+
+ case EVENT_TYPE_LIST: {
+ if ((pos + sizeof(android_event_list_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_list_t);
+ elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+ if (peek) {
+ return elem;
+ }
+ if (context->count[context->list_nest_depth]) {
+ context->count[context->list_nest_depth]--;
+ }
+ context->list_stop = event_list->element_count == 0;
+ context->list_nest_depth++;
+ if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->count[context->list_nest_depth] = event_list->element_count;
+ }
+ context->pos = pos;
+ return elem;
+ }
+
+ case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+ pos++;
+ if (!peek) {
+ context->pos = pos;
+ }
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = !context->list_nest_depth;
+ if (context->list_nest_depth > 0) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ if (!peek) {
+ context->list_nest_depth--;
+ }
+ }
+ return elem;
+
+ default:
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+}
+
+android_log_list_element android_log_read_next(android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 0);
+}
+
+android_log_list_element android_log_peek_next(android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 1);
+}