blob: a3f1d7e5f10030af5170a3a2d203ed407e4eeb78 [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>
Jeff Brown44193d92015-04-28 12:47:02 -070029#include <inttypes.h>
Pierre Zurekead88fc2010-10-17 22:39:37 +020030#include <sys/param.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070031
Colin Cross9227bd32013-07-23 16:59:20 -070032#include <log/logd.h>
33#include <log/logprint.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070034
35typedef struct FilterInfo_t {
36 char *mTag;
37 android_LogPriority mPri;
38 struct FilterInfo_t *p_next;
39} FilterInfo;
40
41struct AndroidLogFormat_t {
42 android_LogPriority global_pri;
43 FilterInfo *filters;
44 AndroidLogPrintFormat format;
Pierre Zurekead88fc2010-10-17 22:39:37 +020045 bool colored_output;
Mark Salyzyne1f20042015-05-06 08:40:40 -070046 bool usec_time_output;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070047};
48
Pierre Zurekead88fc2010-10-17 22:39:37 +020049/*
50 * gnome-terminal color tags
51 * See http://misc.flogisoft.com/bash/tip_colors_and_formatting
52 * for ideas on how to set the forground color of the text for xterm.
53 * The color manipulation character stream is defined as:
54 * ESC [ 3 8 ; 5 ; <color#> m
55 */
56#define ANDROID_COLOR_BLUE 75
57#define ANDROID_COLOR_DEFAULT 231
58#define ANDROID_COLOR_GREEN 40
59#define ANDROID_COLOR_ORANGE 166
60#define ANDROID_COLOR_RED 196
61#define ANDROID_COLOR_YELLOW 226
62
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070063static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri)
64{
65 FilterInfo *p_ret;
66
67 p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
68 p_ret->mTag = strdup(tag);
69 p_ret->mPri = pri;
70
71 return p_ret;
72}
73
Mark Salyzyna04464a2014-04-30 08:50:53 -070074/* balance to above, filterinfo_free left unimplemented */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070075
76/*
77 * Note: also accepts 0-9 priorities
78 * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
79 */
80static android_LogPriority filterCharToPri (char c)
81{
82 android_LogPriority pri;
83
84 c = tolower(c);
85
86 if (c >= '0' && c <= '9') {
87 if (c >= ('0'+ANDROID_LOG_SILENT)) {
88 pri = ANDROID_LOG_VERBOSE;
89 } else {
90 pri = (android_LogPriority)(c - '0');
91 }
92 } else if (c == 'v') {
93 pri = ANDROID_LOG_VERBOSE;
94 } else if (c == 'd') {
95 pri = ANDROID_LOG_DEBUG;
96 } else if (c == 'i') {
97 pri = ANDROID_LOG_INFO;
98 } else if (c == 'w') {
99 pri = ANDROID_LOG_WARN;
100 } else if (c == 'e') {
101 pri = ANDROID_LOG_ERROR;
102 } else if (c == 'f') {
103 pri = ANDROID_LOG_FATAL;
104 } else if (c == 's') {
105 pri = ANDROID_LOG_SILENT;
106 } else if (c == '*') {
107 pri = ANDROID_LOG_DEFAULT;
108 } else {
109 pri = ANDROID_LOG_UNKNOWN;
110 }
111
112 return pri;
113}
114
115static char filterPriToChar (android_LogPriority pri)
116{
117 switch (pri) {
118 case ANDROID_LOG_VERBOSE: return 'V';
119 case ANDROID_LOG_DEBUG: return 'D';
120 case ANDROID_LOG_INFO: return 'I';
121 case ANDROID_LOG_WARN: return 'W';
122 case ANDROID_LOG_ERROR: return 'E';
123 case ANDROID_LOG_FATAL: return 'F';
124 case ANDROID_LOG_SILENT: return 'S';
125
126 case ANDROID_LOG_DEFAULT:
127 case ANDROID_LOG_UNKNOWN:
128 default: return '?';
129 }
130}
131
Pierre Zurekead88fc2010-10-17 22:39:37 +0200132static int colorFromPri (android_LogPriority pri)
133{
134 switch (pri) {
135 case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
136 case ANDROID_LOG_DEBUG: return ANDROID_COLOR_BLUE;
137 case ANDROID_LOG_INFO: return ANDROID_COLOR_GREEN;
138 case ANDROID_LOG_WARN: return ANDROID_COLOR_ORANGE;
139 case ANDROID_LOG_ERROR: return ANDROID_COLOR_RED;
140 case ANDROID_LOG_FATAL: return ANDROID_COLOR_RED;
141 case ANDROID_LOG_SILENT: return ANDROID_COLOR_DEFAULT;
142
143 case ANDROID_LOG_DEFAULT:
144 case ANDROID_LOG_UNKNOWN:
145 default: return ANDROID_COLOR_DEFAULT;
146 }
147}
148
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700149static android_LogPriority filterPriForTag(
150 AndroidLogFormat *p_format, const char *tag)
151{
152 FilterInfo *p_curFilter;
153
154 for (p_curFilter = p_format->filters
155 ; p_curFilter != NULL
156 ; p_curFilter = p_curFilter->p_next
157 ) {
158 if (0 == strcmp(tag, p_curFilter->mTag)) {
159 if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
160 return p_format->global_pri;
161 } else {
162 return p_curFilter->mPri;
163 }
164 }
165 }
166
167 return p_format->global_pri;
168}
169
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700170/**
171 * returns 1 if this log line should be printed based on its priority
172 * and tag, and 0 if it should not
173 */
174int android_log_shouldPrintLine (
175 AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
176{
177 return pri >= filterPriForTag(p_format, tag);
178}
179
180AndroidLogFormat *android_log_format_new()
181{
182 AndroidLogFormat *p_ret;
183
184 p_ret = calloc(1, sizeof(AndroidLogFormat));
185
186 p_ret->global_pri = ANDROID_LOG_VERBOSE;
187 p_ret->format = FORMAT_BRIEF;
Pierre Zurekead88fc2010-10-17 22:39:37 +0200188 p_ret->colored_output = false;
Mark Salyzyne1f20042015-05-06 08:40:40 -0700189 p_ret->usec_time_output = false;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700190
191 return p_ret;
192}
193
194void android_log_format_free(AndroidLogFormat *p_format)
195{
196 FilterInfo *p_info, *p_info_old;
197
198 p_info = p_format->filters;
199
200 while (p_info != NULL) {
201 p_info_old = p_info;
202 p_info = p_info->p_next;
203
204 free(p_info_old);
205 }
206
207 free(p_format);
208}
209
210
211
Mark Salyzyne1f20042015-05-06 08:40:40 -0700212int android_log_setPrintFormat(AndroidLogFormat *p_format,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700213 AndroidLogPrintFormat format)
214{
Mark Salyzyne1f20042015-05-06 08:40:40 -0700215 if (format == FORMAT_MODIFIER_COLOR) {
Pierre Zurekead88fc2010-10-17 22:39:37 +0200216 p_format->colored_output = true;
Mark Salyzyne1f20042015-05-06 08:40:40 -0700217 return 0;
218 }
219 if (format == FORMAT_MODIFIER_TIME_USEC) {
220 p_format->usec_time_output = true;
221 return 0;
222 }
223 p_format->format = format;
224 return 1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700225}
226
227/**
228 * Returns FORMAT_OFF on invalid string
229 */
230AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
231{
232 static AndroidLogPrintFormat format;
233
234 if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
235 else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
236 else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
237 else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
238 else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
239 else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
240 else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
241 else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
Mark Salyzyne1f20042015-05-06 08:40:40 -0700242 else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
243 else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700244 else format = FORMAT_OFF;
245
246 return format;
247}
248
249/**
250 * filterExpression: a single filter expression
251 * eg "AT:d"
252 *
253 * returns 0 on success and -1 on invalid expression
254 *
255 * Assumes single threaded execution
256 */
257
258int android_log_addFilterRule(AndroidLogFormat *p_format,
259 const char *filterExpression)
260{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700261 size_t tagNameLength;
262 android_LogPriority pri = ANDROID_LOG_DEFAULT;
263
264 tagNameLength = strcspn(filterExpression, ":");
265
266 if (tagNameLength == 0) {
267 goto error;
268 }
269
270 if(filterExpression[tagNameLength] == ':') {
271 pri = filterCharToPri(filterExpression[tagNameLength+1]);
272
273 if (pri == ANDROID_LOG_UNKNOWN) {
274 goto error;
275 }
276 }
277
278 if(0 == strncmp("*", filterExpression, tagNameLength)) {
279 // This filter expression refers to the global filter
280 // The default level for this is DEBUG if the priority
281 // is unspecified
282 if (pri == ANDROID_LOG_DEFAULT) {
283 pri = ANDROID_LOG_DEBUG;
284 }
285
286 p_format->global_pri = pri;
287 } else {
288 // for filter expressions that don't refer to the global
289 // filter, the default is verbose if the priority is unspecified
290 if (pri == ANDROID_LOG_DEFAULT) {
291 pri = ANDROID_LOG_VERBOSE;
292 }
293
294 char *tagName;
295
296// Presently HAVE_STRNDUP is never defined, so the second case is always taken
297// Darwin doesn't have strnup, everything else does
298#ifdef HAVE_STRNDUP
299 tagName = strndup(filterExpression, tagNameLength);
300#else
301 //a few extra bytes copied...
302 tagName = strdup(filterExpression);
303 tagName[tagNameLength] = '\0';
304#endif /*HAVE_STRNDUP*/
305
306 FilterInfo *p_fi = filterinfo_new(tagName, pri);
307 free(tagName);
308
309 p_fi->p_next = p_format->filters;
310 p_format->filters = p_fi;
311 }
312
313 return 0;
314error:
315 return -1;
316}
317
318
319/**
320 * filterString: a comma/whitespace-separated set of filter expressions
321 *
322 * eg "AT:d *:i"
323 *
324 * returns 0 on success and -1 on invalid expression
325 *
326 * Assumes single threaded execution
327 *
328 */
329
330int android_log_addFilterString(AndroidLogFormat *p_format,
331 const char *filterString)
332{
333 char *filterStringCopy = strdup (filterString);
334 char *p_cur = filterStringCopy;
335 char *p_ret;
336 int err;
337
338 // Yes, I'm using strsep
339 while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
340 // ignore whitespace-only entries
341 if(p_ret[0] != '\0') {
342 err = android_log_addFilterRule(p_format, p_ret);
343
344 if (err < 0) {
345 goto error;
346 }
347 }
348 }
349
350 free (filterStringCopy);
351 return 0;
352error:
353 free (filterStringCopy);
354 return -1;
355}
356
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700357/**
358 * Splits a wire-format buffer into an AndroidLogEntry
359 * entry allocated by caller. Pointers will point directly into buf
360 *
361 * Returns 0 on success and -1 on invalid wire format (entry will be
362 * in unspecified state)
363 */
364int android_log_processLogBuffer(struct logger_entry *buf,
365 AndroidLogEntry *entry)
366{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700367 entry->tv_sec = buf->sec;
368 entry->tv_nsec = buf->nsec;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700369 entry->pid = buf->pid;
370 entry->tid = buf->tid;
Kenny Root4bf3c022011-09-30 17:10:14 -0700371
372 /*
373 * format: <priority:1><tag:N>\0<message:N>\0
374 *
375 * tag str
Nick Kraleviche1ede152011-10-18 15:23:33 -0700376 * starts at buf->msg+1
Kenny Root4bf3c022011-09-30 17:10:14 -0700377 * msg
Nick Kraleviche1ede152011-10-18 15:23:33 -0700378 * starts at buf->msg+1+len(tag)+1
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700379 *
380 * The message may have been truncated by the kernel log driver.
381 * When that happens, we must null-terminate the message ourselves.
Kenny Root4bf3c022011-09-30 17:10:14 -0700382 */
Nick Kraleviche1ede152011-10-18 15:23:33 -0700383 if (buf->len < 3) {
384 // An well-formed entry must consist of at least a priority
385 // and two null characters
386 fprintf(stderr, "+++ LOG: entry too small\n");
Kenny Root4bf3c022011-09-30 17:10:14 -0700387 return -1;
388 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700389
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700390 int msgStart = -1;
391 int msgEnd = -1;
392
Nick Kraleviche1ede152011-10-18 15:23:33 -0700393 int i;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800394 char *msg = buf->msg;
395 struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
396 if (buf2->hdr_size) {
397 msg = ((char *)buf2) + buf2->hdr_size;
398 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700399 for (i = 1; i < buf->len; i++) {
Mark Salyzyn40b21552013-12-18 12:59:01 -0800400 if (msg[i] == '\0') {
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700401 if (msgStart == -1) {
402 msgStart = i + 1;
403 } else {
404 msgEnd = i;
405 break;
406 }
Nick Kraleviche1ede152011-10-18 15:23:33 -0700407 }
408 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700409
410 if (msgStart == -1) {
411 fprintf(stderr, "+++ LOG: malformed log message\n");
Nick Kralevich63f4a842011-10-17 10:45:03 -0700412 return -1;
413 }
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700414 if (msgEnd == -1) {
415 // incoming message not null-terminated; force it
416 msgEnd = buf->len - 1;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800417 msg[msgEnd] = '\0';
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700418 }
419
Mark Salyzyn40b21552013-12-18 12:59:01 -0800420 entry->priority = msg[0];
421 entry->tag = msg + 1;
422 entry->message = msg + msgStart;
Jeff Sharkeya820a0e2011-10-26 18:40:39 -0700423 entry->messageLen = msgEnd - msgStart;
Nick Kralevich63f4a842011-10-17 10:45:03 -0700424
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700425 return 0;
426}
427
428/*
Mark Salyzyn8470dad2015-03-06 20:42:57 +0000429 * Extract a 4-byte value from a byte stream.
430 */
431static inline uint32_t get4LE(const uint8_t* src)
432{
433 return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
434}
435
436/*
437 * Extract an 8-byte value from a byte stream.
438 */
439static inline uint64_t get8LE(const uint8_t* src)
440{
441 uint32_t low, high;
442
443 low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
444 high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
Jeff Brown44193d92015-04-28 12:47:02 -0700445 return ((uint64_t) high << 32) | (uint64_t) low;
Mark Salyzyn8470dad2015-03-06 20:42:57 +0000446}
447
448
449/*
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700450 * Recursively convert binary log data to printable form.
451 *
452 * This needs to be recursive because you can have lists of lists.
453 *
454 * If we run out of room, we stop processing immediately. It's important
455 * for us to check for space on every output element to avoid producing
456 * garbled output.
457 *
458 * Returns 0 on success, 1 on buffer full, -1 on failure.
459 */
460static int android_log_printBinaryEvent(const unsigned char** pEventData,
461 size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
462{
463 const unsigned char* eventData = *pEventData;
464 size_t eventDataLen = *pEventDataLen;
465 char* outBuf = *pOutBuf;
466 size_t outBufLen = *pOutBufLen;
467 unsigned char type;
468 size_t outCount;
469 int result = 0;
470
471 if (eventDataLen < 1)
472 return -1;
473 type = *eventData++;
474 eventDataLen--;
475
476 //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
477
478 switch (type) {
479 case EVENT_TYPE_INT:
480 /* 32-bit signed int */
481 {
482 int ival;
483
484 if (eventDataLen < 4)
485 return -1;
Mark Salyzyn8470dad2015-03-06 20:42:57 +0000486 ival = get4LE(eventData);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700487 eventData += 4;
488 eventDataLen -= 4;
489
490 outCount = snprintf(outBuf, outBufLen, "%d", ival);
491 if (outCount < outBufLen) {
492 outBuf += outCount;
493 outBufLen -= outCount;
494 } else {
495 /* halt output */
496 goto no_room;
497 }
498 }
499 break;
500 case EVENT_TYPE_LONG:
501 /* 64-bit signed long */
502 {
Jeff Brown44193d92015-04-28 12:47:02 -0700503 uint64_t lval;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700504
505 if (eventDataLen < 8)
506 return -1;
Mark Salyzyn8470dad2015-03-06 20:42:57 +0000507 lval = get8LE(eventData);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700508 eventData += 8;
509 eventDataLen -= 8;
510
Jeff Brown44193d92015-04-28 12:47:02 -0700511 outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
512 if (outCount < outBufLen) {
513 outBuf += outCount;
514 outBufLen -= outCount;
515 } else {
516 /* halt output */
517 goto no_room;
518 }
519 }
520 break;
521 case EVENT_TYPE_FLOAT:
522 /* float */
523 {
524 uint32_t ival;
525 float fval;
526
527 if (eventDataLen < 4)
528 return -1;
529 ival = get4LE(eventData);
530 fval = *(float*)&ival;
531 eventData += 4;
532 eventDataLen -= 4;
533
534 outCount = snprintf(outBuf, outBufLen, "%f", fval);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700535 if (outCount < outBufLen) {
536 outBuf += outCount;
537 outBufLen -= outCount;
538 } else {
539 /* halt output */
540 goto no_room;
541 }
542 }
543 break;
544 case EVENT_TYPE_STRING:
545 /* UTF-8 chars, not NULL-terminated */
546 {
547 unsigned int strLen;
548
549 if (eventDataLen < 4)
550 return -1;
Mark Salyzyn8470dad2015-03-06 20:42:57 +0000551 strLen = get4LE(eventData);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700552 eventData += 4;
553 eventDataLen -= 4;
554
555 if (eventDataLen < strLen)
556 return -1;
557
558 if (strLen < outBufLen) {
559 memcpy(outBuf, eventData, strLen);
560 outBuf += strLen;
561 outBufLen -= strLen;
562 } else if (outBufLen > 0) {
563 /* copy what we can */
564 memcpy(outBuf, eventData, outBufLen);
565 outBuf += outBufLen;
566 outBufLen -= outBufLen;
567 goto no_room;
568 }
569 eventData += strLen;
570 eventDataLen -= strLen;
571 break;
572 }
573 case EVENT_TYPE_LIST:
574 /* N items, all different types */
575 {
576 unsigned char count;
577 int i;
578
579 if (eventDataLen < 1)
580 return -1;
581
582 count = *eventData++;
583 eventDataLen--;
584
585 if (outBufLen > 0) {
586 *outBuf++ = '[';
587 outBufLen--;
588 } else {
589 goto no_room;
590 }
591
592 for (i = 0; i < count; i++) {
593 result = android_log_printBinaryEvent(&eventData, &eventDataLen,
594 &outBuf, &outBufLen);
595 if (result != 0)
596 goto bail;
597
598 if (i < count-1) {
599 if (outBufLen > 0) {
600 *outBuf++ = ',';
601 outBufLen--;
602 } else {
603 goto no_room;
604 }
605 }
606 }
607
608 if (outBufLen > 0) {
609 *outBuf++ = ']';
610 outBufLen--;
611 } else {
612 goto no_room;
613 }
614 }
615 break;
616 default:
617 fprintf(stderr, "Unknown binary event type %d\n", type);
618 return -1;
619 }
620
621bail:
622 *pEventData = eventData;
623 *pEventDataLen = eventDataLen;
624 *pOutBuf = outBuf;
625 *pOutBufLen = outBufLen;
626 return result;
627
628no_room:
629 result = 1;
630 goto bail;
631}
632
633/**
634 * Convert a binary log entry to ASCII form.
635 *
636 * For convenience we mimic the processLogBuffer API. There is no
637 * pre-defined output length for the binary data, since we're free to format
638 * it however we choose, which means we can't really use a fixed-size buffer
639 * here.
640 */
641int android_log_processBinaryLogBuffer(struct logger_entry *buf,
642 AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
643 int messageBufLen)
644{
645 size_t inCount;
646 unsigned int tagIndex;
647 const unsigned char* eventData;
648
649 entry->tv_sec = buf->sec;
650 entry->tv_nsec = buf->nsec;
651 entry->priority = ANDROID_LOG_INFO;
652 entry->pid = buf->pid;
653 entry->tid = buf->tid;
654
655 /*
656 * Pull the tag out.
657 */
658 eventData = (const unsigned char*) buf->msg;
Mark Salyzyn40b21552013-12-18 12:59:01 -0800659 struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
660 if (buf2->hdr_size) {
661 eventData = ((unsigned char *)buf2) + buf2->hdr_size;
662 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700663 inCount = buf->len;
664 if (inCount < 4)
665 return -1;
Mark Salyzyn8470dad2015-03-06 20:42:57 +0000666 tagIndex = get4LE(eventData);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700667 eventData += 4;
668 inCount -= 4;
669
670 if (map != NULL) {
671 entry->tag = android_lookupEventTag(map, tagIndex);
672 } else {
673 entry->tag = NULL;
674 }
675
676 /*
677 * If we don't have a map, or didn't find the tag number in the map,
678 * stuff a generated tag value into the start of the output buffer and
679 * shift the buffer pointers down.
680 */
681 if (entry->tag == NULL) {
682 int tagLen;
683
684 tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
685 entry->tag = messageBuf;
686 messageBuf += tagLen+1;
687 messageBufLen -= tagLen+1;
688 }
689
690 /*
691 * Format the event log data into the buffer.
692 */
693 char* outBuf = messageBuf;
694 size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
695 int result;
696 result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
697 &outRemaining);
698 if (result < 0) {
699 fprintf(stderr, "Binary log entry conversion failed\n");
700 return -1;
701 } else if (result == 1) {
702 if (outBuf > messageBuf) {
703 /* leave an indicator */
704 *(outBuf-1) = '!';
705 } else {
706 /* no room to output anything at all */
707 *outBuf++ = '!';
708 outRemaining--;
709 }
710 /* pretend we ate all the data */
711 inCount = 0;
712 }
713
714 /* eat the silly terminating '\n' */
715 if (inCount == 1 && *eventData == '\n') {
716 eventData++;
717 inCount--;
718 }
719
720 if (inCount != 0) {
721 fprintf(stderr,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800722 "Warning: leftover binary log data (%zu bytes)\n", inCount);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700723 }
724
725 /*
726 * Terminate the buffer. The NUL byte does not count as part of
727 * entry->messageLen.
728 */
729 *outBuf = '\0';
730 entry->messageLen = outBuf - messageBuf;
731 assert(entry->messageLen == (messageBufLen-1) - outRemaining);
732
733 entry->message = messageBuf;
734
735 return 0;
736}
737
738/**
739 * Formats a log message into a buffer
740 *
741 * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
742 * If return value != defaultBuffer, caller must call free()
743 * Returns NULL on malloc error
744 */
745
746char *android_log_formatLogLine (
747 AndroidLogFormat *p_format,
748 char *defaultBuffer,
749 size_t defaultBufferSize,
750 const AndroidLogEntry *entry,
751 size_t *p_outLength)
752{
Yabin Cui8a985352014-11-13 10:02:08 -0800753#if !defined(_WIN32)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700754 struct tm tmBuf;
755#endif
756 struct tm* ptm;
Mark Salyzyne1f20042015-05-06 08:40:40 -0700757 char timeBuf[32]; /* good margin, 23+nul for msec, 26+nul for usec */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700758 char prefixBuf[128], suffixBuf[128];
759 char priChar;
760 int prefixSuffixIsHeaderFooter = 0;
761 char * ret = NULL;
762
763 priChar = filterPriToChar(entry->priority);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200764 size_t prefixLen = 0, suffixLen = 0;
765 size_t len;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700766
767 /*
768 * Get the current date/time in pretty form
769 *
770 * It's often useful when examining a log with "less" to jump to
771 * a specific point in the file by searching for the date/time stamp.
772 * For this reason it's very annoying to have regexp meta characters
773 * in the time stamp. Don't use forward slashes, parenthesis,
774 * brackets, asterisks, or other special chars here.
775 */
Yabin Cui8a985352014-11-13 10:02:08 -0800776#if !defined(_WIN32)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700777 ptm = localtime_r(&(entry->tv_sec), &tmBuf);
778#else
779 ptm = localtime(&(entry->tv_sec));
780#endif
781 //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
782 strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
Mark Salyzyne1f20042015-05-06 08:40:40 -0700783 len = strlen(timeBuf);
784 if (p_format->usec_time_output) {
785 snprintf(timeBuf + len, sizeof(timeBuf) - len,
786 ".%06ld", entry->tv_nsec / 1000);
787 } else {
788 snprintf(timeBuf + len, sizeof(timeBuf) - len,
789 ".%03ld", entry->tv_nsec / 1000000);
790 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700791
792 /*
793 * Construct a buffer containing the log header and log message.
794 */
Pierre Zurekead88fc2010-10-17 22:39:37 +0200795 if (p_format->colored_output) {
796 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
797 colorFromPri(entry->priority));
798 prefixLen = MIN(prefixLen, sizeof(prefixBuf));
799 suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m");
800 suffixLen = MIN(suffixLen, sizeof(suffixBuf));
801 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700802
803 switch (p_format->format) {
804 case FORMAT_TAG:
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 "%c/%-8s: ", priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200807 strcpy(suffixBuf + suffixLen, "\n");
808 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700809 break;
810 case FORMAT_PROCESS:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200811 len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700812 " (%s)\n", entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200813 suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
814 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
815 "%c(%5d) ", priChar, entry->pid);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700816 break;
817 case FORMAT_THREAD:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200818 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800819 "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200820 strcpy(suffixBuf + suffixLen, "\n");
821 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700822 break;
823 case FORMAT_RAW:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200824 prefixBuf[prefixLen] = 0;
825 len = 0;
826 strcpy(suffixBuf + suffixLen, "\n");
827 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700828 break;
829 case FORMAT_TIME:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200830 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Mark Salyzyne1f20042015-05-06 08:40:40 -0700831 "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200832 strcpy(suffixBuf + suffixLen, "\n");
833 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700834 break;
835 case FORMAT_THREADTIME:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200836 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Mark Salyzyne1f20042015-05-06 08:40:40 -0700837 "%s %5d %5d %c %-8s: ", timeBuf,
Andrew Hsiehd2c8f522012-02-27 16:48:18 -0800838 entry->pid, entry->tid, priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200839 strcpy(suffixBuf + suffixLen, "\n");
840 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700841 break;
842 case FORMAT_LONG:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200843 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
Mark Salyzyne1f20042015-05-06 08:40:40 -0700844 "[ %s %5d:%5d %c/%-8s ]\n",
845 timeBuf, entry->pid, entry->tid, priChar, entry->tag);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200846 strcpy(suffixBuf + suffixLen, "\n\n");
847 suffixLen += 2;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700848 prefixSuffixIsHeaderFooter = 1;
849 break;
850 case FORMAT_BRIEF:
851 default:
Pierre Zurekead88fc2010-10-17 22:39:37 +0200852 len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700853 "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
Pierre Zurekead88fc2010-10-17 22:39:37 +0200854 strcpy(suffixBuf + suffixLen, "\n");
855 ++suffixLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700856 break;
857 }
Pierre Zurekead88fc2010-10-17 22:39:37 +0200858
Keith Prestonb45b5c92010-02-11 15:12:53 -0600859 /* snprintf has a weird return value. It returns what would have been
860 * written given a large enough buffer. In the case that the prefix is
861 * longer then our buffer(128), it messes up the calculations below
862 * possibly causing heap corruption. To avoid this we double check and
863 * set the length at the maximum (size minus null byte)
864 */
Pierre Zurekead88fc2010-10-17 22:39:37 +0200865 prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
Mark Salyzyne2428422015-01-22 10:00:04 -0800866 suffixLen = MIN(suffixLen, sizeof(suffixBuf));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700867
868 /* the following code is tragically unreadable */
869
870 size_t numLines;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700871 char *p;
872 size_t bufferSize;
873 const char *pm;
874
875 if (prefixSuffixIsHeaderFooter) {
876 // we're just wrapping message with a header/footer
877 numLines = 1;
878 } else {
879 pm = entry->message;
880 numLines = 0;
881
882 // The line-end finding here must match the line-end finding
883 // in for ( ... numLines...) loop below
884 while (pm < (entry->message + entry->messageLen)) {
885 if (*pm++ == '\n') numLines++;
886 }
887 // plus one line for anything not newline-terminated at the end
888 if (pm > entry->message && *(pm-1) != '\n') numLines++;
889 }
890
891 // this is an upper bound--newlines in message may be counted
892 // extraneously
893 bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
894
895 if (defaultBufferSize >= bufferSize) {
896 ret = defaultBuffer;
897 } else {
898 ret = (char *)malloc(bufferSize);
899
900 if (ret == NULL) {
901 return ret;
902 }
903 }
904
905 ret[0] = '\0'; /* to start strcat off */
906
907 p = ret;
908 pm = entry->message;
909
910 if (prefixSuffixIsHeaderFooter) {
911 strcat(p, prefixBuf);
912 p += prefixLen;
913 strncat(p, entry->message, entry->messageLen);
914 p += entry->messageLen;
915 strcat(p, suffixBuf);
916 p += suffixLen;
917 } else {
918 while(pm < (entry->message + entry->messageLen)) {
919 const char *lineStart;
920 size_t lineLen;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700921 lineStart = pm;
922
923 // Find the next end-of-line in message
924 while (pm < (entry->message + entry->messageLen)
925 && *pm != '\n') pm++;
926 lineLen = pm - lineStart;
927
928 strcat(p, prefixBuf);
929 p += prefixLen;
930 strncat(p, lineStart, lineLen);
931 p += lineLen;
932 strcat(p, suffixBuf);
933 p += suffixLen;
934
935 if (*pm == '\n') pm++;
936 }
937 }
938
939 if (p_outLength != NULL) {
940 *p_outLength = p - ret;
941 }
942
943 return ret;
944}
945
946/**
947 * Either print or do not print log line, based on filter
948 *
949 * Returns count bytes written
950 */
951
Joe Onoratoe2bf2ea2010-03-01 09:11:54 -0800952int android_log_printLogLine(
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700953 AndroidLogFormat *p_format,
954 int fd,
955 const AndroidLogEntry *entry)
956{
957 int ret;
958 char defaultBuffer[512];
959 char *outBuffer = NULL;
960 size_t totalLen;
961
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700962 outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
963 sizeof(defaultBuffer), entry, &totalLen);
964
965 if (!outBuffer)
966 return -1;
967
968 do {
969 ret = write(fd, outBuffer, totalLen);
970 } while (ret < 0 && errno == EINTR);
971
972 if (ret < 0) {
973 fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
974 ret = 0;
975 goto done;
976 }
977
978 if (((size_t)ret) < totalLen) {
979 fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
980 (int)totalLen);
981 goto done;
982 }
983
984done:
985 if (outBuffer != defaultBuffer) {
986 free(outBuffer);
987 }
988
989 return ret;
990}