| /* |
| * 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); |
| } |