Home | History | Annotate | Download | only in liblog
      1 /* //device/libs/cutils/logprint.c
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #define _GNU_SOURCE /* for asprintf */
     19 
     20 #include <ctype.h>
     21 #include <stdio.h>
     22 #include <errno.h>
     23 #include <stdlib.h>
     24 #include <stdint.h>
     25 #include <string.h>
     26 #include <assert.h>
     27 #include <arpa/inet.h>
     28 
     29 #include <cutils/logd.h>
     30 #include <cutils/logprint.h>
     31 
     32 typedef struct FilterInfo_t {
     33     char *mTag;
     34     android_LogPriority mPri;
     35     struct FilterInfo_t *p_next;
     36 } FilterInfo;
     37 
     38 struct AndroidLogFormat_t {
     39     android_LogPriority global_pri;
     40     FilterInfo *filters;
     41     AndroidLogPrintFormat format;
     42 };
     43 
     44 static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri)
     45 {
     46     FilterInfo *p_ret;
     47 
     48     p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
     49     p_ret->mTag = strdup(tag);
     50     p_ret->mPri = pri;
     51 
     52     return p_ret;
     53 }
     54 
     55 static void filterinfo_free(FilterInfo *p_info)
     56 {
     57     if (p_info == NULL) {
     58         return;
     59     }
     60 
     61     free(p_info->mTag);
     62     p_info->mTag = NULL;
     63 }
     64 
     65 /*
     66  * Note: also accepts 0-9 priorities
     67  * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
     68  */
     69 static android_LogPriority filterCharToPri (char c)
     70 {
     71     android_LogPriority pri;
     72 
     73     c = tolower(c);
     74 
     75     if (c >= '0' && c <= '9') {
     76         if (c >= ('0'+ANDROID_LOG_SILENT)) {
     77             pri = ANDROID_LOG_VERBOSE;
     78         } else {
     79             pri = (android_LogPriority)(c - '0');
     80         }
     81     } else if (c == 'v') {
     82         pri = ANDROID_LOG_VERBOSE;
     83     } else if (c == 'd') {
     84         pri = ANDROID_LOG_DEBUG;
     85     } else if (c == 'i') {
     86         pri = ANDROID_LOG_INFO;
     87     } else if (c == 'w') {
     88         pri = ANDROID_LOG_WARN;
     89     } else if (c == 'e') {
     90         pri = ANDROID_LOG_ERROR;
     91     } else if (c == 'f') {
     92         pri = ANDROID_LOG_FATAL;
     93     } else if (c == 's') {
     94         pri = ANDROID_LOG_SILENT;
     95     } else if (c == '*') {
     96         pri = ANDROID_LOG_DEFAULT;
     97     } else {
     98         pri = ANDROID_LOG_UNKNOWN;
     99     }
    100 
    101     return pri;
    102 }
    103 
    104 static char filterPriToChar (android_LogPriority pri)
    105 {
    106     switch (pri) {
    107         case ANDROID_LOG_VERBOSE:       return 'V';
    108         case ANDROID_LOG_DEBUG:         return 'D';
    109         case ANDROID_LOG_INFO:          return 'I';
    110         case ANDROID_LOG_WARN:          return 'W';
    111         case ANDROID_LOG_ERROR:         return 'E';
    112         case ANDROID_LOG_FATAL:         return 'F';
    113         case ANDROID_LOG_SILENT:        return 'S';
    114 
    115         case ANDROID_LOG_DEFAULT:
    116         case ANDROID_LOG_UNKNOWN:
    117         default:                        return '?';
    118     }
    119 }
    120 
    121 static android_LogPriority filterPriForTag(
    122         AndroidLogFormat *p_format, const char *tag)
    123 {
    124     FilterInfo *p_curFilter;
    125 
    126     for (p_curFilter = p_format->filters
    127             ; p_curFilter != NULL
    128             ; p_curFilter = p_curFilter->p_next
    129     ) {
    130         if (0 == strcmp(tag, p_curFilter->mTag)) {
    131             if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
    132                 return p_format->global_pri;
    133             } else {
    134                 return p_curFilter->mPri;
    135             }
    136         }
    137     }
    138 
    139     return p_format->global_pri;
    140 }
    141 
    142 /** for debugging */
    143 static void dumpFilters(AndroidLogFormat *p_format)
    144 {
    145     FilterInfo *p_fi;
    146 
    147     for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) {
    148         char cPri = filterPriToChar(p_fi->mPri);
    149         if (p_fi->mPri == ANDROID_LOG_DEFAULT) {
    150             cPri = filterPriToChar(p_format->global_pri);
    151         }
    152         fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri);
    153     }
    154 
    155     fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri));
    156 
    157 }
    158 
    159 /**
    160  * returns 1 if this log line should be printed based on its priority
    161  * and tag, and 0 if it should not
    162  */
    163 int android_log_shouldPrintLine (
    164         AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
    165 {
    166     return pri >= filterPriForTag(p_format, tag);
    167 }
    168 
    169 AndroidLogFormat *android_log_format_new()
    170 {
    171     AndroidLogFormat *p_ret;
    172 
    173     p_ret = calloc(1, sizeof(AndroidLogFormat));
    174 
    175     p_ret->global_pri = ANDROID_LOG_VERBOSE;
    176     p_ret->format = FORMAT_BRIEF;
    177 
    178     return p_ret;
    179 }
    180 
    181 void android_log_format_free(AndroidLogFormat *p_format)
    182 {
    183     FilterInfo *p_info, *p_info_old;
    184 
    185     p_info = p_format->filters;
    186 
    187     while (p_info != NULL) {
    188         p_info_old = p_info;
    189         p_info = p_info->p_next;
    190 
    191         free(p_info_old);
    192     }
    193 
    194     free(p_format);
    195 }
    196 
    197 
    198 
    199 void android_log_setPrintFormat(AndroidLogFormat *p_format,
    200         AndroidLogPrintFormat format)
    201 {
    202     p_format->format=format;
    203 }
    204 
    205 /**
    206  * Returns FORMAT_OFF on invalid string
    207  */
    208 AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
    209 {
    210     static AndroidLogPrintFormat format;
    211 
    212     if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
    213     else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
    214     else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
    215     else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
    216     else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
    217     else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
    218     else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
    219     else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
    220     else format = FORMAT_OFF;
    221 
    222     return format;
    223 }
    224 
    225 /**
    226  * filterExpression: a single filter expression
    227  * eg "AT:d"
    228  *
    229  * returns 0 on success and -1 on invalid expression
    230  *
    231  * Assumes single threaded execution
    232  */
    233 
    234 int android_log_addFilterRule(AndroidLogFormat *p_format,
    235         const char *filterExpression)
    236 {
    237     size_t i=0;
    238     size_t tagNameLength;
    239     android_LogPriority pri = ANDROID_LOG_DEFAULT;
    240 
    241     tagNameLength = strcspn(filterExpression, ":");
    242 
    243     if (tagNameLength == 0) {
    244         goto error;
    245     }
    246 
    247     if(filterExpression[tagNameLength] == ':') {
    248         pri = filterCharToPri(filterExpression[tagNameLength+1]);
    249 
    250         if (pri == ANDROID_LOG_UNKNOWN) {
    251             goto error;
    252         }
    253     }
    254 
    255     if(0 == strncmp("*", filterExpression, tagNameLength)) {
    256         // This filter expression refers to the global filter
    257         // The default level for this is DEBUG if the priority
    258         // is unspecified
    259         if (pri == ANDROID_LOG_DEFAULT) {
    260             pri = ANDROID_LOG_DEBUG;
    261         }
    262 
    263         p_format->global_pri = pri;
    264     } else {
    265         // for filter expressions that don't refer to the global
    266         // filter, the default is verbose if the priority is unspecified
    267         if (pri == ANDROID_LOG_DEFAULT) {
    268             pri = ANDROID_LOG_VERBOSE;
    269         }
    270 
    271         char *tagName;
    272 
    273 // Presently HAVE_STRNDUP is never defined, so the second case is always taken
    274 // Darwin doesn't have strnup, everything else does
    275 #ifdef HAVE_STRNDUP
    276         tagName = strndup(filterExpression, tagNameLength);
    277 #else
    278         //a few extra bytes copied...
    279         tagName = strdup(filterExpression);
    280         tagName[tagNameLength] = '\0';
    281 #endif /*HAVE_STRNDUP*/
    282 
    283         FilterInfo *p_fi = filterinfo_new(tagName, pri);
    284         free(tagName);
    285 
    286         p_fi->p_next = p_format->filters;
    287         p_format->filters = p_fi;
    288     }
    289 
    290     return 0;
    291 error:
    292     return -1;
    293 }
    294 
    295 
    296 /**
    297  * filterString: a comma/whitespace-separated set of filter expressions
    298  *
    299  * eg "AT:d *:i"
    300  *
    301  * returns 0 on success and -1 on invalid expression
    302  *
    303  * Assumes single threaded execution
    304  *
    305  */
    306 
    307 int android_log_addFilterString(AndroidLogFormat *p_format,
    308         const char *filterString)
    309 {
    310     char *filterStringCopy = strdup (filterString);
    311     char *p_cur = filterStringCopy;
    312     char *p_ret;
    313     int err;
    314 
    315     // Yes, I'm using strsep
    316     while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
    317         // ignore whitespace-only entries
    318         if(p_ret[0] != '\0') {
    319             err = android_log_addFilterRule(p_format, p_ret);
    320 
    321             if (err < 0) {
    322                 goto error;
    323             }
    324         }
    325     }
    326 
    327     free (filterStringCopy);
    328     return 0;
    329 error:
    330     free (filterStringCopy);
    331     return -1;
    332 }
    333 
    334 static inline char * strip_end(char *str)
    335 {
    336     char *end = str + strlen(str) - 1;
    337 
    338     while (end >= str && isspace(*end))
    339         *end-- = '\0';
    340     return str;
    341 }
    342 
    343 /**
    344  * Splits a wire-format buffer into an AndroidLogEntry
    345  * entry allocated by caller. Pointers will point directly into buf
    346  *
    347  * Returns 0 on success and -1 on invalid wire format (entry will be
    348  * in unspecified state)
    349  */
    350 int android_log_processLogBuffer(struct logger_entry *buf,
    351                                  AndroidLogEntry *entry)
    352 {
    353     entry->tv_sec = buf->sec;
    354     entry->tv_nsec = buf->nsec;
    355     entry->pid = buf->pid;
    356     entry->tid = buf->tid;
    357 
    358     /*
    359      * format: <priority:1><tag:N>\0<message:N>\0
    360      *
    361      * tag str
    362      *   starts at buf->msg+1
    363      * msg
    364      *   starts at buf->msg+1+len(tag)+1
    365      *
    366      * The message may have been truncated by the kernel log driver.
    367      * When that happens, we must null-terminate the message ourselves.
    368      */
    369     if (buf->len < 3) {
    370         // An well-formed entry must consist of at least a priority
    371         // and two null characters
    372         fprintf(stderr, "+++ LOG: entry too small\n");
    373         return -1;
    374     }
    375 
    376     int msgStart = -1;
    377     int msgEnd = -1;
    378 
    379     int i;
    380     for (i = 1; i < buf->len; i++) {
    381         if (buf->msg[i] == '\0') {
    382             if (msgStart == -1) {
    383                 msgStart = i + 1;
    384             } else {
    385                 msgEnd = i;
    386                 break;
    387             }
    388         }
    389     }
    390 
    391     if (msgStart == -1) {
    392         fprintf(stderr, "+++ LOG: malformed log message\n");
    393         return -1;
    394     }
    395     if (msgEnd == -1) {
    396         // incoming message not null-terminated; force it
    397         msgEnd = buf->len - 1;
    398         buf->msg[msgEnd] = '\0';
    399     }
    400 
    401     entry->priority = buf->msg[0];
    402     entry->tag = buf->msg + 1;
    403     entry->message = buf->msg + msgStart;
    404     entry->messageLen = msgEnd - msgStart;
    405 
    406     return 0;
    407 }
    408 
    409 /*
    410  * Extract a 4-byte value from a byte stream.
    411  */
    412 static inline uint32_t get4LE(const uint8_t* src)
    413 {
    414     return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
    415 }
    416 
    417 /*
    418  * Extract an 8-byte value from a byte stream.
    419  */
    420 static inline uint64_t get8LE(const uint8_t* src)
    421 {
    422     uint32_t low, high;
    423 
    424     low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
    425     high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
    426     return ((long long) high << 32) | (long long) low;
    427 }
    428 
    429 
    430 /*
    431  * Recursively convert binary log data to printable form.
    432  *
    433  * This needs to be recursive because you can have lists of lists.
    434  *
    435  * If we run out of room, we stop processing immediately.  It's important
    436  * for us to check for space on every output element to avoid producing
    437  * garbled output.
    438  *
    439  * Returns 0 on success, 1 on buffer full, -1 on failure.
    440  */
    441 static int android_log_printBinaryEvent(const unsigned char** pEventData,
    442     size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
    443 {
    444     const unsigned char* eventData = *pEventData;
    445     size_t eventDataLen = *pEventDataLen;
    446     char* outBuf = *pOutBuf;
    447     size_t outBufLen = *pOutBufLen;
    448     unsigned char type;
    449     size_t outCount;
    450     int result = 0;
    451 
    452     if (eventDataLen < 1)
    453         return -1;
    454     type = *eventData++;
    455     eventDataLen--;
    456 
    457     //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
    458 
    459     switch (type) {
    460     case EVENT_TYPE_INT:
    461         /* 32-bit signed int */
    462         {
    463             int ival;
    464 
    465             if (eventDataLen < 4)
    466                 return -1;
    467             ival = get4LE(eventData);
    468             eventData += 4;
    469             eventDataLen -= 4;
    470 
    471             outCount = snprintf(outBuf, outBufLen, "%d", ival);
    472             if (outCount < outBufLen) {
    473                 outBuf += outCount;
    474                 outBufLen -= outCount;
    475             } else {
    476                 /* halt output */
    477                 goto no_room;
    478             }
    479         }
    480         break;
    481     case EVENT_TYPE_LONG:
    482         /* 64-bit signed long */
    483         {
    484             long long lval;
    485 
    486             if (eventDataLen < 8)
    487                 return -1;
    488             lval = get8LE(eventData);
    489             eventData += 8;
    490             eventDataLen -= 8;
    491 
    492             outCount = snprintf(outBuf, outBufLen, "%lld", lval);
    493             if (outCount < outBufLen) {
    494                 outBuf += outCount;
    495                 outBufLen -= outCount;
    496             } else {
    497                 /* halt output */
    498                 goto no_room;
    499             }
    500         }
    501         break;
    502     case EVENT_TYPE_STRING:
    503         /* UTF-8 chars, not NULL-terminated */
    504         {
    505             unsigned int strLen;
    506 
    507             if (eventDataLen < 4)
    508                 return -1;
    509             strLen = get4LE(eventData);
    510             eventData += 4;
    511             eventDataLen -= 4;
    512 
    513             if (eventDataLen < strLen)
    514                 return -1;
    515 
    516             if (strLen < outBufLen) {
    517                 memcpy(outBuf, eventData, strLen);
    518                 outBuf += strLen;
    519                 outBufLen -= strLen;
    520             } else if (outBufLen > 0) {
    521                 /* copy what we can */
    522                 memcpy(outBuf, eventData, outBufLen);
    523                 outBuf += outBufLen;
    524                 outBufLen -= outBufLen;
    525                 goto no_room;
    526             }
    527             eventData += strLen;
    528             eventDataLen -= strLen;
    529             break;
    530         }
    531     case EVENT_TYPE_LIST:
    532         /* N items, all different types */
    533         {
    534             unsigned char count;
    535             int i;
    536 
    537             if (eventDataLen < 1)
    538                 return -1;
    539 
    540             count = *eventData++;
    541             eventDataLen--;
    542 
    543             if (outBufLen > 0) {
    544                 *outBuf++ = '[';
    545                 outBufLen--;
    546             } else {
    547                 goto no_room;
    548             }
    549 
    550             for (i = 0; i < count; i++) {
    551                 result = android_log_printBinaryEvent(&eventData, &eventDataLen,
    552                         &outBuf, &outBufLen);
    553                 if (result != 0)
    554                     goto bail;
    555 
    556                 if (i < count-1) {
    557                     if (outBufLen > 0) {
    558                         *outBuf++ = ',';
    559                         outBufLen--;
    560                     } else {
    561                         goto no_room;
    562                     }
    563                 }
    564             }
    565 
    566             if (outBufLen > 0) {
    567                 *outBuf++ = ']';
    568                 outBufLen--;
    569             } else {
    570                 goto no_room;
    571             }
    572         }
    573         break;
    574     default:
    575         fprintf(stderr, "Unknown binary event type %d\n", type);
    576         return -1;
    577     }
    578 
    579 bail:
    580     *pEventData = eventData;
    581     *pEventDataLen = eventDataLen;
    582     *pOutBuf = outBuf;
    583     *pOutBufLen = outBufLen;
    584     return result;
    585 
    586 no_room:
    587     result = 1;
    588     goto bail;
    589 }
    590 
    591 /**
    592  * Convert a binary log entry to ASCII form.
    593  *
    594  * For convenience we mimic the processLogBuffer API.  There is no
    595  * pre-defined output length for the binary data, since we're free to format
    596  * it however we choose, which means we can't really use a fixed-size buffer
    597  * here.
    598  */
    599 int android_log_processBinaryLogBuffer(struct logger_entry *buf,
    600     AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
    601     int messageBufLen)
    602 {
    603     size_t inCount;
    604     unsigned int tagIndex;
    605     const unsigned char* eventData;
    606 
    607     entry->tv_sec = buf->sec;
    608     entry->tv_nsec = buf->nsec;
    609     entry->priority = ANDROID_LOG_INFO;
    610     entry->pid = buf->pid;
    611     entry->tid = buf->tid;
    612 
    613     /*
    614      * Pull the tag out.
    615      */
    616     eventData = (const unsigned char*) buf->msg;
    617     inCount = buf->len;
    618     if (inCount < 4)
    619         return -1;
    620     tagIndex = get4LE(eventData);
    621     eventData += 4;
    622     inCount -= 4;
    623 
    624     if (map != NULL) {
    625         entry->tag = android_lookupEventTag(map, tagIndex);
    626     } else {
    627         entry->tag = NULL;
    628     }
    629 
    630     /*
    631      * If we don't have a map, or didn't find the tag number in the map,
    632      * stuff a generated tag value into the start of the output buffer and
    633      * shift the buffer pointers down.
    634      */
    635     if (entry->tag == NULL) {
    636         int tagLen;
    637 
    638         tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
    639         entry->tag = messageBuf;
    640         messageBuf += tagLen+1;
    641         messageBufLen -= tagLen+1;
    642     }
    643 
    644     /*
    645      * Format the event log data into the buffer.
    646      */
    647     char* outBuf = messageBuf;
    648     size_t outRemaining = messageBufLen-1;      /* leave one for nul byte */
    649     int result;
    650     result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
    651                 &outRemaining);
    652     if (result < 0) {
    653         fprintf(stderr, "Binary log entry conversion failed\n");
    654         return -1;
    655     } else if (result == 1) {
    656         if (outBuf > messageBuf) {
    657             /* leave an indicator */
    658             *(outBuf-1) = '!';
    659         } else {
    660             /* no room to output anything at all */
    661             *outBuf++ = '!';
    662             outRemaining--;
    663         }
    664         /* pretend we ate all the data */
    665         inCount = 0;
    666     }
    667 
    668     /* eat the silly terminating '\n' */
    669     if (inCount == 1 && *eventData == '\n') {
    670         eventData++;
    671         inCount--;
    672     }
    673 
    674     if (inCount != 0) {
    675         fprintf(stderr,
    676             "Warning: leftover binary log data (%zu bytes)\n", inCount);
    677     }
    678 
    679     /*
    680      * Terminate the buffer.  The NUL byte does not count as part of
    681      * entry->messageLen.
    682      */
    683     *outBuf = '\0';
    684     entry->messageLen = outBuf - messageBuf;
    685     assert(entry->messageLen == (messageBufLen-1) - outRemaining);
    686 
    687     entry->message = messageBuf;
    688 
    689     return 0;
    690 }
    691 
    692 /**
    693  * Formats a log message into a buffer
    694  *
    695  * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
    696  * If return value != defaultBuffer, caller must call free()
    697  * Returns NULL on malloc error
    698  */
    699 
    700 char *android_log_formatLogLine (
    701     AndroidLogFormat *p_format,
    702     char *defaultBuffer,
    703     size_t defaultBufferSize,
    704     const AndroidLogEntry *entry,
    705     size_t *p_outLength)
    706 {
    707 #if defined(HAVE_LOCALTIME_R)
    708     struct tm tmBuf;
    709 #endif
    710     struct tm* ptm;
    711     char timeBuf[32];
    712     char headerBuf[128];
    713     char prefixBuf[128], suffixBuf[128];
    714     char priChar;
    715     int prefixSuffixIsHeaderFooter = 0;
    716     char * ret = NULL;
    717 
    718     priChar = filterPriToChar(entry->priority);
    719 
    720     /*
    721      * Get the current date/time in pretty form
    722      *
    723      * It's often useful when examining a log with "less" to jump to
    724      * a specific point in the file by searching for the date/time stamp.
    725      * For this reason it's very annoying to have regexp meta characters
    726      * in the time stamp.  Don't use forward slashes, parenthesis,
    727      * brackets, asterisks, or other special chars here.
    728      */
    729 #if defined(HAVE_LOCALTIME_R)
    730     ptm = localtime_r(&(entry->tv_sec), &tmBuf);
    731 #else
    732     ptm = localtime(&(entry->tv_sec));
    733 #endif
    734     //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
    735     strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
    736 
    737     /*
    738      * Construct a buffer containing the log header and log message.
    739      */
    740     size_t prefixLen, suffixLen;
    741 
    742     switch (p_format->format) {
    743         case FORMAT_TAG:
    744             prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    745                 "%c/%-8s: ", priChar, entry->tag);
    746             strcpy(suffixBuf, "\n"); suffixLen = 1;
    747             break;
    748         case FORMAT_PROCESS:
    749             prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    750                 "%c(%5d) ", priChar, entry->pid);
    751             suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
    752                 "  (%s)\n", entry->tag);
    753             break;
    754         case FORMAT_THREAD:
    755             prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    756                 "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
    757             strcpy(suffixBuf, "\n");
    758             suffixLen = 1;
    759             break;
    760         case FORMAT_RAW:
    761             prefixBuf[0] = 0;
    762             prefixLen = 0;
    763             strcpy(suffixBuf, "\n");
    764             suffixLen = 1;
    765             break;
    766         case FORMAT_TIME:
    767             prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    768                 "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
    769                 priChar, entry->tag, entry->pid);
    770             strcpy(suffixBuf, "\n");
    771             suffixLen = 1;
    772             break;
    773         case FORMAT_THREADTIME:
    774             prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    775                 "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
    776                 entry->pid, entry->tid, priChar, entry->tag);
    777             strcpy(suffixBuf, "\n");
    778             suffixLen = 1;
    779             break;
    780         case FORMAT_LONG:
    781             prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    782                 "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
    783                 timeBuf, entry->tv_nsec / 1000000, entry->pid,
    784                 entry->tid, priChar, entry->tag);
    785             strcpy(suffixBuf, "\n\n");
    786             suffixLen = 2;
    787             prefixSuffixIsHeaderFooter = 1;
    788             break;
    789         case FORMAT_BRIEF:
    790         default:
    791             prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    792                 "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
    793             strcpy(suffixBuf, "\n");
    794             suffixLen = 1;
    795             break;
    796     }
    797     /* snprintf has a weird return value.   It returns what would have been
    798      * written given a large enough buffer.  In the case that the prefix is
    799      * longer then our buffer(128), it messes up the calculations below
    800      * possibly causing heap corruption.  To avoid this we double check and
    801      * set the length at the maximum (size minus null byte)
    802      */
    803     if(prefixLen >= sizeof(prefixBuf))
    804         prefixLen = sizeof(prefixBuf) - 1;
    805     if(suffixLen >= sizeof(suffixBuf))
    806         suffixLen = sizeof(suffixBuf) - 1;
    807 
    808     /* the following code is tragically unreadable */
    809 
    810     size_t numLines;
    811     size_t i;
    812     char *p;
    813     size_t bufferSize;
    814     const char *pm;
    815 
    816     if (prefixSuffixIsHeaderFooter) {
    817         // we're just wrapping message with a header/footer
    818         numLines = 1;
    819     } else {
    820         pm = entry->message;
    821         numLines = 0;
    822 
    823         // The line-end finding here must match the line-end finding
    824         // in for ( ... numLines...) loop below
    825         while (pm < (entry->message + entry->messageLen)) {
    826             if (*pm++ == '\n') numLines++;
    827         }
    828         // plus one line for anything not newline-terminated at the end
    829         if (pm > entry->message && *(pm-1) != '\n') numLines++;
    830     }
    831 
    832     // this is an upper bound--newlines in message may be counted
    833     // extraneously
    834     bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
    835 
    836     if (defaultBufferSize >= bufferSize) {
    837         ret = defaultBuffer;
    838     } else {
    839         ret = (char *)malloc(bufferSize);
    840 
    841         if (ret == NULL) {
    842             return ret;
    843         }
    844     }
    845 
    846     ret[0] = '\0';       /* to start strcat off */
    847 
    848     p = ret;
    849     pm = entry->message;
    850 
    851     if (prefixSuffixIsHeaderFooter) {
    852         strcat(p, prefixBuf);
    853         p += prefixLen;
    854         strncat(p, entry->message, entry->messageLen);
    855         p += entry->messageLen;
    856         strcat(p, suffixBuf);
    857         p += suffixLen;
    858     } else {
    859         while(pm < (entry->message + entry->messageLen)) {
    860             const char *lineStart;
    861             size_t lineLen;
    862             lineStart = pm;
    863 
    864             // Find the next end-of-line in message
    865             while (pm < (entry->message + entry->messageLen)
    866                     && *pm != '\n') pm++;
    867             lineLen = pm - lineStart;
    868 
    869             strcat(p, prefixBuf);
    870             p += prefixLen;
    871             strncat(p, lineStart, lineLen);
    872             p += lineLen;
    873             strcat(p, suffixBuf);
    874             p += suffixLen;
    875 
    876             if (*pm == '\n') pm++;
    877         }
    878     }
    879 
    880     if (p_outLength != NULL) {
    881         *p_outLength = p - ret;
    882     }
    883 
    884     return ret;
    885 }
    886 
    887 /**
    888  * Either print or do not print log line, based on filter
    889  *
    890  * Returns count bytes written
    891  */
    892 
    893 int android_log_printLogLine(
    894     AndroidLogFormat *p_format,
    895     int fd,
    896     const AndroidLogEntry *entry)
    897 {
    898     int ret;
    899     char defaultBuffer[512];
    900     char *outBuffer = NULL;
    901     size_t totalLen;
    902 
    903     outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
    904             sizeof(defaultBuffer), entry, &totalLen);
    905 
    906     if (!outBuffer)
    907         return -1;
    908 
    909     do {
    910         ret = write(fd, outBuffer, totalLen);
    911     } while (ret < 0 && errno == EINTR);
    912 
    913     if (ret < 0) {
    914         fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
    915         ret = 0;
    916         goto done;
    917     }
    918 
    919     if (((size_t)ret) < totalLen) {
    920         fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
    921                 (int)totalLen);
    922         goto done;
    923     }
    924 
    925 done:
    926     if (outBuffer != defaultBuffer) {
    927         free(outBuffer);
    928     }
    929 
    930     return ret;
    931 }
    932 
    933 
    934 
    935 void logprint_run_tests()
    936 {
    937 #if 0
    938 
    939     fprintf(stderr, "tests disabled\n");
    940 
    941 #else
    942 
    943     int err;
    944     const char *tag;
    945     AndroidLogFormat *p_format;
    946 
    947     p_format = android_log_format_new();
    948 
    949     fprintf(stderr, "running tests\n");
    950 
    951     tag = "random";
    952 
    953     android_log_addFilterRule(p_format,"*:i");
    954 
    955     assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
    956     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
    957     android_log_addFilterRule(p_format, "*");
    958     assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
    959     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
    960     android_log_addFilterRule(p_format, "*:v");
    961     assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
    962     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
    963     android_log_addFilterRule(p_format, "*:i");
    964     assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
    965     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
    966 
    967     android_log_addFilterRule(p_format, "random");
    968     assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
    969     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
    970     android_log_addFilterRule(p_format, "random:v");
    971     assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
    972     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
    973     android_log_addFilterRule(p_format, "random:d");
    974     assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
    975     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
    976     android_log_addFilterRule(p_format, "random:w");
    977     assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
    978     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
    979 
    980     android_log_addFilterRule(p_format, "crap:*");
    981     assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap"));
    982     assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0);
    983 
    984     // invalid expression
    985     err = android_log_addFilterRule(p_format, "random:z");
    986     assert (err < 0);
    987     assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
    988     assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
    989 
    990     // Issue #550946
    991     err = android_log_addFilterString(p_format, " ");
    992     assert(err == 0);
    993     assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
    994 
    995     // note trailing space
    996     err = android_log_addFilterString(p_format, "*:s random:d ");
    997     assert(err == 0);
    998     assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
    999 
   1000     err = android_log_addFilterString(p_format, "*:s random:z");
   1001     assert(err < 0);
   1002 
   1003 
   1004 #if 0
   1005     char *ret;
   1006     char defaultBuffer[512];
   1007 
   1008     ret = android_log_formatLogLine(p_format,
   1009         defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123,
   1010         123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL);
   1011 #endif
   1012 
   1013 
   1014     fprintf(stderr, "tests complete\n");
   1015 #endif
   1016 }
   1017