liblog: event log list logging handler

(cherry pick from commit bd1ad049b2)

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
This commit is contained in:
Mark Salyzyn 2016-02-17 16:08:13 -08:00
parent ac460fc6de
commit 9dd6510dd0
4 changed files with 1138 additions and 29 deletions

View File

@ -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.<tagname>" to generate a runtime
* result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to

View File

@ -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
# ========================================================

520
liblog/log_event_list.c Normal file
View File

@ -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);
}

View File

@ -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<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);
}