blob: 8093a4c2657be4e6cdac61a5987d3a44efcc7922 [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
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700347/**
348 * Splits a wire-format buffer into an AndroidLogEntry
349 * entry allocated by caller. Pointers will point directly into buf
350 *
351 * Returns 0 on success and -1 on invalid wire format (entry will be
352 * in unspecified state)
353 */
354int android_log_processLogBuffer(struct logger_entry *buf,
355 AndroidLogEntry *entry)
356{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700357 entry->tv_sec = buf->sec;
358 entry->tv_nsec = buf->nsec;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700359 entry->pid = buf->pid;
360 entry->tid = buf->tid;
Kenny Root4bf3c022011-09-30 17:10:14 -0700361
362 /*
363 * format: <priority:1><tag:N>\0<message:N>\0
364 *
365 * tag str
Nick Kraleviche1ede152011-10-18 15:23:33 -0700366 * starts at buf->msg+1
Kenny Root4bf3c022011-09-30 17:10:14 -0700367 * msg
Nick Kraleviche1ede152011-10-18 15:23:33 -0700368 * starts at buf->msg+1+len(tag)+1
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700369 *
370 * The message may have been truncated by the kernel log driver.
371 * When that happens, we must null-terminate the message ourselves.
Kenny Root4bf3c022011-09-30 17:10:14 -0700372 */
Nick Kraleviche1ede152011-10-18 15:23:33 -0700373 if (buf->len < 3) {
374 // An well-formed entry must consist of at least a priority
375 // and two null characters
376 fprintf(stderr, "+++ LOG: entry too small\n");
Kenny Root4bf3c022011-09-30 17:10:14 -0700377 return -1;
378 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700379
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700380 int msgStart = -1;
381 int msgEnd = -1;
382
Nick Kraleviche1ede152011-10-18 15:23:33 -0700383 int i;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800384 char *msg = buf->msg;
385 struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
386 if (buf2->hdr_size) {
387 msg = ((char *)buf2) + buf2->hdr_size;
388 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700389 for (i = 1; i < buf->len; i++) {
Mark Salyzyn40b21552013-12-18 12:59:01 -0800390 if (msg[i] == '\0') {
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700391 if (msgStart == -1) {
392 msgStart = i + 1;
393 } else {
394 msgEnd = i;
395 break;
396 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700397 }
398 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700399
400 if (msgStart == -1) {
401 fprintf(stderr, "+++ LOG: malformed log message\n");
Nick Kralevich63f4a842011-10-17 10:45:03 -0700402 return -1;
403 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700404 if (msgEnd == -1) {
405 // incoming message not null-terminated; force it
406 msgEnd = buf->len - 1;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800407 msg[msgEnd] = '\0';
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700408 }
409
Mark Salyzyn40b21552013-12-18 12:59:01 -0800410 entry->priority = msg[0];
411 entry->tag = msg + 1;
412 entry->message = msg + msgStart;
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700413 entry->messageLen = msgEnd - msgStart;
Nick Kralevich63f4a842011-10-17 10:45:03 -0700414
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700415 return 0;
416}
417
418/*
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700419 * Recursively convert binary log data to printable form.
420 *
421 * This needs to be recursive because you can have lists of lists.
422 *
423 * If we run out of room, we stop processing immediately. It's important
424 * for us to check for space on every output element to avoid producing
425 * garbled output.
426 *
427 * Returns 0 on success, 1 on buffer full, -1 on failure.
428 */
429static int android_log_printBinaryEvent(const unsigned char** pEventData,
430 size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
431{
432 const unsigned char* eventData = *pEventData;
433 size_t eventDataLen = *pEventDataLen;
434 char* outBuf = *pOutBuf;
435 size_t outBufLen = *pOutBufLen;
436 unsigned char type;
437 size_t outCount;
438 int result = 0;
439
440 if (eventDataLen < 1)
441 return -1;
442 type = *eventData++;
443 eventDataLen--;
444
445 //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
446
447 switch (type) {
448 case EVENT_TYPE_INT:
449 /* 32-bit signed int */
450 {
451 int ival;
452
453 if (eventDataLen < 4)
454 return -1;
Mark Salyzyn66bfc5c2015-03-03 15:23:24 -0800455 ival = le32toh(*((int32_t *)eventData));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700456 eventData += 4;
457 eventDataLen -= 4;
458
459 outCount = snprintf(outBuf, outBufLen, "%d", ival);
460 if (outCount < outBufLen) {
461 outBuf += outCount;
462 outBufLen -= outCount;
463 } else {
464 /* halt output */
465 goto no_room;
466 }
467 }
468 break;
469 case EVENT_TYPE_LONG:
470 /* 64-bit signed long */
471 {
472 long long lval;
473
474 if (eventDataLen < 8)
475 return -1;
Mark Salyzyn66bfc5c2015-03-03 15:23:24 -0800476 lval = le64toh(*((int64_t *)eventData));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700477 eventData += 8;
478 eventDataLen -= 8;
479
480 outCount = snprintf(outBuf, outBufLen, "%lld", lval);
481 if (outCount < outBufLen) {
482 outBuf += outCount;
483 outBufLen -= outCount;
484 } else {
485 /* halt output */
486 goto no_room;
487 }
488 }
489 break;
490 case EVENT_TYPE_STRING:
491 /* UTF-8 chars, not NULL-terminated */
492 {
493 unsigned int strLen;
494
495 if (eventDataLen < 4)
496 return -1;
Mark Salyzyn66bfc5c2015-03-03 15:23:24 -0800497 strLen = le32toh(*((int32_t *)eventData));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700498 eventData += 4;
499 eventDataLen -= 4;
500
501 if (eventDataLen < strLen)
502 return -1;
503
504 if (strLen < outBufLen) {
505 memcpy(outBuf, eventData, strLen);
506 outBuf += strLen;
507 outBufLen -= strLen;
508 } else if (outBufLen > 0) {
509 /* copy what we can */
510 memcpy(outBuf, eventData, outBufLen);
511 outBuf += outBufLen;
512 outBufLen -= outBufLen;
513 goto no_room;
514 }
515 eventData += strLen;
516 eventDataLen -= strLen;
517 break;
518 }
519 case EVENT_TYPE_LIST:
520 /* N items, all different types */
521 {
522 unsigned char count;
523 int i;
524
525 if (eventDataLen < 1)
526 return -1;
527
528 count = *eventData++;
529 eventDataLen--;
530
531 if (outBufLen > 0) {
532 *outBuf++ = '[';
533 outBufLen--;
534 } else {
535 goto no_room;
536 }
537
538 for (i = 0; i < count; i++) {
539 result = android_log_printBinaryEvent(&eventData, &eventDataLen,
540 &outBuf, &outBufLen);
541 if (result != 0)
542 goto bail;
543
544 if (i < count-1) {
545 if (outBufLen > 0) {
546 *outBuf++ = ',';
547 outBufLen--;
548 } else {
549 goto no_room;
550 }
551 }
552 }
553
554 if (outBufLen > 0) {
555 *outBuf++ = ']';
556 outBufLen--;
557 } else {
558 goto no_room;
559 }
560 }
561 break;
562 default:
563 fprintf(stderr, "Unknown binary event type %d\n", type);
564 return -1;
565 }
566
567bail:
568 *pEventData = eventData;
569 *pEventDataLen = eventDataLen;
570 *pOutBuf = outBuf;
571 *pOutBufLen = outBufLen;
572 return result;
573
574no_room:
575 result = 1;
576 goto bail;
577}
578
579/**
580 * Convert a binary log entry to ASCII form.
581 *
582 * For convenience we mimic the processLogBuffer API. There is no
583 * pre-defined output length for the binary data, since we're free to format
584 * it however we choose, which means we can't really use a fixed-size buffer
585 * here.
586 */
587int android_log_processBinaryLogBuffer(struct logger_entry *buf,
588 AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
589 int messageBufLen)
590{
591 size_t inCount;
592 unsigned int tagIndex;
593 const unsigned char* eventData;
594
595 entry->tv_sec = buf->sec;
596 entry->tv_nsec = buf->nsec;
597 entry->priority = ANDROID_LOG_INFO;
598 entry->pid = buf->pid;
599 entry->tid = buf->tid;
600
601 /*
602 * Pull the tag out.
603 */
604 eventData = (const unsigned char*) buf->msg;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800605 struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
606 if (buf2->hdr_size) {
607 eventData = ((unsigned char *)buf2) + buf2->hdr_size;
608 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700609 inCount = buf->len;
610 if (inCount < 4)
611 return -1;
Mark Salyzyn66bfc5c2015-03-03 15:23:24 -0800612 tagIndex = le32toh(*((int32_t *)eventData));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700613 eventData += 4;
614 inCount -= 4;
615
616 if (map != NULL) {
617 entry->tag = android_lookupEventTag(map, tagIndex);
618 } else {
619 entry->tag = NULL;
620 }
621
622 /*
623 * If we don't have a map, or didn't find the tag number in the map,
624 * stuff a generated tag value into the start of the output buffer and
625 * shift the buffer pointers down.
626 */
627 if (entry->tag == NULL) {
628 int tagLen;
629
630 tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
631 entry->tag = messageBuf;
632 messageBuf += tagLen+1;
633 messageBufLen -= tagLen+1;
634 }
635
636 /*
637 * Format the event log data into the buffer.
638 */
639 char* outBuf = messageBuf;
640 size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
641 int result;
642 result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
643 &outRemaining);
644 if (result < 0) {
645 fprintf(stderr, "Binary log entry conversion failed\n");
646 return -1;
647 } else if (result == 1) {
648 if (outBuf > messageBuf) {
649 /* leave an indicator */
650 *(outBuf-1) = '!';
651 } else {
652 /* no room to output anything at all */
653 *outBuf++ = '!';
654 outRemaining--;
655 }
656 /* pretend we ate all the data */
657 inCount = 0;
658 }
659
660 /* eat the silly terminating '\n' */
661 if (inCount == 1 && *eventData == '\n') {
662 eventData++;
663 inCount--;
664 }
665
666 if (inCount != 0) {
667 fprintf(stderr,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800668 "Warning: leftover binary log data (%zu bytes)\n", inCount);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700669 }
670
671 /*
672 * Terminate the buffer. The NUL byte does not count as part of
673 * entry->messageLen.
674 */
675 *outBuf = '\0';
676 entry->messageLen = outBuf - messageBuf;
677 assert(entry->messageLen == (messageBufLen-1) - outRemaining);
678
679 entry->message = messageBuf;
680
681 return 0;
682}
683
684/**
685 * Formats a log message into a buffer
686 *
687 * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
688 * If return value != defaultBuffer, caller must call free()
689 * Returns NULL on malloc error
690 */
691
692char *android_log_formatLogLine (
693 AndroidLogFormat *p_format,
694 char *defaultBuffer,
695 size_t defaultBufferSize,
696 const AndroidLogEntry *entry,
697 size_t *p_outLength)
698{
Yabin Cui8a985352014-11-13 10:02:08 -0800699#if !defined(_WIN32)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700700 struct tm tmBuf;
701#endif
702 struct tm* ptm;
703 char timeBuf[32];
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700704 char prefixBuf[128], suffixBuf[128];
705 char priChar;
706 int prefixSuffixIsHeaderFooter = 0;
707 char * ret = NULL;
708
709 priChar = filterPriToChar(entry->priority);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200710 size_t prefixLen = 0, suffixLen = 0;
711 size_t len;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700712
713 /*
714 * Get the current date/time in pretty form
715 *
716 * It's often useful when examining a log with "less" to jump to
717 * a specific point in the file by searching for the date/time stamp.
718 * For this reason it's very annoying to have regexp meta characters
719 * in the time stamp. Don't use forward slashes, parenthesis,
720 * brackets, asterisks, or other special chars here.
721 */
Yabin Cui8a985352014-11-13 10:02:08 -0800722#if !defined(_WIN32)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700723 ptm = localtime_r(&(entry->tv_sec), &tmBuf);
724#else
725 ptm = localtime(&(entry->tv_sec));
726#endif
727 //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
728 strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
729
730 /*
731 * Construct a buffer containing the log header and log message.
732 */
Pierre Zurekead88fc2010-10-17 22:39:37 +0200733 if (p_format->colored_output) {
734 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
735 colorFromPri(entry->priority));
736 prefixLen = MIN(prefixLen, sizeof(prefixBuf));
737 suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m");
738 suffixLen = MIN(suffixLen, sizeof(suffixBuf));
739 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700740
741 switch (p_format->format) {
742 case FORMAT_TAG:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200743 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700744 "%c/%-8s: ", priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200745 strcpy(suffixBuf + suffixLen, "\n");
746 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700747 break;
748 case FORMAT_PROCESS:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200749 len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700750 " (%s)\n", entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200751 suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
752 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
753 "%c(%5d) ", priChar, entry->pid);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700754 break;
755 case FORMAT_THREAD:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200756 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800757 "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200758 strcpy(suffixBuf + suffixLen, "\n");
759 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700760 break;
761 case FORMAT_RAW:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200762 prefixBuf[prefixLen] = 0;
763 len = 0;
764 strcpy(suffixBuf + suffixLen, "\n");
765 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700766 break;
767 case FORMAT_TIME:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200768 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700769 "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
770 priChar, entry->tag, entry->pid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200771 strcpy(suffixBuf + suffixLen, "\n");
772 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700773 break;
774 case FORMAT_THREADTIME:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200775 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700776 "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800777 entry->pid, entry->tid, priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200778 strcpy(suffixBuf + suffixLen, "\n");
779 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700780 break;
781 case FORMAT_LONG:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200782 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800783 "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700784 timeBuf, entry->tv_nsec / 1000000, entry->pid,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800785 entry->tid, priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200786 strcpy(suffixBuf + suffixLen, "\n\n");
787 suffixLen += 2;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700788 prefixSuffixIsHeaderFooter = 1;
789 break;
790 case FORMAT_BRIEF:
791 default:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200792 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700793 "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200794 strcpy(suffixBuf + suffixLen, "\n");
795 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700796 break;
797 }
Pierre Zurekead88fc2010-10-17 22:39:37 +0200798
Keith Prestonb45b5c92010-02-11 15:12:53 -0600799 /* snprintf has a weird return value. It returns what would have been
800 * written given a large enough buffer. In the case that the prefix is
801 * longer then our buffer(128), it messes up the calculations below
802 * possibly causing heap corruption. To avoid this we double check and
803 * set the length at the maximum (size minus null byte)
804 */
Pierre Zurekead88fc2010-10-17 22:39:37 +0200805 prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
Mark Salyzyne2428422015-01-22 10:00:04 -0800806 suffixLen = MIN(suffixLen, sizeof(suffixBuf));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700807
808 /* the following code is tragically unreadable */
809
810 size_t numLines;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700811 char *p;
812 size_t bufferSize;
813 const char *pm;
814
815 if (prefixSuffixIsHeaderFooter) {
816 // we're just wrapping message with a header/footer
817 numLines = 1;
818 } else {
819 pm = entry->message;
820 numLines = 0;
821
822 // The line-end finding here must match the line-end finding
823 // in for ( ... numLines...) loop below
824 while (pm < (entry->message + entry->messageLen)) {
825 if (*pm++ == '\n') numLines++;
826 }
827 // plus one line for anything not newline-terminated at the end
828 if (pm > entry->message && *(pm-1) != '\n') numLines++;
829 }
830
831 // this is an upper bound--newlines in message may be counted
832 // extraneously
833 bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
834
835 if (defaultBufferSize >= bufferSize) {
836 ret = defaultBuffer;
837 } else {
838 ret = (char *)malloc(bufferSize);
839
840 if (ret == NULL) {
841 return ret;
842 }
843 }
844
845 ret[0] = '\0'; /* to start strcat off */
846
847 p = ret;
848 pm = entry->message;
849
850 if (prefixSuffixIsHeaderFooter) {
851 strcat(p, prefixBuf);
852 p += prefixLen;
853 strncat(p, entry->message, entry->messageLen);
854 p += entry->messageLen;
855 strcat(p, suffixBuf);
856 p += suffixLen;
857 } else {
858 while(pm < (entry->message + entry->messageLen)) {
859 const char *lineStart;
860 size_t lineLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700861 lineStart = pm;
862
863 // Find the next end-of-line in message
864 while (pm < (entry->message + entry->messageLen)
865 && *pm != '\n') pm++;
866 lineLen = pm - lineStart;
867
868 strcat(p, prefixBuf);
869 p += prefixLen;
870 strncat(p, lineStart, lineLen);
871 p += lineLen;
872 strcat(p, suffixBuf);
873 p += suffixLen;
874
875 if (*pm == '\n') pm++;
876 }
877 }
878
879 if (p_outLength != NULL) {
880 *p_outLength = p - ret;
881 }
882
883 return ret;
884}
885
886/**
887 * Either print or do not print log line, based on filter
888 *
889 * Returns count bytes written
890 */
891
Joe Onoratoe2bf2ea2010-03-01 09:11:54 -0800892int android_log_printLogLine(
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700893 AndroidLogFormat *p_format,
894 int fd,
895 const AndroidLogEntry *entry)
896{
897 int ret;
898 char defaultBuffer[512];
899 char *outBuffer = NULL;
900 size_t totalLen;
901
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700902 outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
903 sizeof(defaultBuffer), entry, &totalLen);
904
905 if (!outBuffer)
906 return -1;
907
908 do {
909 ret = write(fd, outBuffer, totalLen);
910 } while (ret < 0 && errno == EINTR);
911
912 if (ret < 0) {
913 fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
914 ret = 0;
915 goto done;
916 }
917
918 if (((size_t)ret) < totalLen) {
919 fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
920 (int)totalLen);
921 goto done;
922 }
923
924done:
925 if (outBuffer != defaultBuffer) {
926 free(outBuffer);
927 }
928
929 return ret;
930}