Line data Source code
1 : /* //device/libs/cutils/logprint.c
2 : **
3 : ** Copyright 2006, The Android Open Source Project
4 : **
5 : ** Licensed under the Apache License, Version 2.0 (the "License");
6 : ** you may not use this file except in compliance with the License.
7 : ** You may obtain a copy of the License at
8 : **
9 : ** http://www.apache.org/licenses/LICENSE-2.0
10 : **
11 : ** Unless required by applicable law or agreed to in writing, software
12 : ** distributed under the License is distributed on an "AS IS" BASIS,
13 : ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : ** See the License for the specific language governing permissions and
15 : ** limitations under the License.
16 : */
17 :
18 : #define _GNU_SOURCE /* for asprintf */
19 :
20 : #include <ctype.h>
21 : #include <stdio.h>
22 : #include <errno.h>
23 : #include <stdlib.h>
24 : #include <stdint.h>
25 : #include <string.h>
26 : #include <assert.h>
27 : #include <arpa/inet.h>
28 :
29 : #include <log/logd.h>
30 : #include <log/logprint.h>
31 :
32 : #ifdef _MSC_VER
33 : #if _MSC_VER < 1900
34 : #include <nspr/prprf.h>
35 : #define snprintf PR_snprintf
36 : #endif
37 : #define inline
38 : /* We don't want to indent large blocks because it causes unnecessary merge
39 : * conflicts */
40 : #define UNINDENTED_BLOCK_START {
41 : #define UNINDENTED_BLOCK_END }
42 : #else
43 : #define UNINDENTED_BLOCK_START
44 : #define UNINDENTED_BLOCK_END
45 : #endif
46 :
47 : #ifdef WIN32
48 : static char *
49 : strsep(char **stringp, const char *delim)
50 : {
51 : char* res = *stringp;
52 : while (**stringp) {
53 : const char *c;
54 : for (c = delim; *c; c++) {
55 : if (**stringp == *c) {
56 : **stringp++ = 0;
57 : return res;
58 : }
59 : }
60 : }
61 : return res;
62 : }
63 : #endif
64 :
65 : typedef struct FilterInfo_t {
66 : char *mTag;
67 : android_LogPriority mPri;
68 : struct FilterInfo_t *p_next;
69 : } FilterInfo;
70 :
71 : struct AndroidLogFormat_t {
72 : android_LogPriority global_pri;
73 : FilterInfo *filters;
74 : AndroidLogPrintFormat format;
75 : };
76 :
77 0 : static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri)
78 : {
79 : FilterInfo *p_ret;
80 :
81 0 : p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
82 0 : p_ret->mTag = strdup(tag);
83 0 : p_ret->mPri = pri;
84 :
85 0 : return p_ret;
86 : }
87 :
88 0 : static void filterinfo_free(FilterInfo *p_info)
89 : {
90 0 : if (p_info == NULL) {
91 0 : return;
92 : }
93 :
94 0 : free(p_info->mTag);
95 0 : p_info->mTag = NULL;
96 : }
97 :
98 : /*
99 : * Note: also accepts 0-9 priorities
100 : * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
101 : */
102 0 : static android_LogPriority filterCharToPri (char c)
103 : {
104 : android_LogPriority pri;
105 :
106 0 : c = tolower(c);
107 :
108 0 : if (c >= '0' && c <= '9') {
109 0 : if (c >= ('0'+ANDROID_LOG_SILENT)) {
110 0 : pri = ANDROID_LOG_VERBOSE;
111 : } else {
112 0 : pri = (android_LogPriority)(c - '0');
113 : }
114 0 : } else if (c == 'v') {
115 0 : pri = ANDROID_LOG_VERBOSE;
116 0 : } else if (c == 'd') {
117 0 : pri = ANDROID_LOG_DEBUG;
118 0 : } else if (c == 'i') {
119 0 : pri = ANDROID_LOG_INFO;
120 0 : } else if (c == 'w') {
121 0 : pri = ANDROID_LOG_WARN;
122 0 : } else if (c == 'e') {
123 0 : pri = ANDROID_LOG_ERROR;
124 0 : } else if (c == 'f') {
125 0 : pri = ANDROID_LOG_FATAL;
126 0 : } else if (c == 's') {
127 0 : pri = ANDROID_LOG_SILENT;
128 0 : } else if (c == '*') {
129 0 : pri = ANDROID_LOG_DEFAULT;
130 : } else {
131 0 : pri = ANDROID_LOG_UNKNOWN;
132 : }
133 :
134 0 : return pri;
135 : }
136 :
137 0 : static char filterPriToChar (android_LogPriority pri)
138 : {
139 0 : switch (pri) {
140 0 : case ANDROID_LOG_VERBOSE: return 'V';
141 0 : case ANDROID_LOG_DEBUG: return 'D';
142 0 : case ANDROID_LOG_INFO: return 'I';
143 0 : case ANDROID_LOG_WARN: return 'W';
144 0 : case ANDROID_LOG_ERROR: return 'E';
145 0 : case ANDROID_LOG_FATAL: return 'F';
146 0 : case ANDROID_LOG_SILENT: return 'S';
147 :
148 : case ANDROID_LOG_DEFAULT:
149 : case ANDROID_LOG_UNKNOWN:
150 0 : default: return '?';
151 : }
152 : }
153 :
154 0 : static android_LogPriority filterPriForTag(
155 : AndroidLogFormat *p_format, const char *tag)
156 : {
157 : FilterInfo *p_curFilter;
158 :
159 0 : for (p_curFilter = p_format->filters
160 : ; p_curFilter != NULL
161 0 : ; p_curFilter = p_curFilter->p_next
162 : ) {
163 0 : if (0 == strcmp(tag, p_curFilter->mTag)) {
164 0 : if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
165 0 : return p_format->global_pri;
166 : } else {
167 0 : return p_curFilter->mPri;
168 : }
169 : }
170 : }
171 :
172 0 : return p_format->global_pri;
173 : }
174 :
175 : /** for debugging */
176 0 : static void dumpFilters(AndroidLogFormat *p_format)
177 : {
178 : FilterInfo *p_fi;
179 :
180 0 : for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) {
181 0 : char cPri = filterPriToChar(p_fi->mPri);
182 0 : if (p_fi->mPri == ANDROID_LOG_DEFAULT) {
183 0 : cPri = filterPriToChar(p_format->global_pri);
184 : }
185 0 : fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri);
186 : }
187 :
188 0 : fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri));
189 :
190 0 : }
191 :
192 : /**
193 : * returns 1 if this log line should be printed based on its priority
194 : * and tag, and 0 if it should not
195 : */
196 0 : int android_log_shouldPrintLine (
197 : AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
198 : {
199 0 : return pri >= filterPriForTag(p_format, tag);
200 : }
201 :
202 0 : AndroidLogFormat *android_log_format_new()
203 : {
204 : AndroidLogFormat *p_ret;
205 :
206 0 : p_ret = calloc(1, sizeof(AndroidLogFormat));
207 :
208 0 : p_ret->global_pri = ANDROID_LOG_VERBOSE;
209 0 : p_ret->format = FORMAT_BRIEF;
210 :
211 0 : return p_ret;
212 : }
213 :
214 0 : void android_log_format_free(AndroidLogFormat *p_format)
215 : {
216 : FilterInfo *p_info, *p_info_old;
217 :
218 0 : p_info = p_format->filters;
219 :
220 0 : while (p_info != NULL) {
221 0 : p_info_old = p_info;
222 0 : p_info = p_info->p_next;
223 :
224 0 : free(p_info_old);
225 : }
226 :
227 0 : free(p_format);
228 0 : }
229 :
230 :
231 :
232 0 : void android_log_setPrintFormat(AndroidLogFormat *p_format,
233 : AndroidLogPrintFormat format)
234 : {
235 0 : p_format->format=format;
236 0 : }
237 :
238 : /**
239 : * Returns FORMAT_OFF on invalid string
240 : */
241 0 : AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
242 : {
243 : static AndroidLogPrintFormat format;
244 :
245 0 : if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
246 0 : else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
247 0 : else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
248 0 : else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
249 0 : else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
250 0 : else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
251 0 : else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
252 0 : else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
253 0 : else format = FORMAT_OFF;
254 :
255 0 : return format;
256 : }
257 :
258 : /**
259 : * filterExpression: a single filter expression
260 : * eg "AT:d"
261 : *
262 : * returns 0 on success and -1 on invalid expression
263 : *
264 : * Assumes single threaded execution
265 : */
266 :
267 0 : int android_log_addFilterRule(AndroidLogFormat *p_format,
268 : const char *filterExpression)
269 : {
270 : size_t tagNameLength;
271 0 : android_LogPriority pri = ANDROID_LOG_DEFAULT;
272 :
273 0 : tagNameLength = strcspn(filterExpression, ":");
274 :
275 0 : if (tagNameLength == 0) {
276 0 : goto error;
277 : }
278 :
279 0 : if(filterExpression[tagNameLength] == ':') {
280 0 : pri = filterCharToPri(filterExpression[tagNameLength+1]);
281 :
282 0 : if (pri == ANDROID_LOG_UNKNOWN) {
283 0 : goto error;
284 : }
285 : }
286 :
287 0 : if(0 == strncmp("*", filterExpression, tagNameLength)) {
288 : // This filter expression refers to the global filter
289 : // The default level for this is DEBUG if the priority
290 : // is unspecified
291 0 : if (pri == ANDROID_LOG_DEFAULT) {
292 0 : pri = ANDROID_LOG_DEBUG;
293 : }
294 :
295 0 : p_format->global_pri = pri;
296 : } else {
297 : // for filter expressions that don't refer to the global
298 : // filter, the default is verbose if the priority is unspecified
299 0 : if (pri == ANDROID_LOG_DEFAULT) {
300 0 : pri = ANDROID_LOG_VERBOSE;
301 : }
302 :
303 : UNINDENTED_BLOCK_START
304 : char *tagName;
305 :
306 : // Presently HAVE_STRNDUP is never defined, so the second case is always taken
307 : // Darwin doesn't have strnup, everything else does
308 : #ifdef HAVE_STRNDUP
309 0 : tagName = strndup(filterExpression, tagNameLength);
310 : #else
311 : //a few extra bytes copied...
312 : tagName = strdup(filterExpression);
313 : tagName[tagNameLength] = '\0';
314 : #endif /*HAVE_STRNDUP*/
315 :
316 : UNINDENTED_BLOCK_START
317 0 : FilterInfo *p_fi = filterinfo_new(tagName, pri);
318 0 : free(tagName);
319 :
320 0 : p_fi->p_next = p_format->filters;
321 0 : p_format->filters = p_fi;
322 : UNINDENTED_BLOCK_END
323 : UNINDENTED_BLOCK_END
324 : }
325 :
326 0 : return 0;
327 : error:
328 0 : return -1;
329 : }
330 :
331 :
332 : /**
333 : * filterString: a comma/whitespace-separated set of filter expressions
334 : *
335 : * eg "AT:d *:i"
336 : *
337 : * returns 0 on success and -1 on invalid expression
338 : *
339 : * Assumes single threaded execution
340 : *
341 : */
342 :
343 0 : int android_log_addFilterString(AndroidLogFormat *p_format,
344 : const char *filterString)
345 : {
346 0 : char *filterStringCopy = strdup (filterString);
347 0 : char *p_cur = filterStringCopy;
348 : char *p_ret;
349 : int err;
350 :
351 : // Yes, I'm using strsep
352 0 : while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
353 : // ignore whitespace-only entries
354 0 : if(p_ret[0] != '\0') {
355 0 : err = android_log_addFilterRule(p_format, p_ret);
356 :
357 0 : if (err < 0) {
358 0 : goto error;
359 : }
360 : }
361 : }
362 :
363 0 : free (filterStringCopy);
364 0 : return 0;
365 : error:
366 0 : free (filterStringCopy);
367 0 : return -1;
368 : }
369 :
370 : static inline char * strip_end(char *str)
371 : {
372 : char *end = str + strlen(str) - 1;
373 :
374 : while (end >= str && isspace(*end))
375 : *end-- = '\0';
376 : return str;
377 : }
378 :
379 : /**
380 : * Splits a wire-format buffer into an AndroidLogEntry
381 : * entry allocated by caller. Pointers will point directly into buf
382 : *
383 : * Returns 0 on success and -1 on invalid wire format (entry will be
384 : * in unspecified state)
385 : */
386 0 : int android_log_processLogBuffer(struct logger_entry *buf,
387 : AndroidLogEntry *entry)
388 : {
389 0 : entry->tv_sec = buf->sec;
390 0 : entry->tv_nsec = buf->nsec;
391 0 : entry->pid = buf->pid;
392 0 : entry->tid = buf->tid;
393 :
394 : /*
395 : * format: <priority:1><tag:N>\0<message:N>\0
396 : *
397 : * tag str
398 : * starts at buf->msg+1
399 : * msg
400 : * starts at buf->msg+1+len(tag)+1
401 : *
402 : * The message may have been truncated by the kernel log driver.
403 : * When that happens, we must null-terminate the message ourselves.
404 : */
405 0 : if (buf->len < 3) {
406 : // An well-formed entry must consist of at least a priority
407 : // and two null characters
408 0 : fprintf(stderr, "+++ LOG: entry too small\n");
409 0 : return -1;
410 : }
411 :
412 : UNINDENTED_BLOCK_START
413 0 : int msgStart = -1;
414 0 : int msgEnd = -1;
415 :
416 : int i;
417 0 : for (i = 1; i < buf->len; i++) {
418 0 : if (buf->msg[i] == '\0') {
419 0 : if (msgStart == -1) {
420 0 : msgStart = i + 1;
421 : } else {
422 0 : msgEnd = i;
423 0 : break;
424 : }
425 : }
426 : }
427 :
428 0 : if (msgStart == -1) {
429 0 : fprintf(stderr, "+++ LOG: malformed log message\n");
430 0 : return -1;
431 : }
432 0 : if (msgEnd == -1) {
433 : // incoming message not null-terminated; force it
434 0 : msgEnd = buf->len - 1;
435 0 : buf->msg[msgEnd] = '\0';
436 : }
437 :
438 0 : entry->priority = buf->msg[0];
439 0 : entry->tag = buf->msg + 1;
440 0 : entry->message = buf->msg + msgStart;
441 0 : entry->messageLen = msgEnd - msgStart;
442 :
443 0 : return 0;
444 : UNINDENTED_BLOCK_END
445 : }
446 :
447 : /*
448 : * Extract a 4-byte value from a byte stream.
449 : */
450 0 : static inline uint32_t get4LE(const uint8_t* src)
451 : {
452 0 : return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
453 : }
454 :
455 : /*
456 : * Extract an 8-byte value from a byte stream.
457 : */
458 0 : static inline uint64_t get8LE(const uint8_t* src)
459 : {
460 : uint32_t low, high;
461 :
462 0 : low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
463 0 : high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
464 0 : return ((long long) high << 32) | (long long) low;
465 : }
466 :
467 :
468 : /*
469 : * Recursively convert binary log data to printable form.
470 : *
471 : * This needs to be recursive because you can have lists of lists.
472 : *
473 : * If we run out of room, we stop processing immediately. It's important
474 : * for us to check for space on every output element to avoid producing
475 : * garbled output.
476 : *
477 : * Returns 0 on success, 1 on buffer full, -1 on failure.
478 : */
479 0 : static int android_log_printBinaryEvent(const unsigned char** pEventData,
480 : size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
481 : {
482 0 : const unsigned char* eventData = *pEventData;
483 0 : size_t eventDataLen = *pEventDataLen;
484 0 : char* outBuf = *pOutBuf;
485 0 : size_t outBufLen = *pOutBufLen;
486 : unsigned char type;
487 : size_t outCount;
488 0 : int result = 0;
489 :
490 0 : if (eventDataLen < 1)
491 0 : return -1;
492 0 : type = *eventData++;
493 0 : eventDataLen--;
494 :
495 : //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
496 :
497 0 : switch (type) {
498 : case EVENT_TYPE_INT:
499 : /* 32-bit signed int */
500 : {
501 : int ival;
502 :
503 0 : if (eventDataLen < 4)
504 0 : return -1;
505 0 : ival = get4LE(eventData);
506 0 : eventData += 4;
507 0 : eventDataLen -= 4;
508 :
509 0 : outCount = snprintf(outBuf, outBufLen, "%d", ival);
510 0 : if (outCount < outBufLen) {
511 0 : outBuf += outCount;
512 0 : outBufLen -= outCount;
513 : } else {
514 : /* halt output */
515 0 : goto no_room;
516 : }
517 : }
518 0 : break;
519 : case EVENT_TYPE_LONG:
520 : /* 64-bit signed long */
521 : {
522 : long long lval;
523 :
524 0 : if (eventDataLen < 8)
525 0 : return -1;
526 0 : lval = get8LE(eventData);
527 0 : eventData += 8;
528 0 : eventDataLen -= 8;
529 :
530 0 : outCount = snprintf(outBuf, outBufLen, "%lld", lval);
531 0 : if (outCount < outBufLen) {
532 0 : outBuf += outCount;
533 0 : outBufLen -= outCount;
534 : } else {
535 : /* halt output */
536 0 : goto no_room;
537 : }
538 : }
539 0 : break;
540 : case EVENT_TYPE_STRING:
541 : /* UTF-8 chars, not NULL-terminated */
542 : {
543 : unsigned int strLen;
544 :
545 0 : if (eventDataLen < 4)
546 0 : return -1;
547 0 : strLen = get4LE(eventData);
548 0 : eventData += 4;
549 0 : eventDataLen -= 4;
550 :
551 0 : if (eventDataLen < strLen)
552 0 : return -1;
553 :
554 0 : if (strLen < outBufLen) {
555 0 : memcpy(outBuf, eventData, strLen);
556 0 : outBuf += strLen;
557 0 : outBufLen -= strLen;
558 0 : } else if (outBufLen > 0) {
559 : /* copy what we can */
560 0 : memcpy(outBuf, eventData, outBufLen);
561 0 : outBuf += outBufLen;
562 0 : outBufLen -= outBufLen;
563 0 : goto no_room;
564 : }
565 0 : eventData += strLen;
566 0 : eventDataLen -= strLen;
567 0 : break;
568 : }
569 : case EVENT_TYPE_LIST:
570 : /* N items, all different types */
571 : {
572 : unsigned char count;
573 : int i;
574 :
575 0 : if (eventDataLen < 1)
576 0 : return -1;
577 :
578 0 : count = *eventData++;
579 0 : eventDataLen--;
580 :
581 0 : if (outBufLen > 0) {
582 0 : *outBuf++ = '[';
583 0 : outBufLen--;
584 : } else {
585 0 : goto no_room;
586 : }
587 :
588 0 : for (i = 0; i < count; i++) {
589 0 : result = android_log_printBinaryEvent(&eventData, &eventDataLen,
590 : &outBuf, &outBufLen);
591 0 : if (result != 0)
592 0 : goto bail;
593 :
594 0 : if (i < count-1) {
595 0 : if (outBufLen > 0) {
596 0 : *outBuf++ = ',';
597 0 : outBufLen--;
598 : } else {
599 0 : goto no_room;
600 : }
601 : }
602 : }
603 :
604 0 : if (outBufLen > 0) {
605 0 : *outBuf++ = ']';
606 0 : outBufLen--;
607 : } else {
608 0 : goto no_room;
609 : }
610 : }
611 0 : break;
612 : default:
613 0 : fprintf(stderr, "Unknown binary event type %d\n", type);
614 0 : return -1;
615 : }
616 :
617 : bail:
618 0 : *pEventData = eventData;
619 0 : *pEventDataLen = eventDataLen;
620 0 : *pOutBuf = outBuf;
621 0 : *pOutBufLen = outBufLen;
622 0 : return result;
623 :
624 : no_room:
625 0 : result = 1;
626 0 : goto bail;
627 : }
628 :
629 : /**
630 : * Convert a binary log entry to ASCII form.
631 : *
632 : * For convenience we mimic the processLogBuffer API. There is no
633 : * pre-defined output length for the binary data, since we're free to format
634 : * it however we choose, which means we can't really use a fixed-size buffer
635 : * here.
636 : */
637 0 : int android_log_processBinaryLogBuffer(struct logger_entry *buf,
638 : AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
639 : int messageBufLen)
640 : {
641 : size_t inCount;
642 : unsigned int tagIndex;
643 : const unsigned char* eventData;
644 :
645 0 : entry->tv_sec = buf->sec;
646 0 : entry->tv_nsec = buf->nsec;
647 0 : entry->priority = ANDROID_LOG_INFO;
648 0 : entry->pid = buf->pid;
649 0 : entry->tid = buf->tid;
650 :
651 : /*
652 : * Pull the tag out.
653 : */
654 0 : eventData = (const unsigned char*) buf->msg;
655 0 : inCount = buf->len;
656 0 : if (inCount < 4)
657 0 : return -1;
658 0 : tagIndex = get4LE(eventData);
659 0 : eventData += 4;
660 0 : inCount -= 4;
661 :
662 0 : entry->tag = NULL;
663 :
664 : /*
665 : * If we don't have a map, or didn't find the tag number in the map,
666 : * stuff a generated tag value into the start of the output buffer and
667 : * shift the buffer pointers down.
668 : */
669 0 : if (entry->tag == NULL) {
670 : int tagLen;
671 :
672 0 : tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
673 0 : entry->tag = messageBuf;
674 0 : messageBuf += tagLen+1;
675 0 : messageBufLen -= tagLen+1;
676 : }
677 :
678 : /*
679 : * Format the event log data into the buffer.
680 : */
681 : UNINDENTED_BLOCK_START
682 0 : char* outBuf = messageBuf;
683 0 : size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
684 : int result;
685 0 : result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
686 : &outRemaining);
687 0 : if (result < 0) {
688 0 : fprintf(stderr, "Binary log entry conversion failed\n");
689 0 : return -1;
690 0 : } else if (result == 1) {
691 0 : if (outBuf > messageBuf) {
692 : /* leave an indicator */
693 0 : *(outBuf-1) = '!';
694 : } else {
695 : /* no room to output anything at all */
696 0 : *outBuf++ = '!';
697 0 : outRemaining--;
698 : }
699 : /* pretend we ate all the data */
700 0 : inCount = 0;
701 : }
702 :
703 : /* eat the silly terminating '\n' */
704 0 : if (inCount == 1 && *eventData == '\n') {
705 0 : eventData++;
706 0 : inCount--;
707 : }
708 :
709 0 : if (inCount != 0) {
710 0 : fprintf(stderr,
711 : "Warning: leftover binary log data (%zu bytes)\n", inCount);
712 : }
713 :
714 : /*
715 : * Terminate the buffer. The NUL byte does not count as part of
716 : * entry->messageLen.
717 : */
718 0 : *outBuf = '\0';
719 0 : entry->messageLen = outBuf - messageBuf;
720 0 : assert(entry->messageLen == (messageBufLen-1) - outRemaining);
721 :
722 0 : entry->message = messageBuf;
723 :
724 0 : return 0;
725 : UNINDENTED_BLOCK_END
726 : }
727 :
728 : /**
729 : * Formats a log message into a buffer
730 : *
731 : * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
732 : * If return value != defaultBuffer, caller must call free()
733 : * Returns NULL on malloc error
734 : */
735 :
736 0 : char *android_log_formatLogLine (
737 : AndroidLogFormat *p_format,
738 : char *defaultBuffer,
739 : size_t defaultBufferSize,
740 : const AndroidLogEntry *entry,
741 : size_t *p_outLength)
742 : {
743 : #if defined(HAVE_LOCALTIME_R)
744 : struct tm tmBuf;
745 : #endif
746 : struct tm* ptm;
747 : char timeBuf[32];
748 : char prefixBuf[128], suffixBuf[128];
749 : char priChar;
750 0 : int prefixSuffixIsHeaderFooter = 0;
751 0 : char * ret = NULL;
752 :
753 0 : priChar = filterPriToChar(entry->priority);
754 :
755 : /*
756 : * Get the current date/time in pretty form
757 : *
758 : * It's often useful when examining a log with "less" to jump to
759 : * a specific point in the file by searching for the date/time stamp.
760 : * For this reason it's very annoying to have regexp meta characters
761 : * in the time stamp. Don't use forward slashes, parenthesis,
762 : * brackets, asterisks, or other special chars here.
763 : */
764 : #if defined(HAVE_LOCALTIME_R)
765 0 : ptm = localtime_r(&(entry->tv_sec), &tmBuf);
766 : #else
767 : ptm = localtime(&(entry->tv_sec));
768 : #endif
769 : //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
770 0 : strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
771 :
772 : /*
773 : * Construct a buffer containing the log header and log message.
774 : */
775 : UNINDENTED_BLOCK_START
776 : size_t prefixLen, suffixLen;
777 :
778 0 : switch (p_format->format) {
779 : case FORMAT_TAG:
780 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
781 : "%c/%-8s: ", priChar, entry->tag);
782 0 : strcpy(suffixBuf, "\n"); suffixLen = 1;
783 0 : break;
784 : case FORMAT_PROCESS:
785 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
786 : "%c(%5d) ", priChar, entry->pid);
787 0 : suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
788 : " (%s)\n", entry->tag);
789 0 : break;
790 : case FORMAT_THREAD:
791 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
792 : "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
793 0 : strcpy(suffixBuf, "\n");
794 0 : suffixLen = 1;
795 0 : break;
796 : case FORMAT_RAW:
797 0 : prefixBuf[0] = 0;
798 0 : prefixLen = 0;
799 0 : strcpy(suffixBuf, "\n");
800 0 : suffixLen = 1;
801 0 : break;
802 : case FORMAT_TIME:
803 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
804 0 : "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
805 : priChar, entry->tag, entry->pid);
806 0 : strcpy(suffixBuf, "\n");
807 0 : suffixLen = 1;
808 0 : break;
809 : case FORMAT_THREADTIME:
810 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
811 0 : "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
812 : entry->pid, entry->tid, priChar, entry->tag);
813 0 : strcpy(suffixBuf, "\n");
814 0 : suffixLen = 1;
815 0 : break;
816 : case FORMAT_LONG:
817 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
818 : "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
819 0 : timeBuf, entry->tv_nsec / 1000000, entry->pid,
820 : entry->tid, priChar, entry->tag);
821 0 : strcpy(suffixBuf, "\n\n");
822 0 : suffixLen = 2;
823 0 : prefixSuffixIsHeaderFooter = 1;
824 0 : break;
825 : case FORMAT_BRIEF:
826 : default:
827 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
828 : "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
829 0 : strcpy(suffixBuf, "\n");
830 0 : suffixLen = 1;
831 0 : break;
832 : }
833 : /* snprintf has a weird return value. It returns what would have been
834 : * written given a large enough buffer. In the case that the prefix is
835 : * longer then our buffer(128), it messes up the calculations below
836 : * possibly causing heap corruption. To avoid this we double check and
837 : * set the length at the maximum (size minus null byte)
838 : */
839 0 : if(prefixLen >= sizeof(prefixBuf))
840 0 : prefixLen = sizeof(prefixBuf) - 1;
841 0 : if(suffixLen >= sizeof(suffixBuf))
842 0 : suffixLen = sizeof(suffixBuf) - 1;
843 :
844 : /* the following code is tragically unreadable */
845 :
846 : UNINDENTED_BLOCK_START
847 : size_t numLines;
848 : char *p;
849 : size_t bufferSize;
850 : const char *pm;
851 :
852 0 : if (prefixSuffixIsHeaderFooter) {
853 : // we're just wrapping message with a header/footer
854 0 : numLines = 1;
855 : } else {
856 0 : pm = entry->message;
857 0 : numLines = 0;
858 :
859 : // The line-end finding here must match the line-end finding
860 : // in for ( ... numLines...) loop below
861 0 : while (pm < (entry->message + entry->messageLen)) {
862 0 : if (*pm++ == '\n') numLines++;
863 : }
864 : // plus one line for anything not newline-terminated at the end
865 0 : if (pm > entry->message && *(pm-1) != '\n') numLines++;
866 : }
867 :
868 : // this is an upper bound--newlines in message may be counted
869 : // extraneously
870 0 : bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
871 :
872 0 : if (defaultBufferSize >= bufferSize) {
873 0 : ret = defaultBuffer;
874 : } else {
875 0 : ret = (char *)malloc(bufferSize);
876 :
877 0 : if (ret == NULL) {
878 0 : return ret;
879 : }
880 : }
881 :
882 0 : ret[0] = '\0'; /* to start strcat off */
883 :
884 0 : p = ret;
885 0 : pm = entry->message;
886 :
887 0 : if (prefixSuffixIsHeaderFooter) {
888 0 : strcat(p, prefixBuf);
889 0 : p += prefixLen;
890 0 : strncat(p, entry->message, entry->messageLen);
891 0 : p += entry->messageLen;
892 0 : strcat(p, suffixBuf);
893 0 : p += suffixLen;
894 : } else {
895 0 : while(pm < (entry->message + entry->messageLen)) {
896 : const char *lineStart;
897 : size_t lineLen;
898 0 : lineStart = pm;
899 :
900 : // Find the next end-of-line in message
901 0 : while (pm < (entry->message + entry->messageLen)
902 0 : && *pm != '\n') pm++;
903 0 : lineLen = pm - lineStart;
904 :
905 0 : strcat(p, prefixBuf);
906 0 : p += prefixLen;
907 0 : strncat(p, lineStart, lineLen);
908 0 : p += lineLen;
909 0 : strcat(p, suffixBuf);
910 0 : p += suffixLen;
911 :
912 0 : if (*pm == '\n') pm++;
913 : }
914 : }
915 :
916 0 : if (p_outLength != NULL) {
917 0 : *p_outLength = p - ret;
918 : }
919 :
920 0 : return ret;
921 : UNINDENTED_BLOCK_END
922 : UNINDENTED_BLOCK_END
923 : }
924 :
925 : /**
926 : * Either print or do not print log line, based on filter
927 : *
928 : * Returns count bytes written
929 : */
930 :
931 0 : int android_log_printLogLine(
932 : AndroidLogFormat *p_format,
933 : int fd,
934 : const AndroidLogEntry *entry)
935 : {
936 : int ret;
937 : char defaultBuffer[512];
938 0 : char *outBuffer = NULL;
939 : size_t totalLen;
940 :
941 0 : outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
942 : sizeof(defaultBuffer), entry, &totalLen);
943 :
944 0 : if (!outBuffer)
945 0 : return -1;
946 :
947 : do {
948 0 : ret = write(fd, outBuffer, totalLen);
949 0 : } while (ret < 0 && errno == EINTR);
950 :
951 0 : if (ret < 0) {
952 0 : fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
953 0 : ret = 0;
954 0 : goto done;
955 : }
956 :
957 0 : if (((size_t)ret) < totalLen) {
958 0 : fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
959 : (int)totalLen);
960 0 : goto done;
961 : }
962 :
963 : done:
964 0 : if (outBuffer != defaultBuffer) {
965 0 : free(outBuffer);
966 : }
967 :
968 0 : return ret;
969 : }
970 :
971 :
972 :
973 0 : void logprint_run_tests()
974 : {
975 : #if 0
976 :
977 : fprintf(stderr, "tests disabled\n");
978 :
979 : #else
980 :
981 : int err;
982 : const char *tag;
983 : AndroidLogFormat *p_format;
984 :
985 0 : p_format = android_log_format_new();
986 :
987 0 : fprintf(stderr, "running tests\n");
988 :
989 0 : tag = "random";
990 :
991 0 : android_log_addFilterRule(p_format,"*:i");
992 :
993 0 : assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
994 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
995 0 : android_log_addFilterRule(p_format, "*");
996 0 : assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
997 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
998 0 : android_log_addFilterRule(p_format, "*:v");
999 0 : assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
1000 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
1001 0 : android_log_addFilterRule(p_format, "*:i");
1002 0 : assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
1003 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
1004 :
1005 0 : android_log_addFilterRule(p_format, "random");
1006 0 : assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
1007 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
1008 0 : android_log_addFilterRule(p_format, "random:v");
1009 0 : assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
1010 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
1011 0 : android_log_addFilterRule(p_format, "random:d");
1012 0 : assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
1013 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
1014 0 : android_log_addFilterRule(p_format, "random:w");
1015 0 : assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
1016 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
1017 :
1018 0 : android_log_addFilterRule(p_format, "crap:*");
1019 0 : assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap"));
1020 0 : assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0);
1021 :
1022 : // invalid expression
1023 0 : err = android_log_addFilterRule(p_format, "random:z");
1024 0 : assert (err < 0);
1025 0 : assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
1026 0 : assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
1027 :
1028 : // Issue #550946
1029 0 : err = android_log_addFilterString(p_format, " ");
1030 0 : assert(err == 0);
1031 0 : assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
1032 :
1033 : // note trailing space
1034 0 : err = android_log_addFilterString(p_format, "*:s random:d ");
1035 0 : assert(err == 0);
1036 0 : assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
1037 :
1038 0 : err = android_log_addFilterString(p_format, "*:s random:z");
1039 0 : assert(err < 0);
1040 :
1041 :
1042 : #if 0
1043 : char *ret;
1044 : char defaultBuffer[512];
1045 :
1046 : ret = android_log_formatLogLine(p_format,
1047 : defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123,
1048 : 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL);
1049 : #endif
1050 :
1051 :
1052 0 : fprintf(stderr, "tests complete\n");
1053 : #endif
1054 0 : }
1055 :
1056 : #undef UNINDENTED_BLOCK_START
1057 : #undef UNINDENTED_BLOCK_END
|