logprint.c revision 5045f575252ce3d25304ba26cec299395c02152c
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 <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 <log/logd.h>
30#include <log/logprint.h>
31
32typedef struct FilterInfo_t {
33    char *mTag;
34    android_LogPriority mPri;
35    struct FilterInfo_t *p_next;
36} FilterInfo;
37
38struct AndroidLogFormat_t {
39    android_LogPriority global_pri;
40    FilterInfo *filters;
41    AndroidLogPrintFormat format;
42};
43
44static 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
55static 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 */
69static 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
104static 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
121static 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 */
143static 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 */
163int android_log_shouldPrintLine (
164        AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
165{
166    return pri >= filterPriForTag(p_format, tag);
167}
168
169AndroidLogFormat *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
181void 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
199void 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 */
208AndroidLogPrintFormat 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
234int 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;
291error:
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
307int 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;
329error:
330    free (filterStringCopy);
331    return -1;
332}
333
334static 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 */
350int 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    char *msg = buf->msg;
381    struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
382    if (buf2->hdr_size) {
383        msg = ((char *)buf2) + buf2->hdr_size;
384    }
385    for (i = 1; i < buf->len; i++) {
386        if (msg[i] == '\0') {
387            if (msgStart == -1) {
388                msgStart = i + 1;
389            } else {
390                msgEnd = i;
391                break;
392            }
393        }
394    }
395
396    if (msgStart == -1) {
397        fprintf(stderr, "+++ LOG: malformed log message\n");
398        return -1;
399    }
400    if (msgEnd == -1) {
401        // incoming message not null-terminated; force it
402        msgEnd = buf->len - 1;
403        msg[msgEnd] = '\0';
404    }
405
406    entry->priority = msg[0];
407    entry->tag = msg + 1;
408    entry->message = msg + msgStart;
409    entry->messageLen = msgEnd - msgStart;
410
411    return 0;
412}
413
414/*
415 * Extract a 4-byte value from a byte stream.
416 */
417static inline uint32_t get4LE(const uint8_t* src)
418{
419    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
420}
421
422/*
423 * Extract an 8-byte value from a byte stream.
424 */
425static inline uint64_t get8LE(const uint8_t* src)
426{
427    uint32_t low, high;
428
429    low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
430    high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
431    return ((long long) high << 32) | (long long) low;
432}
433
434
435/*
436 * Recursively convert binary log data to printable form.
437 *
438 * This needs to be recursive because you can have lists of lists.
439 *
440 * If we run out of room, we stop processing immediately.  It's important
441 * for us to check for space on every output element to avoid producing
442 * garbled output.
443 *
444 * Returns 0 on success, 1 on buffer full, -1 on failure.
445 */
446static int android_log_printBinaryEvent(const unsigned char** pEventData,
447    size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
448{
449    const unsigned char* eventData = *pEventData;
450    size_t eventDataLen = *pEventDataLen;
451    char* outBuf = *pOutBuf;
452    size_t outBufLen = *pOutBufLen;
453    unsigned char type;
454    size_t outCount;
455    int result = 0;
456
457    if (eventDataLen < 1)
458        return -1;
459    type = *eventData++;
460    eventDataLen--;
461
462    //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
463
464    switch (type) {
465    case EVENT_TYPE_INT:
466        /* 32-bit signed int */
467        {
468            int ival;
469
470            if (eventDataLen < 4)
471                return -1;
472            ival = get4LE(eventData);
473            eventData += 4;
474            eventDataLen -= 4;
475
476            outCount = snprintf(outBuf, outBufLen, "%d", ival);
477            if (outCount < outBufLen) {
478                outBuf += outCount;
479                outBufLen -= outCount;
480            } else {
481                /* halt output */
482                goto no_room;
483            }
484        }
485        break;
486    case EVENT_TYPE_LONG:
487        /* 64-bit signed long */
488        {
489            long long lval;
490
491            if (eventDataLen < 8)
492                return -1;
493            lval = get8LE(eventData);
494            eventData += 8;
495            eventDataLen -= 8;
496
497            outCount = snprintf(outBuf, outBufLen, "%lld", lval);
498            if (outCount < outBufLen) {
499                outBuf += outCount;
500                outBufLen -= outCount;
501            } else {
502                /* halt output */
503                goto no_room;
504            }
505        }
506        break;
507    case EVENT_TYPE_STRING:
508        /* UTF-8 chars, not NULL-terminated */
509        {
510            unsigned int strLen;
511
512            if (eventDataLen < 4)
513                return -1;
514            strLen = get4LE(eventData);
515            eventData += 4;
516            eventDataLen -= 4;
517
518            if (eventDataLen < strLen)
519                return -1;
520
521            if (strLen < outBufLen) {
522                memcpy(outBuf, eventData, strLen);
523                outBuf += strLen;
524                outBufLen -= strLen;
525            } else if (outBufLen > 0) {
526                /* copy what we can */
527                memcpy(outBuf, eventData, outBufLen);
528                outBuf += outBufLen;
529                outBufLen -= outBufLen;
530                goto no_room;
531            }
532            eventData += strLen;
533            eventDataLen -= strLen;
534            break;
535        }
536    case EVENT_TYPE_LIST:
537        /* N items, all different types */
538        {
539            unsigned char count;
540            int i;
541
542            if (eventDataLen < 1)
543                return -1;
544
545            count = *eventData++;
546            eventDataLen--;
547
548            if (outBufLen > 0) {
549                *outBuf++ = '[';
550                outBufLen--;
551            } else {
552                goto no_room;
553            }
554
555            for (i = 0; i < count; i++) {
556                result = android_log_printBinaryEvent(&eventData, &eventDataLen,
557                        &outBuf, &outBufLen);
558                if (result != 0)
559                    goto bail;
560
561                if (i < count-1) {
562                    if (outBufLen > 0) {
563                        *outBuf++ = ',';
564                        outBufLen--;
565                    } else {
566                        goto no_room;
567                    }
568                }
569            }
570
571            if (outBufLen > 0) {
572                *outBuf++ = ']';
573                outBufLen--;
574            } else {
575                goto no_room;
576            }
577        }
578        break;
579    default:
580        fprintf(stderr, "Unknown binary event type %d\n", type);
581        return -1;
582    }
583
584bail:
585    *pEventData = eventData;
586    *pEventDataLen = eventDataLen;
587    *pOutBuf = outBuf;
588    *pOutBufLen = outBufLen;
589    return result;
590
591no_room:
592    result = 1;
593    goto bail;
594}
595
596/**
597 * Convert a binary log entry to ASCII form.
598 *
599 * For convenience we mimic the processLogBuffer API.  There is no
600 * pre-defined output length for the binary data, since we're free to format
601 * it however we choose, which means we can't really use a fixed-size buffer
602 * here.
603 */
604int android_log_processBinaryLogBuffer(struct logger_entry *buf,
605    AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
606    int messageBufLen)
607{
608    size_t inCount;
609    unsigned int tagIndex;
610    const unsigned char* eventData;
611
612    entry->tv_sec = buf->sec;
613    entry->tv_nsec = buf->nsec;
614    entry->priority = ANDROID_LOG_INFO;
615    entry->pid = buf->pid;
616    entry->tid = buf->tid;
617
618    /*
619     * Pull the tag out.
620     */
621    eventData = (const unsigned char*) buf->msg;
622    struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
623    if (buf2->hdr_size) {
624        eventData = ((unsigned char *)buf2) + buf2->hdr_size;
625    }
626    inCount = buf->len;
627    if (inCount < 4)
628        return -1;
629    tagIndex = get4LE(eventData);
630    eventData += 4;
631    inCount -= 4;
632
633    if (map != NULL) {
634        entry->tag = android_lookupEventTag(map, tagIndex);
635    } else {
636        entry->tag = NULL;
637    }
638
639    /*
640     * If we don't have a map, or didn't find the tag number in the map,
641     * stuff a generated tag value into the start of the output buffer and
642     * shift the buffer pointers down.
643     */
644    if (entry->tag == NULL) {
645        int tagLen;
646
647        tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
648        entry->tag = messageBuf;
649        messageBuf += tagLen+1;
650        messageBufLen -= tagLen+1;
651    }
652
653    /*
654     * Format the event log data into the buffer.
655     */
656    char* outBuf = messageBuf;
657    size_t outRemaining = messageBufLen-1;      /* leave one for nul byte */
658    int result;
659    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
660                &outRemaining);
661    if (result < 0) {
662        fprintf(stderr, "Binary log entry conversion failed\n");
663        return -1;
664    } else if (result == 1) {
665        if (outBuf > messageBuf) {
666            /* leave an indicator */
667            *(outBuf-1) = '!';
668        } else {
669            /* no room to output anything at all */
670            *outBuf++ = '!';
671            outRemaining--;
672        }
673        /* pretend we ate all the data */
674        inCount = 0;
675    }
676
677    /* eat the silly terminating '\n' */
678    if (inCount == 1 && *eventData == '\n') {
679        eventData++;
680        inCount--;
681    }
682
683    if (inCount != 0) {
684        fprintf(stderr,
685            "Warning: leftover binary log data (%zu bytes)\n", inCount);
686    }
687
688    /*
689     * Terminate the buffer.  The NUL byte does not count as part of
690     * entry->messageLen.
691     */
692    *outBuf = '\0';
693    entry->messageLen = outBuf - messageBuf;
694    assert(entry->messageLen == (messageBufLen-1) - outRemaining);
695
696    entry->message = messageBuf;
697
698    return 0;
699}
700
701/**
702 * Formats a log message into a buffer
703 *
704 * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
705 * If return value != defaultBuffer, caller must call free()
706 * Returns NULL on malloc error
707 */
708
709char *android_log_formatLogLine (
710    AndroidLogFormat *p_format,
711    char *defaultBuffer,
712    size_t defaultBufferSize,
713    const AndroidLogEntry *entry,
714    size_t *p_outLength)
715{
716#if defined(HAVE_LOCALTIME_R)
717    struct tm tmBuf;
718#endif
719    struct tm* ptm;
720    char timeBuf[32];
721    char headerBuf[128];
722    char prefixBuf[128], suffixBuf[128];
723    char priChar;
724    int prefixSuffixIsHeaderFooter = 0;
725    char * ret = NULL;
726
727    priChar = filterPriToChar(entry->priority);
728
729    /*
730     * Get the current date/time in pretty form
731     *
732     * It's often useful when examining a log with "less" to jump to
733     * a specific point in the file by searching for the date/time stamp.
734     * For this reason it's very annoying to have regexp meta characters
735     * in the time stamp.  Don't use forward slashes, parenthesis,
736     * brackets, asterisks, or other special chars here.
737     */
738#if defined(HAVE_LOCALTIME_R)
739    ptm = localtime_r(&(entry->tv_sec), &tmBuf);
740#else
741    ptm = localtime(&(entry->tv_sec));
742#endif
743    //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
744    strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
745
746    /*
747     * Construct a buffer containing the log header and log message.
748     */
749    size_t prefixLen, suffixLen;
750
751    switch (p_format->format) {
752        case FORMAT_TAG:
753            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
754                "%c/%-8s: ", priChar, entry->tag);
755            strcpy(suffixBuf, "\n"); suffixLen = 1;
756            break;
757        case FORMAT_PROCESS:
758            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
759                "%c(%5d) ", priChar, entry->pid);
760            suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
761                "  (%s)\n", entry->tag);
762            break;
763        case FORMAT_THREAD:
764            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
765                "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
766            strcpy(suffixBuf, "\n");
767            suffixLen = 1;
768            break;
769        case FORMAT_RAW:
770            prefixBuf[0] = 0;
771            prefixLen = 0;
772            strcpy(suffixBuf, "\n");
773            suffixLen = 1;
774            break;
775        case FORMAT_TIME:
776            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
777                "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
778                priChar, entry->tag, entry->pid);
779            strcpy(suffixBuf, "\n");
780            suffixLen = 1;
781            break;
782        case FORMAT_THREADTIME:
783            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
784                "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
785                entry->pid, entry->tid, priChar, entry->tag);
786            strcpy(suffixBuf, "\n");
787            suffixLen = 1;
788            break;
789        case FORMAT_LONG:
790            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
791                "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
792                timeBuf, entry->tv_nsec / 1000000, entry->pid,
793                entry->tid, priChar, entry->tag);
794            strcpy(suffixBuf, "\n\n");
795            suffixLen = 2;
796            prefixSuffixIsHeaderFooter = 1;
797            break;
798        case FORMAT_BRIEF:
799        default:
800            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
801                "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
802            strcpy(suffixBuf, "\n");
803            suffixLen = 1;
804            break;
805    }
806    /* snprintf has a weird return value.   It returns what would have been
807     * written given a large enough buffer.  In the case that the prefix is
808     * longer then our buffer(128), it messes up the calculations below
809     * possibly causing heap corruption.  To avoid this we double check and
810     * set the length at the maximum (size minus null byte)
811     */
812    if(prefixLen >= sizeof(prefixBuf))
813        prefixLen = sizeof(prefixBuf) - 1;
814    if(suffixLen >= sizeof(suffixBuf))
815        suffixLen = sizeof(suffixBuf) - 1;
816
817    /* the following code is tragically unreadable */
818
819    size_t numLines;
820    size_t i;
821    char *p;
822    size_t bufferSize;
823    const char *pm;
824
825    if (prefixSuffixIsHeaderFooter) {
826        // we're just wrapping message with a header/footer
827        numLines = 1;
828    } else {
829        pm = entry->message;
830        numLines = 0;
831
832        // The line-end finding here must match the line-end finding
833        // in for ( ... numLines...) loop below
834        while (pm < (entry->message + entry->messageLen)) {
835            if (*pm++ == '\n') numLines++;
836        }
837        // plus one line for anything not newline-terminated at the end
838        if (pm > entry->message && *(pm-1) != '\n') numLines++;
839    }
840
841    // this is an upper bound--newlines in message may be counted
842    // extraneously
843    bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
844
845    if (defaultBufferSize >= bufferSize) {
846        ret = defaultBuffer;
847    } else {
848        ret = (char *)malloc(bufferSize);
849
850        if (ret == NULL) {
851            return ret;
852        }
853    }
854
855    ret[0] = '\0';       /* to start strcat off */
856
857    p = ret;
858    pm = entry->message;
859
860    if (prefixSuffixIsHeaderFooter) {
861        strcat(p, prefixBuf);
862        p += prefixLen;
863        strncat(p, entry->message, entry->messageLen);
864        p += entry->messageLen;
865        strcat(p, suffixBuf);
866        p += suffixLen;
867    } else {
868        while(pm < (entry->message + entry->messageLen)) {
869            const char *lineStart;
870            size_t lineLen;
871            lineStart = pm;
872
873            // Find the next end-of-line in message
874            while (pm < (entry->message + entry->messageLen)
875                    && *pm != '\n') pm++;
876            lineLen = pm - lineStart;
877
878            strcat(p, prefixBuf);
879            p += prefixLen;
880            strncat(p, lineStart, lineLen);
881            p += lineLen;
882            strcat(p, suffixBuf);
883            p += suffixLen;
884
885            if (*pm == '\n') pm++;
886        }
887    }
888
889    if (p_outLength != NULL) {
890        *p_outLength = p - ret;
891    }
892
893    return ret;
894}
895
896/**
897 * Either print or do not print log line, based on filter
898 *
899 * Returns count bytes written
900 */
901
902int android_log_printLogLine(
903    AndroidLogFormat *p_format,
904    int fd,
905    const AndroidLogEntry *entry)
906{
907    int ret;
908    char defaultBuffer[512];
909    char *outBuffer = NULL;
910    size_t totalLen;
911
912    outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
913            sizeof(defaultBuffer), entry, &totalLen);
914
915    if (!outBuffer)
916        return -1;
917
918    do {
919        ret = write(fd, outBuffer, totalLen);
920    } while (ret < 0 && errno == EINTR);
921
922    if (ret < 0) {
923        fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
924        ret = 0;
925        goto done;
926    }
927
928    if (((size_t)ret) < totalLen) {
929        fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
930                (int)totalLen);
931        goto done;
932    }
933
934done:
935    if (outBuffer != defaultBuffer) {
936        free(outBuffer);
937    }
938
939    return ret;
940}
941