| /* | |
| * 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> | |
| #include "log_portability.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]; | |
| }; | |
| // TODO(tomcherry): real C++ structs. | |
| typedef struct android_log_context_internal android_log_context_internal; | |
| static void init_context(android_log_context_internal* context, uint32_t tag) { | |
| size_t needed; | |
| context->tag = tag; | |
| context->read_write_flag = kAndroidLoggerWrite; | |
| needed = sizeof(uint8_t) + sizeof(uint8_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; | |
| size_t i; | |
| 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 ctx) { | |
| android_log_context_internal* context; | |
| uint32_t tag; | |
| context = (android_log_context_internal*)ctx; | |
| 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 ctx, const char* msg, size_t len) { | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| 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 ctx) { | |
| size_t needed; | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| if (!context || (kAndroidLoggerWrite != context->read_write_flag)) { | |
| return -EBADF; | |
| } | |
| if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) { | |
| context->overflow = true; | |
| return -EOVERFLOW; | |
| } | |
| needed = sizeof(uint8_t) + sizeof(uint8_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; | |
| } | |
| context->storage[context->pos + 0] = EVENT_TYPE_LIST; | |
| context->storage[context->pos + 1] = 0; | |
| context->list[context->list_nest_depth] = context->pos + 1; | |
| context->count[context->list_nest_depth] = 0; | |
| context->pos += needed; | |
| return 0; | |
| } | |
| static inline void copy4LE(uint8_t* buf, uint32_t val) { | |
| buf[0] = val & 0xFF; | |
| buf[1] = (val >> 8) & 0xFF; | |
| buf[2] = (val >> 16) & 0xFF; | |
| buf[3] = (val >> 24) & 0xFF; | |
| } | |
| int android_log_write_int32(android_log_context ctx, int32_t value) { | |
| size_t needed; | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| if (!context || (kAndroidLoggerWrite != context->read_write_flag)) { | |
| return -EBADF; | |
| } | |
| if (context->overflow) { | |
| return -EIO; | |
| } | |
| needed = sizeof(uint8_t) + sizeof(value); | |
| if ((context->pos + needed) > MAX_EVENT_PAYLOAD) { | |
| context->overflow = true; | |
| return -EIO; | |
| } | |
| context->count[context->list_nest_depth]++; | |
| context->storage[context->pos + 0] = EVENT_TYPE_INT; | |
| copy4LE(&context->storage[context->pos + 1], value); | |
| context->pos += needed; | |
| return 0; | |
| } | |
| static inline void copy8LE(uint8_t* buf, uint64_t val) { | |
| buf[0] = val & 0xFF; | |
| buf[1] = (val >> 8) & 0xFF; | |
| buf[2] = (val >> 16) & 0xFF; | |
| buf[3] = (val >> 24) & 0xFF; | |
| buf[4] = (val >> 32) & 0xFF; | |
| buf[5] = (val >> 40) & 0xFF; | |
| buf[6] = (val >> 48) & 0xFF; | |
| buf[7] = (val >> 56) & 0xFF; | |
| } | |
| int android_log_write_int64(android_log_context ctx, int64_t value) { | |
| size_t needed; | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| if (!context || (kAndroidLoggerWrite != context->read_write_flag)) { | |
| return -EBADF; | |
| } | |
| if (context->overflow) { | |
| return -EIO; | |
| } | |
| needed = sizeof(uint8_t) + sizeof(value); | |
| if ((context->pos + needed) > MAX_EVENT_PAYLOAD) { | |
| context->overflow = true; | |
| return -EIO; | |
| } | |
| context->count[context->list_nest_depth]++; | |
| context->storage[context->pos + 0] = EVENT_TYPE_LONG; | |
| copy8LE(&context->storage[context->pos + 1], value); | |
| context->pos += needed; | |
| return 0; | |
| } | |
| int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) { | |
| size_t needed; | |
| ssize_t len; | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| if (!context || (kAndroidLoggerWrite != context->read_write_flag)) { | |
| return -EBADF; | |
| } | |
| if (context->overflow) { | |
| return -EIO; | |
| } | |
| if (!value) { | |
| value = ""; | |
| } | |
| len = strnlen(value, maxlen); | |
| needed = sizeof(uint8_t) + sizeof(int32_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]++; | |
| context->storage[context->pos + 0] = EVENT_TYPE_STRING; | |
| copy4LE(&context->storage[context->pos + 1], len); | |
| if (len) { | |
| memcpy(&context->storage[context->pos + 5], 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 ctx, float value) { | |
| size_t needed; | |
| uint32_t ivalue; | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| if (!context || (kAndroidLoggerWrite != context->read_write_flag)) { | |
| return -EBADF; | |
| } | |
| if (context->overflow) { | |
| return -EIO; | |
| } | |
| needed = sizeof(uint8_t) + sizeof(ivalue); | |
| if ((context->pos + needed) > MAX_EVENT_PAYLOAD) { | |
| context->overflow = true; | |
| return -EIO; | |
| } | |
| ivalue = *(uint32_t*)&value; | |
| context->count[context->list_nest_depth]++; | |
| context->storage[context->pos + 0] = EVENT_TYPE_FLOAT; | |
| copy4LE(&context->storage[context->pos + 1], ivalue); | |
| context->pos += needed; | |
| return 0; | |
| } | |
| int android_log_write_list_end(android_log_context ctx) { | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| 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 ctx, log_id_t id) { | |
| android_log_context_internal* context; | |
| const char* msg; | |
| ssize_t len; | |
| if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) { | |
| return -EINVAL; | |
| } | |
| context = (android_log_context_internal*)ctx; | |
| 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 ctx, const char** buffer) { | |
| android_log_context_internal* context; | |
| const char* msg; | |
| ssize_t len; | |
| context = (android_log_context_internal*)ctx; | |
| 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; | |
| } | |
| /* | |
| * Extract a 4-byte value from a byte stream. | |
| */ | |
| static inline uint32_t get4LE(const uint8_t* src) { | |
| return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); | |
| } | |
| /* | |
| * Extract an 8-byte value from a byte stream. | |
| */ | |
| static inline uint64_t get8LE(const uint8_t* src) { | |
| uint32_t low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); | |
| uint32_t high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); | |
| return ((uint64_t)high << 32) | (uint64_t)low; | |
| } | |
| /* | |
| * 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 ctx, int peek) { | |
| android_log_list_element elem; | |
| unsigned pos; | |
| android_log_context_internal* context; | |
| context = (android_log_context_internal*)ctx; | |
| 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 + elem.len) > context->len) { | |
| elem.type = EVENT_TYPE_UNKNOWN; | |
| return elem; | |
| } | |
| elem.data.int32 = get4LE(&context->storage[pos]); | |
| /* 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_LONG: | |
| elem.len = sizeof(int64_t); | |
| if ((pos + elem.len) > context->len) { | |
| elem.type = EVENT_TYPE_UNKNOWN; | |
| return elem; | |
| } | |
| elem.data.int64 = get8LE(&context->storage[pos]); | |
| /* 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_STRING: | |
| if ((pos + sizeof(int32_t)) > context->len) { | |
| elem.type = EVENT_TYPE_UNKNOWN; | |
| elem.complete = true; | |
| return elem; | |
| } | |
| elem.len = get4LE(&context->storage[pos]); | |
| pos += sizeof(int32_t); | |
| 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 = (char*)&context->storage[pos]; | |
| /* 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(uint8_t)) > context->len) { | |
| elem.type = EVENT_TYPE_UNKNOWN; | |
| elem.complete = true; | |
| return elem; | |
| } | |
| 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 = !context->storage[pos]; | |
| context->list_nest_depth++; | |
| if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) { | |
| context->count[context->list_nest_depth] = context->storage[pos]; | |
| } | |
| context->pos = pos + sizeof(uint8_t); | |
| return elem; | |
| case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */ | |
| 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); | |
| } |