From 9dd6510dd09f40973b8834c5cad1217ce1c3011d Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Wed, 17 Feb 2016 16:08:13 -0800 Subject: [PATCH] liblog: event log list logging handler (cherry pick from commit bd1ad049b2494c70f671dbd6a0566f30dc420d1a) Based off an initial request and effort by williamluh@google.com - Added the following functions: * Composing and Writing: android_log_context create_android_logger(uint32_t tag) int android_log_write_list_begin(android_log_context ctx) int android_log_write_list_end(android_log_context ctx) int android_log_write_int32(android_log_context ctx, int32_t value) int android_log_write_int64(android_log_context ctx, int64_t value) int android_log_write_string8(android_log_context ctx, const char *value) int android_log_write_float32(android_log_context ctx, float value) int android_log_write_list(android_log_context ctx, log_id_t id) * Reading and Interpreting: android_log_context create_android_log_parser(const char *msg, size_t len) android_log_list_element android_log_read_next(android_log_context ctx) android_log_list_element android_log_peek_next(android_log_context ctx) * Destroy context used above: int android_log_destroy(android_log_context *ctx); - Added unit gTests We moved implemented android_log_buffer_to_string() to the test since it is an alternate for already existing logprint functionality. Please move into liblog should it be of some common use, otherwise as is it is a good means of stessing the reading and interpreting handlers. Signed-off-by: Mark Salyzyn Bug: 19235719 Change-Id: I4aa1927e8e6a75f0a129d15a27c891cf1ccd4f5c --- include/log/log.h | 118 ++++++-- liblog/Android.mk | 1 + liblog/log_event_list.c | 520 ++++++++++++++++++++++++++++++++++ liblog/tests/liblog_test.cpp | 528 ++++++++++++++++++++++++++++++++++- 4 files changed, 1138 insertions(+), 29 deletions(-) create mode 100644 liblog/log_event_list.c diff --git a/include/log/log.h b/include/log/log.h index 1bd9165c3..6ad6f0a2e 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -484,15 +484,19 @@ extern "C" { */ /* - * Event log entry types. These must match up with the declarations in - * java/android/android/util/EventLog.java. + * Event log entry types. */ typedef enum { - EVENT_TYPE_INT = 0, - EVENT_TYPE_LONG = 1, - EVENT_TYPE_STRING = 2, - EVENT_TYPE_LIST = 3, - EVENT_TYPE_FLOAT = 4, + /* Special markers for android_log_list_element type */ + EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */ + EVENT_TYPE_UNKNOWN = '?', /* protocol error */ + + /* must match with declaration in java/android/android/util/EventLog.java */ + EVENT_TYPE_INT = 0, /* uint32_t */ + EVENT_TYPE_LONG = 1, /* uint64_t */ + EVENT_TYPE_STRING = 2, + EVENT_TYPE_LIST = 3, + EVENT_TYPE_FLOAT = 4, } AndroidEventLogType; #define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType) #define typeof_AndroidEventLogType unsigned char @@ -522,7 +526,85 @@ typedef enum { #define LOG_EVENT_STRING(_tag, _value) \ (void) __android_log_bswrite(_tag, _value); #endif -/* TODO: something for LIST */ + +typedef enum log_id { + LOG_ID_MIN = 0, + +#ifndef LINT_RLOG + LOG_ID_MAIN = 0, +#endif + LOG_ID_RADIO = 1, +#ifndef LINT_RLOG + LOG_ID_EVENTS = 2, + LOG_ID_SYSTEM = 3, + LOG_ID_CRASH = 4, + LOG_ID_SECURITY = 5, + LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */ +#endif + + LOG_ID_MAX +} log_id_t; +#define sizeof_log_id_t sizeof(typeof_log_id_t) +#define typeof_log_id_t unsigned char + +/* For manipulating lists of events. */ + +#define ANDROID_MAX_LIST_NEST_DEPTH 8 + +/* + * The opaque context used to manipulate lists of events. + */ +typedef struct android_log_context_internal *android_log_context; + +/* + * Elements returned when reading a list of events. + */ +typedef struct { + AndroidEventLogType type; + uint16_t complete; + uint16_t len; + union { + int32_t int32; + int64_t int64; + char *string; + float float32; + } data; +} android_log_list_element; + +/* + * Creates a context associated with an event tag to write elements to + * the list of events. + */ +android_log_context create_android_logger(uint32_t tag); + +/* All lists must be braced by a begin and end call */ +/* + * NB: If the first level braces are missing when specifying multiple + * elements, we will manufacturer a list to embrace it for your API + * convenience. For a single element, it will remain solitary. + */ +int android_log_write_list_begin(android_log_context ctx); +int android_log_write_list_end(android_log_context ctx); + +int android_log_write_int32(android_log_context ctx, int32_t value); +int android_log_write_int64(android_log_context ctx, int64_t value); +int android_log_write_string8(android_log_context ctx, const char *value); +int android_log_write_float32(android_log_context ctx, float value); + +/* Submit the composed list context to the specified logger id */ +/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */ +int android_log_write_list(android_log_context ctx, log_id_t id); + +/* + * Creates a context from a raw buffer representing a list of events to be read. + */ +android_log_context create_android_log_parser(const char *msg, size_t len); + +android_log_list_element android_log_read_next(android_log_context ctx); +android_log_list_element android_log_peek_next(android_log_context ctx); + +/* Finished with reader or writer context */ +int android_log_destroy(android_log_context *ctx); /* * =========================================================================== @@ -585,26 +667,6 @@ typedef enum { (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0) #endif -typedef enum log_id { - LOG_ID_MIN = 0, - -#ifndef LINT_RLOG - LOG_ID_MAIN = 0, -#endif - LOG_ID_RADIO = 1, -#ifndef LINT_RLOG - LOG_ID_EVENTS = 2, - LOG_ID_SYSTEM = 3, - LOG_ID_CRASH = 4, - LOG_ID_SECURITY = 5, - LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */ -#endif - - LOG_ID_MAX -} log_id_t; -#define sizeof_log_id_t sizeof(typeof_log_id_t) -#define typeof_log_id_t unsigned char - /* * Use the per-tag properties "log.tag." to generate a runtime * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to diff --git a/liblog/Android.mk b/liblog/Android.mk index a183db83e..c7b76d87a 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -28,6 +28,7 @@ liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.lo liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c liblog_target_sources += logprint.c liblog_target_sources += log_read.c +liblog_target_sources += log_event_list.c # Shared and static library for host # ======================================================== diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c new file mode 100644 index 000000000..50a27c090 --- /dev/null +++ b/liblog/log_event_list.c @@ -0,0 +1,520 @@ +/* + * 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)) + +typedef struct { + 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 */ + enum { + kAndroidLoggerRead = 1, + kAndroidLoggerWrite = 2, + } read_write_flag; + uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD]; +} android_log_context_internal; + +android_log_context create_android_logger(uint32_t tag) { + size_t needed, i; + android_log_context_internal *context; + + context = calloc(1, sizeof(android_log_context_internal)); + if (!context) { + return NULL; + } + 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; + + 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 = calloc(1, sizeof(android_log_context_internal)); + if (!context) { + return NULL; + } + len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD; + context->len = len; + memcpy(context->storage, msg, len); + context->read_write_flag = kAndroidLoggerRead; + + 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_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(android_log_context ctx, const char *value) { + size_t needed; + int32_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) { + return -EINVAL; + } + len = strlen(value); + needed = sizeof(uint8_t) + sizeof(len) + len; + if ((context->pos + needed) > MAX_EVENT_PAYLOAD) { + /* Truncate string for delivery */ + len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(len); + 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); + memcpy(&context->storage[context->pos + 5], value, len); + context->pos += needed; + return 0; +} + +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)) { + 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'snot 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) : + __android_log_security_bwrite(context->tag, msg, 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 = 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); +} diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index 8517c9f89..65d14569c 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 The Android Open Source Project + * Copyright (C) 2013-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. @@ -1672,3 +1672,529 @@ TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) { android_logger_list_close(logger_list); } + +static int is_real_element(int type) { + return ((type == EVENT_TYPE_INT) || + (type == EVENT_TYPE_LONG) || + (type == EVENT_TYPE_STRING) || + (type == EVENT_TYPE_FLOAT)); +} + +int android_log_buffer_to_string(const char *msg, size_t len, + char *strOut, size_t strOutLen) { + android_log_context context = create_android_log_parser(msg, len); + android_log_list_element elem; + bool overflow = false; + /* Reserve 1 byte for null terminator. */ + size_t origStrOutLen = strOutLen--; + + if (!context) { + return -EBADF; + } + + memset(&elem, 0, sizeof(elem)); + + size_t outCount; + + do { + elem = android_log_read_next(context); + switch ((int)elem.type) { + case EVENT_TYPE_LIST: + if (strOutLen == 0) { + overflow = true; + } else { + *strOut++ = '['; + strOutLen--; + } + break; + + case EVENT_TYPE_LIST_STOP: + if (strOutLen == 0) { + overflow = true; + } else { + *strOut++ = ']'; + strOutLen--; + } + break; + + case EVENT_TYPE_INT: + /* + * snprintf also requires room for the null terminator, which + * we don't care about but we have allocated enough room for + * that + */ + outCount = snprintf(strOut, strOutLen + 1, + "%" PRId32, elem.data.int32); + if (outCount <= strOutLen) { + strOut += outCount; + strOutLen -= outCount; + } else { + overflow = true; + } + break; + + case EVENT_TYPE_LONG: + /* + * snprintf also requires room for the null terminator, which + * we don't care about but we have allocated enough room for + * that + */ + outCount = snprintf(strOut, strOutLen + 1, + "%" PRId64, elem.data.int64); + if (outCount <= strOutLen) { + strOut += outCount; + strOutLen -= outCount; + } else { + overflow = true; + } + break; + + case EVENT_TYPE_FLOAT: + /* + * snprintf also requires room for the null terminator, which + * we don't care about but we have allocated enough room for + * that + */ + outCount = snprintf(strOut, strOutLen + 1, "%f", elem.data.float32); + if (outCount <= strOutLen) { + strOut += outCount; + strOutLen -= outCount; + } else { + overflow = true; + } + break; + + default: + elem.complete = true; + break; + + case EVENT_TYPE_UNKNOWN: +#if 0 // Ideal purity in the test, we want to complain about UNKNOWN showing up + if (elem.complete) { + break; + } +#endif + elem.data.string = const_cast(""); + elem.len = strlen(elem.data.string); + /* FALLTHRU */ + case EVENT_TYPE_STRING: + if (elem.len <= strOutLen) { + memcpy(strOut, elem.data.string, elem.len); + strOut += elem.len; + strOutLen -= elem.len; + } else if (strOutLen > 0) { + /* copy what we can */ + memcpy(strOut, elem.data.string, strOutLen); + strOut += strOutLen; + strOutLen = 0; + overflow = true; + } + break; + } + + if (elem.complete) { + break; + } + /* Determine whether to put a comma or not. */ + if (!overflow && (is_real_element(elem.type) || + (elem.type == EVENT_TYPE_LIST_STOP))) { + android_log_list_element next = android_log_peek_next(context); + if (!next.complete && (is_real_element(next.type) || + (next.type == EVENT_TYPE_LIST))) { + if (strOutLen == 0) { + overflow = true; + } else { + *strOut++ = ','; + strOutLen--; + } + } + } + } while ((elem.type != EVENT_TYPE_UNKNOWN) && !overflow && !elem.complete); + + android_log_destroy(&context); + + if (overflow) { + if (strOutLen < origStrOutLen) { + /* leave an indicator */ + *(strOut-1) = '!'; + } else { + /* nothing was written at all */ + *strOut++ = '!'; + } + } + *strOut++ = '\0'; + + if ((elem.type == EVENT_TYPE_UNKNOWN) && !elem.complete) { + fprintf(stderr, "Binary log entry conversion failed\n"); + return -EINVAL; + } + + return 0; +} + +static const char *event_test_int32(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010)); + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint32_t); + + return "1076895760"; +} + +static const char *event_test_int64(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010)); + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint64_t); + + return "-9191740941672636400"; +} + +static const char *event_test_list_int64(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint8_t) + + sizeof(uint8_t) + sizeof(uint64_t); + + return "[-9191740941672636400]"; +} + +static const char *event_test_simple_automagic_list(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + // The convenience API where we allow a simple list to be + // created without explicit begin or end calls. + EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010)); + EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010)); + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint8_t) + + sizeof(uint8_t) + sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint64_t); + + return "[1076895760,-9191740941672636400]"; +} + +static const char *event_test_list_empty(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint8_t); + + return "[]"; +} + +static const char *event_test_complex_nested_list(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + + EXPECT_LE(0, android_log_write_list_begin(ctx)); // [ + EXPECT_LE(0, android_log_write_int32(ctx, 0x01020304)); + EXPECT_LE(0, android_log_write_int64(ctx, 0x0102030405060708)); + EXPECT_LE(0, android_log_write_string8(ctx, "Hello World")); + EXPECT_LE(0, android_log_write_list_begin(ctx)); // [ + EXPECT_LE(0, android_log_write_int32(ctx, 1)); + EXPECT_LE(0, android_log_write_int32(ctx, 2)); + EXPECT_LE(0, android_log_write_int32(ctx, 3)); + EXPECT_LE(0, android_log_write_int32(ctx, 4)); + EXPECT_LE(0, android_log_write_list_end(ctx)); // ] + EXPECT_LE(0, android_log_write_float32(ctx, 1.0102030405060708)); + EXPECT_LE(0, android_log_write_list_end(ctx)); // ] + + // + // This one checks for the automagic list creation because a list + // begin and end was missing for it! This is actually an corner + // case, and not the behavior we morally support. The automagic API is to + // allow for a simple case of a series of objects in a single list. e.g. + // int32,int32,int32,string -> [int32,int32,int32,string] + // + EXPECT_LE(0, android_log_write_string8(ctx, "dlroW olleH")); + + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint8_t) + + sizeof(uint8_t) + sizeof(uint8_t) + + sizeof(uint8_t) + sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint64_t) + + sizeof(uint8_t) + sizeof(uint32_t) + + sizeof("Hello World") - 1 + + sizeof(uint8_t) + sizeof(uint8_t) + + 4 * (sizeof(uint8_t) + sizeof(uint32_t)) + + sizeof(uint8_t) + sizeof(uint32_t) + + sizeof(uint8_t) + sizeof(uint32_t) + + sizeof("dlroW olleH") - 1; + + return "[[16909060,72623859790382856,Hello World,[1,2,3,4],1.010203],dlroW olleH]"; +} + +static const char *event_test_7_level_prefix(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 1)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 2)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 3)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 4)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 5)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 6)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 7)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + 7 * + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t)); + + return "[[[[[[[1],2],3],4],5],6],7]"; +} + +static const char *event_test_7_level_suffix(uint32_t tag, size_t &expected_len) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(tag))); + if (!ctx) { + return NULL; + } + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 1)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 2)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 3)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 4)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 5)); + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, 6)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list_end(ctx)); + EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + + expected_len = sizeof(uint32_t) + 6 * + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t)); + + return "[1,[2,[3,[4,[5,[6]]]]]]"; +} + +// make sure all user buffers are flushed +static void print_barrier() { + std::cout.flush(); + fflush(stdout); + std::cerr.flush(); + fflush(stderr); // everything else is paranoia ... +} + +static void create_android_logger(const char *(*fn)(uint32_t tag, size_t &expected_len)) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid))); + + log_time ts(android_log_clockid()); + + size_t expected_len; + const char *expected_string = (*fn)(1005, expected_len); + + if (!expected_string) { + android_logger_list_close(logger_list); + return; + } + + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.sec < (ts.tv_sec - 1)) + || ((ts.tv_sec + 1) < log_msg.entry.sec) + || ((size_t)log_msg.entry.len != expected_len) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + ++count; + + AndroidLogFormat *logformat = android_log_format_new(); + EXPECT_TRUE(NULL != logformat); + AndroidLogEntry entry; + char msgBuf[1024]; + int processBinaryLogBuffer = android_log_processBinaryLogBuffer( + &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf)); + EXPECT_EQ(0, processBinaryLogBuffer); + if (processBinaryLogBuffer == 0) { + print_barrier(); + int printLogLine = android_log_printLogLine( + logformat, fileno(stderr), &entry); + print_barrier(); + EXPECT_EQ(20 + (int)strlen(expected_string), printLogLine); + } + android_log_format_free(logformat); + + // test buffer reading API + snprintf(msgBuf, sizeof(msgBuf), "I/[%d]", get4LE(eventData)); + print_barrier(); + fprintf(stderr, "%-10s(%5u): ", msgBuf, pid); + memset(msgBuf, 0, sizeof(msgBuf)); + int buffer_to_string = android_log_buffer_to_string( + eventData + sizeof(uint32_t), + log_msg.entry.len - sizeof(uint32_t), + msgBuf, sizeof(msgBuf)); + fprintf(stderr, "%s\n", msgBuf); + print_barrier(); + EXPECT_EQ(0, buffer_to_string); + EXPECT_EQ(strlen(expected_string), strlen(msgBuf)); + EXPECT_EQ(0, strcmp(expected_string, msgBuf)); + } + + EXPECT_EQ(1, count); + + android_logger_list_close(logger_list); +} + +TEST(liblog, create_android_logger_int32) { + create_android_logger(event_test_int32); +} + +TEST(liblog, create_android_logger_int64) { + create_android_logger(event_test_int64); +} + +TEST(liblog, create_android_logger_list_int64) { + create_android_logger(event_test_list_int64); +} + +TEST(liblog, create_android_logger_simple_automagic_list) { + create_android_logger(event_test_simple_automagic_list); +} + +TEST(liblog, create_android_logger_list_empty) { + create_android_logger(event_test_list_empty); +} + +TEST(liblog, create_android_logger_complex_nested_list) { + create_android_logger(event_test_complex_nested_list); +} + +TEST(liblog, create_android_logger_7_level_prefix) { + create_android_logger(event_test_7_level_prefix); +} + +TEST(liblog, create_android_logger_7_level_suffix) { + create_android_logger(event_test_7_level_suffix); +} + +TEST(liblog, create_android_logger_overflow) { + android_log_context ctx; + + EXPECT_TRUE(NULL != (ctx = create_android_logger(1005))); + if (ctx) { + for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) { + EXPECT_LE(0, android_log_write_list_begin(ctx)); + } + EXPECT_GT(0, android_log_write_list_begin(ctx)); + /* One more for good measure, must be permanently unhappy */ + EXPECT_GT(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_destroy(&ctx)); + EXPECT_TRUE(NULL == ctx); + } + + ASSERT_TRUE(NULL != (ctx = create_android_logger(1005))); + for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) { + EXPECT_LE(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_write_int32(ctx, i)); + } + EXPECT_GT(0, android_log_write_list_begin(ctx)); + /* One more for good measure, must be permanently unhappy */ + EXPECT_GT(0, android_log_write_list_begin(ctx)); + EXPECT_LE(0, android_log_destroy(&ctx)); + ASSERT_TRUE(NULL == ctx); +}