diff options
Diffstat (limited to 'liblog/log_event_list.cpp')
-rw-r--r-- | liblog/log_event_list.cpp | 543 |
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); +} |