blob: 08e830acc47f3a5fe7529dfab399c3b56d27eeb0 [file] [log] [blame]
Mark Salyzyncf4aa032013-11-22 07:54:30 -08001/*
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07002**
Mark Salyzyn40b21552013-12-18 12:59:01 -08003** Copyright 2006-2014, The Android Open Source Project
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07004**
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
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070020#include <arpa/inet.h>
Mark Salyzyna04464a2014-04-30 08:50:53 -070021#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>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070028
Colin Cross9227bd32013-07-23 16:59:20 -070029#include <log/logd.h>
30#include <log/logprint.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070031
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
Mark Salyzyna04464a2014-04-30 08:50:53 -070055/* balance to above, filterinfo_free left unimplemented */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070056
57/*
58 * Note: also accepts 0-9 priorities
59 * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
60 */
61static 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
96static 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
113static 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
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700134/**
135 * returns 1 if this log line should be printed based on its priority
136 * and tag, and 0 if it should not
137 */
138int android_log_shouldPrintLine (
139 AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
140{
141 return pri >= filterPriForTag(p_format, tag);
142}
143
144AndroidLogFormat *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
156void 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
174void 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 */
183AndroidLogPrintFormat 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
209int android_log_addFilterRule(AndroidLogFormat *p_format,
210 const char *filterExpression)
211{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700212 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;
265error:
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
281int 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;
303error:
304 free (filterStringCopy);
305 return -1;
306}
307
308static 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 */
324int android_log_processLogBuffer(struct logger_entry *buf,
325 AndroidLogEntry *entry)
326{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700327 entry->tv_sec = buf->sec;
328 entry->tv_nsec = buf->nsec;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700329 entry->pid = buf->pid;
330 entry->tid = buf->tid;
Kenny Root4bf3c022011-09-30 17:10:14 -0700331
332 /*
333 * format: <priority:1><tag:N>\0<message:N>\0
334 *
335 * tag str
Nick Kraleviche1ede152011-10-18 15:23:33 -0700336 * starts at buf->msg+1
Kenny Root4bf3c022011-09-30 17:10:14 -0700337 * msg
Nick Kraleviche1ede152011-10-18 15:23:33 -0700338 * starts at buf->msg+1+len(tag)+1
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700339 *
340 * The message may have been truncated by the kernel log driver.
341 * When that happens, we must null-terminate the message ourselves.
Kenny Root4bf3c022011-09-30 17:10:14 -0700342 */
Nick Kraleviche1ede152011-10-18 15:23:33 -0700343 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");
Kenny Root4bf3c022011-09-30 17:10:14 -0700347 return -1;
348 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700349
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700350 int msgStart = -1;
351 int msgEnd = -1;
352
Nick Kraleviche1ede152011-10-18 15:23:33 -0700353 int i;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800354 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 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700359 for (i = 1; i < buf->len; i++) {
Mark Salyzyn40b21552013-12-18 12:59:01 -0800360 if (msg[i] == '\0') {
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700361 if (msgStart == -1) {
362 msgStart = i + 1;
363 } else {
364 msgEnd = i;
365 break;
366 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700367 }
368 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700369
370 if (msgStart == -1) {
371 fprintf(stderr, "+++ LOG: malformed log message\n");
Nick Kralevich63f4a842011-10-17 10:45:03 -0700372 return -1;
373 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700374 if (msgEnd == -1) {
375 // incoming message not null-terminated; force it
376 msgEnd = buf->len - 1;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800377 msg[msgEnd] = '\0';
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700378 }
379
Mark Salyzyn40b21552013-12-18 12:59:01 -0800380 entry->priority = msg[0];
381 entry->tag = msg + 1;
382 entry->message = msg + msgStart;
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700383 entry->messageLen = msgEnd - msgStart;
Nick Kralevich63f4a842011-10-17 10:45:03 -0700384
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700385 return 0;
386}
387
388/*
389 * Extract a 4-byte value from a byte stream.
390 */
391static 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 */
399static 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 */
420static 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
558bail:
559 *pEventData = eventData;
560 *pEventDataLen = eventDataLen;
561 *pOutBuf = outBuf;
562 *pOutBufLen = outBufLen;
563 return result;
564
565no_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 */
578int 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;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800596 struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
597 if (buf2->hdr_size) {
598 eventData = ((unsigned char *)buf2) + buf2->hdr_size;
599 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700600 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,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800659 "Warning: leftover binary log data (%zu bytes)\n", inCount);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700660 }
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
683char *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];
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700695 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),
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800738 "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700739 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,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800758 entry->pid, entry->tid, priChar, entry->tag);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700759 strcpy(suffixBuf, "\n");
760 suffixLen = 1;
761 break;
762 case FORMAT_LONG:
763 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800764 "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700765 timeBuf, entry->tv_nsec / 1000000, entry->pid,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800766 entry->tid, priChar, entry->tag);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700767 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 }
Keith Prestonb45b5c92010-02-11 15:12:53 -0600779 /* 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;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700789
790 /* the following code is tragically unreadable */
791
792 size_t numLines;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700793 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;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700843 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
Joe Onoratoe2bf2ea2010-03-01 09:11:54 -0800874int android_log_printLogLine(
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700875 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
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700884 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
906done:
907 if (outBuffer != defaultBuffer) {
908 free(outBuffer);
909 }
910
911 return ret;
912}