Merge "liblog: logcat: Add printable format modifier"

This commit is contained in:
Mark Salyzyn 2015-06-04 15:34:48 +00:00 committed by Gerrit Code Review
commit 79ae578edd
3 changed files with 186 additions and 33 deletions

View File

@ -36,9 +36,10 @@ typedef enum {
FORMAT_TIME,
FORMAT_THREADTIME,
FORMAT_LONG,
/* The following two are modifiers to above formats */
/* The following three are modifiers to above formats */
FORMAT_MODIFIER_COLOR, /* converts priority to color */
FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
} AndroidLogPrintFormat;
typedef struct AndroidLogFormat_t AndroidLogFormat;

View File

@ -32,6 +32,9 @@
#include <log/logd.h>
#include <log/logprint.h>
/* open coded fragment, prevent circular dependencies */
#define WEAK static
typedef struct FilterInfo_t {
char *mTag;
android_LogPriority mPri;
@ -44,6 +47,7 @@ struct AndroidLogFormat_t {
AndroidLogPrintFormat format;
bool colored_output;
bool usec_time_output;
bool printable_output;
};
/*
@ -187,6 +191,7 @@ AndroidLogFormat *android_log_format_new()
p_ret->format = FORMAT_BRIEF;
p_ret->colored_output = false;
p_ret->usec_time_output = false;
p_ret->printable_output = false;
return p_ret;
}
@ -212,13 +217,18 @@ void android_log_format_free(AndroidLogFormat *p_format)
int android_log_setPrintFormat(AndroidLogFormat *p_format,
AndroidLogPrintFormat format)
{
if (format == FORMAT_MODIFIER_COLOR) {
switch (format) {
case FORMAT_MODIFIER_COLOR:
p_format->colored_output = true;
return 0;
}
if (format == FORMAT_MODIFIER_TIME_USEC) {
case FORMAT_MODIFIER_TIME_USEC:
p_format->usec_time_output = true;
return 0;
case FORMAT_MODIFIER_PRINTABLE:
p_format->printable_output = true;
return 0;
default:
break;
}
p_format->format = format;
return 1;
@ -241,6 +251,7 @@ AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
else format = FORMAT_OFF;
return format;
@ -276,29 +287,35 @@ int android_log_addFilterRule(AndroidLogFormat *p_format,
}
if(0 == strncmp("*", filterExpression, tagNameLength)) {
// This filter expression refers to the global filter
// The default level for this is DEBUG if the priority
// is unspecified
/*
* This filter expression refers to the global filter
* The default level for this is DEBUG if the priority
* is unspecified
*/
if (pri == ANDROID_LOG_DEFAULT) {
pri = ANDROID_LOG_DEBUG;
}
p_format->global_pri = pri;
} else {
// for filter expressions that don't refer to the global
// filter, the default is verbose if the priority is unspecified
/*
* for filter expressions that don't refer to the global
* filter, the default is verbose if the priority is unspecified
*/
if (pri == ANDROID_LOG_DEFAULT) {
pri = ANDROID_LOG_VERBOSE;
}
char *tagName;
// Presently HAVE_STRNDUP is never defined, so the second case is always taken
// Darwin doesn't have strnup, everything else does
/*
* Presently HAVE_STRNDUP is never defined, so the second case is always taken
* Darwin doesn't have strnup, everything else does
*/
#ifdef HAVE_STRNDUP
tagName = strndup(filterExpression, tagNameLength);
#else
//a few extra bytes copied...
/* a few extra bytes copied... */
tagName = strdup(filterExpression);
tagName[tagNameLength] = '\0';
#endif /*HAVE_STRNDUP*/
@ -335,9 +352,9 @@ int android_log_addFilterString(AndroidLogFormat *p_format,
char *p_ret;
int err;
// Yes, I'm using strsep
/* Yes, I'm using strsep */
while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
// ignore whitespace-only entries
/* ignore whitespace-only entries */
if(p_ret[0] != '\0') {
err = android_log_addFilterRule(p_format, p_ret);
@ -381,8 +398,10 @@ int android_log_processLogBuffer(struct logger_entry *buf,
* When that happens, we must null-terminate the message ourselves.
*/
if (buf->len < 3) {
// An well-formed entry must consist of at least a priority
// and two null characters
/*
* An well-formed entry must consist of at least a priority
* and two null characters
*/
fprintf(stderr, "+++ LOG: entry too small\n");
return -1;
}
@ -412,7 +431,7 @@ int android_log_processLogBuffer(struct logger_entry *buf,
return -1;
}
if (msgEnd == -1) {
// incoming message not null-terminated; force it
/* incoming message not null-terminated; force it */
msgEnd = buf->len - 1;
msg[msgEnd] = '\0';
}
@ -473,8 +492,6 @@ static int android_log_printBinaryEvent(const unsigned char** pEventData,
type = *eventData++;
eventDataLen--;
//fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
switch (type) {
case EVENT_TYPE_INT:
/* 32-bit signed int */
@ -735,6 +752,122 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf,
return 0;
}
/*
* One utf8 character at a time
*
* Returns the length of the utf8 character in the buffer,
* or -1 if illegal or truncated
*
* Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
* can not remove from here because of library circular dependencies.
* Expect one-day utf8_character_length with the same signature could
* _also_ be part of libutils/Unicode.cpp if its usefullness needs to
* propagate globally.
*/
WEAK ssize_t utf8_character_length(const char *src, size_t len)
{
const char *cur = src;
const char first_char = *cur++;
static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
int32_t mask, to_ignore_mask;
size_t num_to_read;
uint32_t utf32;
if ((first_char & 0x80) == 0) { /* ASCII */
return 1;
}
/*
* (UTF-8's character must not be like 10xxxxxx,
* but 110xxxxx, 1110xxxx, ... or 1111110x)
*/
if ((first_char & 0x40) == 0) {
return -1;
}
for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
num_to_read < 5 && (first_char & mask);
num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
if (num_to_read > len) {
return -1;
}
if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
return -1;
}
utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
}
/* "first_char" must be (110xxxxx - 11110xxx) */
if (num_to_read >= 5) {
return -1;
}
to_ignore_mask |= mask;
utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
if (utf32 > kUnicodeMaxCodepoint) {
return -1;
}
return num_to_read;
}
/*
* Convert to printable from message to p buffer, return string length. If p is
* NULL, do not copy, but still return the expected string length.
*/
static size_t convertPrintable(char *p, const char *message, size_t messageLen)
{
char *begin = p;
bool print = p != NULL;
while (messageLen) {
char buf[6];
ssize_t len = sizeof(buf) - 1;
if ((size_t)len > messageLen) {
len = messageLen;
}
len = utf8_character_length(message, len);
if (len < 0) {
snprintf(buf, sizeof(buf),
((messageLen > 1) && isdigit(message[1]))
? "\\%03o"
: "\\%o",
*message & 0377);
len = 1;
} else {
buf[0] = '\0';
if (len == 1) {
if (*message == '\a') {
strcpy(buf, "\\a");
} else if (*message == '\b') {
strcpy(buf, "\\b");
} else if (*message == '\t') {
strcpy(buf, "\\t");
} else if (*message == '\v') {
strcpy(buf, "\\v");
} else if (*message == '\f') {
strcpy(buf, "\\f");
} else if (*message == '\r') {
strcpy(buf, "\\r");
} else if (*message == '\\') {
strcpy(buf, "\\\\");
} else if ((*message < ' ') || (*message & 0x80)) {
snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
}
}
if (!buf[0]) {
strncpy(buf, message, len);
buf[len] = '\0';
}
}
if (print) {
strcpy(p, buf);
}
p += strlen(buf);
message += len;
messageLen -= len;
}
return p - begin;
}
/**
* Formats a log message into a buffer
*
@ -778,7 +911,7 @@ char *android_log_formatLogLine (
#else
ptm = localtime(&(entry->tv_sec));
#endif
//strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
/* strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); */
strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
len = strlen(timeBuf);
if (p_format->usec_time_output) {
@ -873,24 +1006,34 @@ char *android_log_formatLogLine (
const char *pm;
if (prefixSuffixIsHeaderFooter) {
// we're just wrapping message with a header/footer
/* we're just wrapping message with a header/footer */
numLines = 1;
} else {
pm = entry->message;
numLines = 0;
// The line-end finding here must match the line-end finding
// in for ( ... numLines...) loop below
/*
* The line-end finding here must match the line-end finding
* in for ( ... numLines...) loop below
*/
while (pm < (entry->message + entry->messageLen)) {
if (*pm++ == '\n') numLines++;
}
// plus one line for anything not newline-terminated at the end
/* plus one line for anything not newline-terminated at the end */
if (pm > entry->message && *(pm-1) != '\n') numLines++;
}
// this is an upper bound--newlines in message may be counted
// extraneously
bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
/*
* this is an upper bound--newlines in message may be counted
* extraneously
*/
bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
if (p_format->printable_output) {
/* Calculate extra length to convert non-printable to printable */
bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
} else {
bufferSize += entry->messageLen;
}
if (defaultBufferSize >= bufferSize) {
ret = defaultBuffer;
@ -910,8 +1053,12 @@ char *android_log_formatLogLine (
if (prefixSuffixIsHeaderFooter) {
strcat(p, prefixBuf);
p += prefixLen;
strncat(p, entry->message, entry->messageLen);
p += entry->messageLen;
if (p_format->printable_output) {
p += convertPrintable(p, entry->message, entry->messageLen);
} else {
strncat(p, entry->message, entry->messageLen);
p += entry->messageLen;
}
strcat(p, suffixBuf);
p += suffixLen;
} else {
@ -920,15 +1067,19 @@ char *android_log_formatLogLine (
size_t lineLen;
lineStart = pm;
// Find the next end-of-line in message
/* Find the next end-of-line in message */
while (pm < (entry->message + entry->messageLen)
&& *pm != '\n') pm++;
lineLen = pm - lineStart;
strcat(p, prefixBuf);
p += prefixLen;
strncat(p, lineStart, lineLen);
p += lineLen;
if (p_format->printable_output) {
p += convertPrintable(p, lineStart, lineLen);
} else {
strncat(p, lineStart, lineLen);
p += lineLen;
}
strcat(p, suffixBuf);
p += suffixLen;

View File

@ -253,7 +253,8 @@ static void show_help(const char *cmd)
" -r <kbytes> Rotate log every kbytes. Requires -f\n"
" -n <count> Sets max number of rotated logs to <count>, default 4\n"
" -v <format> Sets the log print format, where <format> is:\n\n"
" brief color long process raw tag thread threadtime time usec\n\n"
" brief color long printable process raw tag thread\n"
" threadtime time usec\n\n"
" -D print dividers between each log buffer\n"
" -c clear (flush) the entire log and exit\n"
" -d dump the log and then exit (don't block)\n"