/* * Copyright (C) 2006-2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_MAX_ROTATED_LOGS 4 static AndroidLogFormat* g_logformat; // logd prefixes records with a length field #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) struct log_device_t { const char* device; bool binary; struct logger* logger; struct logger_list* logger_list; bool printed; log_device_t* next; log_device_t(const char* d, bool b) { device = d; binary = b; next = NULL; printed = false; logger = NULL; logger_list = NULL; } }; namespace android { // Global Variables static const char* g_outputFileName; // 0 means "no log rotation" static size_t g_logRotateSizeKBytes; // 0 means "unbounded" static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; static int g_outFD = -1; static size_t g_outByteCount; static int g_printBinary; static int g_devCount; // >1 means multiple static pcrecpp::RE* g_regex; // 0 means "infinite" static size_t g_maxCount; static size_t g_printCount; static bool g_printItAnyways; static bool g_debug; enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT }; // if showHelp is set, newline required in fmt statement to transition to usage __noreturn static void logcat_panic(enum helpType showHelp, const char* fmt, ...) __printflike(2, 3); static int openLogFile(const char* pathname) { return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); } static void rotateLogs() { int err; // Can't rotate logs if we're not outputting to a file if (g_outputFileName == NULL) { return; } close(g_outFD); // Compute the maximum number of digits needed to count up to // g_maxRotatedLogs in decimal. eg: // g_maxRotatedLogs == 30 // -> log10(30) == 1.477 // -> maxRotationCountDigits == 2 int maxRotationCountDigits = (g_maxRotatedLogs > 0) ? (int)(floor(log10(g_maxRotatedLogs) + 1)) : 0; for (int i = g_maxRotatedLogs; i > 0; i--) { std::string file1 = android::base::StringPrintf( "%s.%.*d", g_outputFileName, maxRotationCountDigits, i); std::string file0; if (i - 1 == 0) { file0 = android::base::StringPrintf("%s", g_outputFileName); } else { file0 = android::base::StringPrintf("%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1); } if ((file0.length() == 0) || (file1.length() == 0)) { perror("while rotating log files"); break; } err = rename(file0.c_str(), file1.c_str()); if (err < 0 && errno != ENOENT) { perror("while rotating log files"); } } g_outFD = openLogFile(g_outputFileName); if (g_outFD < 0) { logcat_panic(HELP_FALSE, "couldn't open output file"); } g_outByteCount = 0; } void printBinary(struct log_msg* buf) { size_t size = buf->len(); TEMP_FAILURE_RETRY(write(g_outFD, buf, size)); } static bool regexOk(const AndroidLogEntry& entry) { if (!g_regex) { return true; } std::string messageString(entry.message, entry.messageLen); return g_regex->PartialMatch(messageString); } static void processBuffer(log_device_t* dev, struct log_msg* buf) { int bytesWritten = 0; int err; AndroidLogEntry entry; char binaryMsgBuf[1024]; if (dev->binary) { static bool hasOpenedEventTagMap = false; static EventTagMap* eventTagMap = NULL; if (!eventTagMap && !hasOpenedEventTagMap) { eventTagMap = android_openEventTagMap(NULL); hasOpenedEventTagMap = true; } err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry, eventTagMap, binaryMsgBuf, sizeof(binaryMsgBuf)); // printf(">>> pri=%d len=%d msg='%s'\n", // entry.priority, entry.messageLen, entry.message); } else { err = android_log_processLogBuffer(&buf->entry_v1, &entry); } if ((err < 0) && !g_debug) { goto error; } if (android_log_shouldPrintLine( g_logformat, std::string(entry.tag, entry.tagLen).c_str(), entry.priority)) { bool match = regexOk(entry); g_printCount += match; if (match || g_printItAnyways) { bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry); if (bytesWritten < 0) { logcat_panic(HELP_FALSE, "output error"); } } } g_outByteCount += bytesWritten; if (g_logRotateSizeKBytes > 0 && (g_outByteCount / 1024) >= g_logRotateSizeKBytes) { rotateLogs(); } error: return; } static void maybePrintStart(log_device_t* dev, bool printDividers) { if (!dev->printed || printDividers) { if (g_devCount > 1 && !g_printBinary) { char buf[1024]; snprintf(buf, sizeof(buf), "--------- %s %s\n", dev->printed ? "switch to" : "beginning of", dev->device); if (write(g_outFD, buf, strlen(buf)) < 0) { logcat_panic(HELP_FALSE, "output error"); } } dev->printed = true; } } static void setupOutputAndSchedulingPolicy(bool blocking) { if (g_outputFileName == NULL) { g_outFD = STDOUT_FILENO; return; } if (blocking) { // Lower priority and set to batch scheduling if we are saving // the logs into files and taking continuous content. if (set_sched_policy(0, SP_BACKGROUND) < 0) { fprintf(stderr, "failed to set background scheduling policy\n"); } struct sched_param param; memset(¶m, 0, sizeof(param)); if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) { fprintf(stderr, "failed to set to batch scheduler\n"); } if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { fprintf(stderr, "failed set to priority\n"); } } g_outFD = openLogFile(g_outputFileName); if (g_outFD < 0) { logcat_panic(HELP_FALSE, "couldn't open output file"); } struct stat statbuf; if (fstat(g_outFD, &statbuf) == -1) { close(g_outFD); logcat_panic(HELP_FALSE, "couldn't get output file stat\n"); } if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) { close(g_outFD); logcat_panic(HELP_FALSE, "invalid output file stat\n"); } g_outByteCount = statbuf.st_size; } // clang-format off static void show_help(const char* cmd) { fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd); fprintf(stderr, "options include:\n" " -s Set default filter to silent. Equivalent to filterspec '*:S'\n" " -f , --file= Log to file. Default is stdout\n" " -r , --rotate-kbytes=\n" " Rotate log every kbytes. Requires -f option\n" " -n , --rotate-count=\n" " Sets max number of rotated logs to , default 4\n" " --id= If the signature id for logging to file changes, then clear\n" " the fileset and continue\n" " -v , --format=\n" " Sets log print format verb and adverbs, where is:\n" " brief help long process raw tag thread threadtime time\n" " and individually flagged modifying adverbs can be added:\n" " color descriptive epoch monotonic printable uid\n" " usec UTC year zone\n" // private and undocumented nsec, no signal, too much noise // useful for -T or -t accurate testing though. " -D, --dividers Print dividers between each log buffer\n" " -c, --clear Clear (flush) the entire log and exit\n" " if Log to File specified, clear fileset instead\n" " -d Dump the log and then exit (don't block)\n" " -e , --regex=\n" " Only print lines where the log message matches \n" " where is a regular expression\n" // Leave --head undocumented as alias for -m " -m , --max-count=\n" " Quit after printing lines. This is meant to be\n" " paired with --regex, but will work on its own.\n" " --print Paired with --regex and --max-count to let content bypass\n" " regex filter but still stop at number of matches.\n" // Leave --tail undocumented as alias for -t " -t Print only the most recent lines (implies -d)\n" " -t '