/* * 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 #include #include #include #include #include #include #include #include #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(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(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(&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(&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(&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(&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(&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(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(&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(&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(&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(&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); }