liblog: event log list logging handler
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 <salyzyn@google.com>
Bug: 19235719
Change-Id: I4aa1927e8e6a75f0a129d15a27c891cf1ccd4f5c
diff --git a/include/log/log.h b/include/log/log.h
index 1bd9165..6ad6f0a 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -484,15 +484,19 @@
*/
/*
- * 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 @@
#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 @@
(__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.<tagname>" 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 a183db8..c7b76d8 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -28,6 +28,7 @@
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 0000000..50a27c0
--- /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 <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#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 f8d4c4c..1317853 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.
@@ -1741,3 +1741,529 @@
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<char *>("<unknown>");
+ 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 <oops> 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);
+}