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