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