blob: 244f723f01adf40d160731e83dec30e7340b1004 [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>
Pierre Zurekead88fc2010-10-17 22:39:37 +020024#include <stdbool.h>
Mark Salyzyna04464a2014-04-30 08:50:53 -070025#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
Pierre Zurekead88fc2010-10-17 22:39:37 +020029#include <sys/param.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070030
Colin Cross9227bd32013-07-23 16:59:20 -070031#include <log/logd.h>
32#include <log/logprint.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070033
34typedef struct FilterInfo_t {
35 char *mTag;
36 android_LogPriority mPri;
37 struct FilterInfo_t *p_next;
38} FilterInfo;
39
40struct AndroidLogFormat_t {
41 android_LogPriority global_pri;
42 FilterInfo *filters;
43 AndroidLogPrintFormat format;
Pierre Zurekead88fc2010-10-17 22:39:37 +020044 bool colored_output;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070045};
46
Pierre Zurekead88fc2010-10-17 22:39:37 +020047/*
48 * gnome-terminal color tags
49 * See http://misc.flogisoft.com/bash/tip_colors_and_formatting
50 * for ideas on how to set the forground color of the text for xterm.
51 * The color manipulation character stream is defined as:
52 * ESC [ 3 8 ; 5 ; <color#> m
53 */
54#define ANDROID_COLOR_BLUE 75
55#define ANDROID_COLOR_DEFAULT 231
56#define ANDROID_COLOR_GREEN 40
57#define ANDROID_COLOR_ORANGE 166
58#define ANDROID_COLOR_RED 196
59#define ANDROID_COLOR_YELLOW 226
60
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070061static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri)
62{
63 FilterInfo *p_ret;
64
65 p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
66 p_ret->mTag = strdup(tag);
67 p_ret->mPri = pri;
68
69 return p_ret;
70}
71
Mark Salyzyna04464a2014-04-30 08:50:53 -070072/* balance to above, filterinfo_free left unimplemented */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070073
74/*
75 * Note: also accepts 0-9 priorities
76 * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
77 */
78static android_LogPriority filterCharToPri (char c)
79{
80 android_LogPriority pri;
81
82 c = tolower(c);
83
84 if (c >= '0' && c <= '9') {
85 if (c >= ('0'+ANDROID_LOG_SILENT)) {
86 pri = ANDROID_LOG_VERBOSE;
87 } else {
88 pri = (android_LogPriority)(c - '0');
89 }
90 } else if (c == 'v') {
91 pri = ANDROID_LOG_VERBOSE;
92 } else if (c == 'd') {
93 pri = ANDROID_LOG_DEBUG;
94 } else if (c == 'i') {
95 pri = ANDROID_LOG_INFO;
96 } else if (c == 'w') {
97 pri = ANDROID_LOG_WARN;
98 } else if (c == 'e') {
99 pri = ANDROID_LOG_ERROR;
100 } else if (c == 'f') {
101 pri = ANDROID_LOG_FATAL;
102 } else if (c == 's') {
103 pri = ANDROID_LOG_SILENT;
104 } else if (c == '*') {
105 pri = ANDROID_LOG_DEFAULT;
106 } else {
107 pri = ANDROID_LOG_UNKNOWN;
108 }
109
110 return pri;
111}
112
113static char filterPriToChar (android_LogPriority pri)
114{
115 switch (pri) {
116 case ANDROID_LOG_VERBOSE: return 'V';
117 case ANDROID_LOG_DEBUG: return 'D';
118 case ANDROID_LOG_INFO: return 'I';
119 case ANDROID_LOG_WARN: return 'W';
120 case ANDROID_LOG_ERROR: return 'E';
121 case ANDROID_LOG_FATAL: return 'F';
122 case ANDROID_LOG_SILENT: return 'S';
123
124 case ANDROID_LOG_DEFAULT:
125 case ANDROID_LOG_UNKNOWN:
126 default: return '?';
127 }
128}
129
Pierre Zurekead88fc2010-10-17 22:39:37 +0200130static int colorFromPri (android_LogPriority pri)
131{
132 switch (pri) {
133 case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
134 case ANDROID_LOG_DEBUG: return ANDROID_COLOR_BLUE;
135 case ANDROID_LOG_INFO: return ANDROID_COLOR_GREEN;
136 case ANDROID_LOG_WARN: return ANDROID_COLOR_ORANGE;
137 case ANDROID_LOG_ERROR: return ANDROID_COLOR_RED;
138 case ANDROID_LOG_FATAL: return ANDROID_COLOR_RED;
139 case ANDROID_LOG_SILENT: return ANDROID_COLOR_DEFAULT;
140
141 case ANDROID_LOG_DEFAULT:
142 case ANDROID_LOG_UNKNOWN:
143 default: return ANDROID_COLOR_DEFAULT;
144 }
145}
146
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700147static android_LogPriority filterPriForTag(
148 AndroidLogFormat *p_format, const char *tag)
149{
150 FilterInfo *p_curFilter;
151
152 for (p_curFilter = p_format->filters
153 ; p_curFilter != NULL
154 ; p_curFilter = p_curFilter->p_next
155 ) {
156 if (0 == strcmp(tag, p_curFilter->mTag)) {
157 if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
158 return p_format->global_pri;
159 } else {
160 return p_curFilter->mPri;
161 }
162 }
163 }
164
165 return p_format->global_pri;
166}
167
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700168/**
169 * returns 1 if this log line should be printed based on its priority
170 * and tag, and 0 if it should not
171 */
172int android_log_shouldPrintLine (
173 AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
174{
175 return pri >= filterPriForTag(p_format, tag);
176}
177
178AndroidLogFormat *android_log_format_new()
179{
180 AndroidLogFormat *p_ret;
181
182 p_ret = calloc(1, sizeof(AndroidLogFormat));
183
184 p_ret->global_pri = ANDROID_LOG_VERBOSE;
185 p_ret->format = FORMAT_BRIEF;
Pierre Zurekead88fc2010-10-17 22:39:37 +0200186 p_ret->colored_output = false;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700187
188 return p_ret;
189}
190
191void android_log_format_free(AndroidLogFormat *p_format)
192{
193 FilterInfo *p_info, *p_info_old;
194
195 p_info = p_format->filters;
196
197 while (p_info != NULL) {
198 p_info_old = p_info;
199 p_info = p_info->p_next;
200
201 free(p_info_old);
202 }
203
204 free(p_format);
205}
206
207
208
209void android_log_setPrintFormat(AndroidLogFormat *p_format,
210 AndroidLogPrintFormat format)
211{
Pierre Zurekead88fc2010-10-17 22:39:37 +0200212 if (format == FORMAT_COLOR)
213 p_format->colored_output = true;
214 else
215 p_format->format = format;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700216}
217
218/**
219 * Returns FORMAT_OFF on invalid string
220 */
221AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
222{
223 static AndroidLogPrintFormat format;
224
225 if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
226 else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
227 else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
228 else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
229 else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
230 else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
231 else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
232 else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
Pierre Zurekead88fc2010-10-17 22:39:37 +0200233 else if (strcmp(formatString, "color") == 0) format = FORMAT_COLOR;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700234 else format = FORMAT_OFF;
235
236 return format;
237}
238
239/**
240 * filterExpression: a single filter expression
241 * eg "AT:d"
242 *
243 * returns 0 on success and -1 on invalid expression
244 *
245 * Assumes single threaded execution
246 */
247
248int android_log_addFilterRule(AndroidLogFormat *p_format,
249 const char *filterExpression)
250{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700251 size_t tagNameLength;
252 android_LogPriority pri = ANDROID_LOG_DEFAULT;
253
254 tagNameLength = strcspn(filterExpression, ":");
255
256 if (tagNameLength == 0) {
257 goto error;
258 }
259
260 if(filterExpression[tagNameLength] == ':') {
261 pri = filterCharToPri(filterExpression[tagNameLength+1]);
262
263 if (pri == ANDROID_LOG_UNKNOWN) {
264 goto error;
265 }
266 }
267
268 if(0 == strncmp("*", filterExpression, tagNameLength)) {
269 // This filter expression refers to the global filter
270 // The default level for this is DEBUG if the priority
271 // is unspecified
272 if (pri == ANDROID_LOG_DEFAULT) {
273 pri = ANDROID_LOG_DEBUG;
274 }
275
276 p_format->global_pri = pri;
277 } else {
278 // for filter expressions that don't refer to the global
279 // filter, the default is verbose if the priority is unspecified
280 if (pri == ANDROID_LOG_DEFAULT) {
281 pri = ANDROID_LOG_VERBOSE;
282 }
283
284 char *tagName;
285
286// Presently HAVE_STRNDUP is never defined, so the second case is always taken
287// Darwin doesn't have strnup, everything else does
288#ifdef HAVE_STRNDUP
289 tagName = strndup(filterExpression, tagNameLength);
290#else
291 //a few extra bytes copied...
292 tagName = strdup(filterExpression);
293 tagName[tagNameLength] = '\0';
294#endif /*HAVE_STRNDUP*/
295
296 FilterInfo *p_fi = filterinfo_new(tagName, pri);
297 free(tagName);
298
299 p_fi->p_next = p_format->filters;
300 p_format->filters = p_fi;
301 }
302
303 return 0;
304error:
305 return -1;
306}
307
308
309/**
310 * filterString: a comma/whitespace-separated set of filter expressions
311 *
312 * eg "AT:d *:i"
313 *
314 * returns 0 on success and -1 on invalid expression
315 *
316 * Assumes single threaded execution
317 *
318 */
319
320int android_log_addFilterString(AndroidLogFormat *p_format,
321 const char *filterString)
322{
323 char *filterStringCopy = strdup (filterString);
324 char *p_cur = filterStringCopy;
325 char *p_ret;
326 int err;
327
328 // Yes, I'm using strsep
329 while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
330 // ignore whitespace-only entries
331 if(p_ret[0] != '\0') {
332 err = android_log_addFilterRule(p_format, p_ret);
333
334 if (err < 0) {
335 goto error;
336 }
337 }
338 }
339
340 free (filterStringCopy);
341 return 0;
342error:
343 free (filterStringCopy);
344 return -1;
345}
346
347static inline char * strip_end(char *str)
348{
349 char *end = str + strlen(str) - 1;
350
351 while (end >= str && isspace(*end))
352 *end-- = '\0';
353 return str;
354}
355
356/**
357 * Splits a wire-format buffer into an AndroidLogEntry
358 * entry allocated by caller. Pointers will point directly into buf
359 *
360 * Returns 0 on success and -1 on invalid wire format (entry will be
361 * in unspecified state)
362 */
363int android_log_processLogBuffer(struct logger_entry *buf,
364 AndroidLogEntry *entry)
365{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700366 entry->tv_sec = buf->sec;
367 entry->tv_nsec = buf->nsec;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700368 entry->pid = buf->pid;
369 entry->tid = buf->tid;
Kenny Root4bf3c022011-09-30 17:10:14 -0700370
371 /*
372 * format: <priority:1><tag:N>\0<message:N>\0
373 *
374 * tag str
Nick Kraleviche1ede152011-10-18 15:23:33 -0700375 * starts at buf->msg+1
Kenny Root4bf3c022011-09-30 17:10:14 -0700376 * msg
Nick Kraleviche1ede152011-10-18 15:23:33 -0700377 * starts at buf->msg+1+len(tag)+1
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700378 *
379 * The message may have been truncated by the kernel log driver.
380 * When that happens, we must null-terminate the message ourselves.
Kenny Root4bf3c022011-09-30 17:10:14 -0700381 */
Nick Kraleviche1ede152011-10-18 15:23:33 -0700382 if (buf->len < 3) {
383 // An well-formed entry must consist of at least a priority
384 // and two null characters
385 fprintf(stderr, "+++ LOG: entry too small\n");
Kenny Root4bf3c022011-09-30 17:10:14 -0700386 return -1;
387 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700388
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700389 int msgStart = -1;
390 int msgEnd = -1;
391
Nick Kraleviche1ede152011-10-18 15:23:33 -0700392 int i;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800393 char *msg = buf->msg;
394 struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
395 if (buf2->hdr_size) {
396 msg = ((char *)buf2) + buf2->hdr_size;
397 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700398 for (i = 1; i < buf->len; i++) {
Mark Salyzyn40b21552013-12-18 12:59:01 -0800399 if (msg[i] == '\0') {
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700400 if (msgStart == -1) {
401 msgStart = i + 1;
402 } else {
403 msgEnd = i;
404 break;
405 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700406 }
407 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700408
409 if (msgStart == -1) {
410 fprintf(stderr, "+++ LOG: malformed log message\n");
Nick Kralevich63f4a842011-10-17 10:45:03 -0700411 return -1;
412 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700413 if (msgEnd == -1) {
414 // incoming message not null-terminated; force it
415 msgEnd = buf->len - 1;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800416 msg[msgEnd] = '\0';
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700417 }
418
Mark Salyzyn40b21552013-12-18 12:59:01 -0800419 entry->priority = msg[0];
420 entry->tag = msg + 1;
421 entry->message = msg + msgStart;
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700422 entry->messageLen = msgEnd - msgStart;
Nick Kralevich63f4a842011-10-17 10:45:03 -0700423
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700424 return 0;
425}
426
427/*
428 * Extract a 4-byte value from a byte stream.
429 */
430static inline uint32_t get4LE(const uint8_t* src)
431{
432 return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
433}
434
435/*
436 * Extract an 8-byte value from a byte stream.
437 */
438static inline uint64_t get8LE(const uint8_t* src)
439{
440 uint32_t low, high;
441
442 low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
443 high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
444 return ((long long) high << 32) | (long long) low;
445}
446
447
448/*
449 * Recursively convert binary log data to printable form.
450 *
451 * This needs to be recursive because you can have lists of lists.
452 *
453 * If we run out of room, we stop processing immediately. It's important
454 * for us to check for space on every output element to avoid producing
455 * garbled output.
456 *
457 * Returns 0 on success, 1 on buffer full, -1 on failure.
458 */
459static int android_log_printBinaryEvent(const unsigned char** pEventData,
460 size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
461{
462 const unsigned char* eventData = *pEventData;
463 size_t eventDataLen = *pEventDataLen;
464 char* outBuf = *pOutBuf;
465 size_t outBufLen = *pOutBufLen;
466 unsigned char type;
467 size_t outCount;
468 int result = 0;
469
470 if (eventDataLen < 1)
471 return -1;
472 type = *eventData++;
473 eventDataLen--;
474
475 //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
476
477 switch (type) {
478 case EVENT_TYPE_INT:
479 /* 32-bit signed int */
480 {
481 int ival;
482
483 if (eventDataLen < 4)
484 return -1;
485 ival = get4LE(eventData);
486 eventData += 4;
487 eventDataLen -= 4;
488
489 outCount = snprintf(outBuf, outBufLen, "%d", ival);
490 if (outCount < outBufLen) {
491 outBuf += outCount;
492 outBufLen -= outCount;
493 } else {
494 /* halt output */
495 goto no_room;
496 }
497 }
498 break;
499 case EVENT_TYPE_LONG:
500 /* 64-bit signed long */
501 {
502 long long lval;
503
504 if (eventDataLen < 8)
505 return -1;
506 lval = get8LE(eventData);
507 eventData += 8;
508 eventDataLen -= 8;
509
510 outCount = snprintf(outBuf, outBufLen, "%lld", lval);
511 if (outCount < outBufLen) {
512 outBuf += outCount;
513 outBufLen -= outCount;
514 } else {
515 /* halt output */
516 goto no_room;
517 }
518 }
519 break;
520 case EVENT_TYPE_STRING:
521 /* UTF-8 chars, not NULL-terminated */
522 {
523 unsigned int strLen;
524
525 if (eventDataLen < 4)
526 return -1;
527 strLen = get4LE(eventData);
528 eventData += 4;
529 eventDataLen -= 4;
530
531 if (eventDataLen < strLen)
532 return -1;
533
534 if (strLen < outBufLen) {
535 memcpy(outBuf, eventData, strLen);
536 outBuf += strLen;
537 outBufLen -= strLen;
538 } else if (outBufLen > 0) {
539 /* copy what we can */
540 memcpy(outBuf, eventData, outBufLen);
541 outBuf += outBufLen;
542 outBufLen -= outBufLen;
543 goto no_room;
544 }
545 eventData += strLen;
546 eventDataLen -= strLen;
547 break;
548 }
549 case EVENT_TYPE_LIST:
550 /* N items, all different types */
551 {
552 unsigned char count;
553 int i;
554
555 if (eventDataLen < 1)
556 return -1;
557
558 count = *eventData++;
559 eventDataLen--;
560
561 if (outBufLen > 0) {
562 *outBuf++ = '[';
563 outBufLen--;
564 } else {
565 goto no_room;
566 }
567
568 for (i = 0; i < count; i++) {
569 result = android_log_printBinaryEvent(&eventData, &eventDataLen,
570 &outBuf, &outBufLen);
571 if (result != 0)
572 goto bail;
573
574 if (i < count-1) {
575 if (outBufLen > 0) {
576 *outBuf++ = ',';
577 outBufLen--;
578 } else {
579 goto no_room;
580 }
581 }
582 }
583
584 if (outBufLen > 0) {
585 *outBuf++ = ']';
586 outBufLen--;
587 } else {
588 goto no_room;
589 }
590 }
591 break;
592 default:
593 fprintf(stderr, "Unknown binary event type %d\n", type);
594 return -1;
595 }
596
597bail:
598 *pEventData = eventData;
599 *pEventDataLen = eventDataLen;
600 *pOutBuf = outBuf;
601 *pOutBufLen = outBufLen;
602 return result;
603
604no_room:
605 result = 1;
606 goto bail;
607}
608
609/**
610 * Convert a binary log entry to ASCII form.
611 *
612 * For convenience we mimic the processLogBuffer API. There is no
613 * pre-defined output length for the binary data, since we're free to format
614 * it however we choose, which means we can't really use a fixed-size buffer
615 * here.
616 */
617int android_log_processBinaryLogBuffer(struct logger_entry *buf,
618 AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
619 int messageBufLen)
620{
621 size_t inCount;
622 unsigned int tagIndex;
623 const unsigned char* eventData;
624
625 entry->tv_sec = buf->sec;
626 entry->tv_nsec = buf->nsec;
627 entry->priority = ANDROID_LOG_INFO;
628 entry->pid = buf->pid;
629 entry->tid = buf->tid;
630
631 /*
632 * Pull the tag out.
633 */
634 eventData = (const unsigned char*) buf->msg;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800635 struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
636 if (buf2->hdr_size) {
637 eventData = ((unsigned char *)buf2) + buf2->hdr_size;
638 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700639 inCount = buf->len;
640 if (inCount < 4)
641 return -1;
642 tagIndex = get4LE(eventData);
643 eventData += 4;
644 inCount -= 4;
645
646 if (map != NULL) {
647 entry->tag = android_lookupEventTag(map, tagIndex);
648 } else {
649 entry->tag = NULL;
650 }
651
652 /*
653 * If we don't have a map, or didn't find the tag number in the map,
654 * stuff a generated tag value into the start of the output buffer and
655 * shift the buffer pointers down.
656 */
657 if (entry->tag == NULL) {
658 int tagLen;
659
660 tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
661 entry->tag = messageBuf;
662 messageBuf += tagLen+1;
663 messageBufLen -= tagLen+1;
664 }
665
666 /*
667 * Format the event log data into the buffer.
668 */
669 char* outBuf = messageBuf;
670 size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
671 int result;
672 result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
673 &outRemaining);
674 if (result < 0) {
675 fprintf(stderr, "Binary log entry conversion failed\n");
676 return -1;
677 } else if (result == 1) {
678 if (outBuf > messageBuf) {
679 /* leave an indicator */
680 *(outBuf-1) = '!';
681 } else {
682 /* no room to output anything at all */
683 *outBuf++ = '!';
684 outRemaining--;
685 }
686 /* pretend we ate all the data */
687 inCount = 0;
688 }
689
690 /* eat the silly terminating '\n' */
691 if (inCount == 1 && *eventData == '\n') {
692 eventData++;
693 inCount--;
694 }
695
696 if (inCount != 0) {
697 fprintf(stderr,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800698 "Warning: leftover binary log data (%zu bytes)\n", inCount);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700699 }
700
701 /*
702 * Terminate the buffer. The NUL byte does not count as part of
703 * entry->messageLen.
704 */
705 *outBuf = '\0';
706 entry->messageLen = outBuf - messageBuf;
707 assert(entry->messageLen == (messageBufLen-1) - outRemaining);
708
709 entry->message = messageBuf;
710
711 return 0;
712}
713
714/**
715 * Formats a log message into a buffer
716 *
717 * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
718 * If return value != defaultBuffer, caller must call free()
719 * Returns NULL on malloc error
720 */
721
722char *android_log_formatLogLine (
723 AndroidLogFormat *p_format,
724 char *defaultBuffer,
725 size_t defaultBufferSize,
726 const AndroidLogEntry *entry,
727 size_t *p_outLength)
728{
729#if defined(HAVE_LOCALTIME_R)
730 struct tm tmBuf;
731#endif
732 struct tm* ptm;
733 char timeBuf[32];
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700734 char prefixBuf[128], suffixBuf[128];
735 char priChar;
736 int prefixSuffixIsHeaderFooter = 0;
737 char * ret = NULL;
738
739 priChar = filterPriToChar(entry->priority);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200740 size_t prefixLen = 0, suffixLen = 0;
741 size_t len;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700742
743 /*
744 * Get the current date/time in pretty form
745 *
746 * It's often useful when examining a log with "less" to jump to
747 * a specific point in the file by searching for the date/time stamp.
748 * For this reason it's very annoying to have regexp meta characters
749 * in the time stamp. Don't use forward slashes, parenthesis,
750 * brackets, asterisks, or other special chars here.
751 */
752#if defined(HAVE_LOCALTIME_R)
753 ptm = localtime_r(&(entry->tv_sec), &tmBuf);
754#else
755 ptm = localtime(&(entry->tv_sec));
756#endif
757 //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
758 strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
759
760 /*
761 * Construct a buffer containing the log header and log message.
762 */
Pierre Zurekead88fc2010-10-17 22:39:37 +0200763 if (p_format->colored_output) {
764 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
765 colorFromPri(entry->priority));
766 prefixLen = MIN(prefixLen, sizeof(prefixBuf));
767 suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m");
768 suffixLen = MIN(suffixLen, sizeof(suffixBuf));
769 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700770
771 switch (p_format->format) {
772 case FORMAT_TAG:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200773 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700774 "%c/%-8s: ", priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200775 strcpy(suffixBuf + suffixLen, "\n");
776 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700777 break;
778 case FORMAT_PROCESS:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200779 len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700780 " (%s)\n", entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200781 suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
782 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
783 "%c(%5d) ", priChar, entry->pid);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700784 break;
785 case FORMAT_THREAD:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200786 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800787 "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200788 strcpy(suffixBuf + suffixLen, "\n");
789 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700790 break;
791 case FORMAT_RAW:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200792 prefixBuf[prefixLen] = 0;
793 len = 0;
794 strcpy(suffixBuf + suffixLen, "\n");
795 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700796 break;
797 case FORMAT_TIME:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200798 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700799 "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
800 priChar, entry->tag, entry->pid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200801 strcpy(suffixBuf + suffixLen, "\n");
802 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700803 break;
804 case FORMAT_THREADTIME:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200805 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700806 "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800807 entry->pid, entry->tid, priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200808 strcpy(suffixBuf + suffixLen, "\n");
809 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700810 break;
811 case FORMAT_LONG:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200812 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800813 "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700814 timeBuf, entry->tv_nsec / 1000000, entry->pid,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800815 entry->tid, priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200816 strcpy(suffixBuf + suffixLen, "\n\n");
817 suffixLen += 2;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700818 prefixSuffixIsHeaderFooter = 1;
819 break;
820 case FORMAT_BRIEF:
821 default:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200822 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700823 "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200824 strcpy(suffixBuf + suffixLen, "\n");
825 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700826 break;
827 }
Pierre Zurekead88fc2010-10-17 22:39:37 +0200828
Keith Prestonb45b5c92010-02-11 15:12:53 -0600829 /* snprintf has a weird return value. It returns what would have been
830 * written given a large enough buffer. In the case that the prefix is
831 * longer then our buffer(128), it messes up the calculations below
832 * possibly causing heap corruption. To avoid this we double check and
833 * set the length at the maximum (size minus null byte)
834 */
Pierre Zurekead88fc2010-10-17 22:39:37 +0200835 prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
836 suffixLen = MIN(suffixLen, sizeof(suffixLen));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700837
838 /* the following code is tragically unreadable */
839
840 size_t numLines;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700841 char *p;
842 size_t bufferSize;
843 const char *pm;
844
845 if (prefixSuffixIsHeaderFooter) {
846 // we're just wrapping message with a header/footer
847 numLines = 1;
848 } else {
849 pm = entry->message;
850 numLines = 0;
851
852 // The line-end finding here must match the line-end finding
853 // in for ( ... numLines...) loop below
854 while (pm < (entry->message + entry->messageLen)) {
855 if (*pm++ == '\n') numLines++;
856 }
857 // plus one line for anything not newline-terminated at the end
858 if (pm > entry->message && *(pm-1) != '\n') numLines++;
859 }
860
861 // this is an upper bound--newlines in message may be counted
862 // extraneously
863 bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
864
865 if (defaultBufferSize >= bufferSize) {
866 ret = defaultBuffer;
867 } else {
868 ret = (char *)malloc(bufferSize);
869
870 if (ret == NULL) {
871 return ret;
872 }
873 }
874
875 ret[0] = '\0'; /* to start strcat off */
876
877 p = ret;
878 pm = entry->message;
879
880 if (prefixSuffixIsHeaderFooter) {
881 strcat(p, prefixBuf);
882 p += prefixLen;
883 strncat(p, entry->message, entry->messageLen);
884 p += entry->messageLen;
885 strcat(p, suffixBuf);
886 p += suffixLen;
887 } else {
888 while(pm < (entry->message + entry->messageLen)) {
889 const char *lineStart;
890 size_t lineLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700891 lineStart = pm;
892
893 // Find the next end-of-line in message
894 while (pm < (entry->message + entry->messageLen)
895 && *pm != '\n') pm++;
896 lineLen = pm - lineStart;
897
898 strcat(p, prefixBuf);
899 p += prefixLen;
900 strncat(p, lineStart, lineLen);
901 p += lineLen;
902 strcat(p, suffixBuf);
903 p += suffixLen;
904
905 if (*pm == '\n') pm++;
906 }
907 }
908
909 if (p_outLength != NULL) {
910 *p_outLength = p - ret;
911 }
912
913 return ret;
914}
915
916/**
917 * Either print or do not print log line, based on filter
918 *
919 * Returns count bytes written
920 */
921
Joe Onoratoe2bf2ea2010-03-01 09:11:54 -0800922int android_log_printLogLine(
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700923 AndroidLogFormat *p_format,
924 int fd,
925 const AndroidLogEntry *entry)
926{
927 int ret;
928 char defaultBuffer[512];
929 char *outBuffer = NULL;
930 size_t totalLen;
931
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700932 outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
933 sizeof(defaultBuffer), entry, &totalLen);
934
935 if (!outBuffer)
936 return -1;
937
938 do {
939 ret = write(fd, outBuffer, totalLen);
940 } while (ret < 0 && errno == EINTR);
941
942 if (ret < 0) {
943 fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
944 ret = 0;
945 goto done;
946 }
947
948 if (((size_t)ret) < totalLen) {
949 fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
950 (int)totalLen);
951 goto done;
952 }
953
954done:
955 if (outBuffer != defaultBuffer) {
956 free(outBuffer);
957 }
958
959 return ret;
960}