/* * Copyright (C) 2006-2017 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 #include #include #include #include #include #include #include #define DEFAULT_MAX_ROTATED_LOGS 4 using android::base::Join; using android::base::ParseByteCount; using android::base::ParseUint; using android::base::Split; using android::base::StringPrintf; using android::base::WriteFully; class Logcat { public: int Run(int argc, char** argv); private: void RotateLogs(); void ProcessBuffer(struct log_msg* buf); void PrintDividers(log_id_t log_id, bool print_dividers); void SetupOutputAndSchedulingPolicy(bool blocking); int SetLogFormat(const char* format_string); // Used for all options android::base::unique_fd output_fd_{dup(STDOUT_FILENO)}; std::unique_ptr logformat_{ android_log_format_new(), &android_log_format_free}; // For logging to a file and log rotation const char* output_file_name_ = nullptr; size_t log_rotate_size_kb_ = 0; // 0 means "no log rotation" size_t max_rotated_logs_ = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded" size_t out_byte_count_ = 0; // For binary log buffers int print_binary_ = 0; std::unique_ptr event_tag_map_{ nullptr, &android_closeEventTagMap}; bool has_opened_event_tag_map_ = false; // For the related --regex, --max-count, --print std::unique_ptr regex_; size_t max_count_ = 0; // 0 means "infinite" size_t print_count_ = 0; bool print_it_anyway_ = false; // For PrintDividers() bool print_dividers_ = false; log_id_t last_printed_id_ = LOG_ID_MAX; bool printed_start_[LOG_ID_MAX] = {}; bool debug_ = false; }; #ifndef F2FS_IOC_SET_PIN_FILE #define F2FS_IOCTL_MAGIC 0xf5 #define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32) #endif static int openLogFile(const char* pathname, size_t sizeKB) { int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP); if (fd < 0) { return fd; } // no need to check errors __u32 set = 1; ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set); fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, (sizeKB << 10)); return fd; } static void closeLogFile(const char* pathname) { int fd = open(pathname, O_WRONLY | O_CLOEXEC); if (fd == -1) { return; } // no need to check errors __u32 set = 0; ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set); close(fd); } void Logcat::RotateLogs() { // Can't rotate logs if we're not outputting to a file if (!output_file_name_) return; output_fd_.reset(); // Compute the maximum number of digits needed to count up to // maxRotatedLogs in decimal. eg: // maxRotatedLogs == 30 // -> log10(30) == 1.477 // -> maxRotationCountDigits == 2 int max_rotation_count_digits = max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0; for (int i = max_rotated_logs_; i > 0; i--) { std::string file1 = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i); std::string file0; if (!(i - 1)) { file0 = output_file_name_; } else { file0 = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i - 1); } if (!file0.length() || !file1.length()) { perror("while rotating log files"); break; } closeLogFile(file0.c_str()); int err = rename(file0.c_str(), file1.c_str()); if (err < 0 && errno != ENOENT) { perror("while rotating log files"); } } output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_)); if (!output_fd_.ok()) { error(EXIT_FAILURE, errno, "Couldn't open output file"); } out_byte_count_ = 0; } void Logcat::ProcessBuffer(struct log_msg* buf) { int bytesWritten = 0; int err; AndroidLogEntry entry; char binaryMsgBuf[1024]; bool is_binary = buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY; if (is_binary) { if (!event_tag_map_ && !has_opened_event_tag_map_) { event_tag_map_.reset(android_openEventTagMap(nullptr)); has_opened_event_tag_map_ = true; } err = android_log_processBinaryLogBuffer(&buf->entry, &entry, event_tag_map_.get(), binaryMsgBuf, sizeof(binaryMsgBuf)); // printf(">>> pri=%d len=%d msg='%s'\n", // entry.priority, entry.messageLen, entry.message); } else { err = android_log_processLogBuffer(&buf->entry, &entry); } if (err < 0 && !debug_) return; if (android_log_shouldPrintLine(logformat_.get(), std::string(entry.tag, entry.tagLen).c_str(), entry.priority)) { bool match = !regex_ || std::regex_search(entry.message, entry.message + entry.messageLen, *regex_); print_count_ += match; if (match || print_it_anyway_) { PrintDividers(buf->id(), print_dividers_); bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry); if (bytesWritten < 0) { error(EXIT_FAILURE, 0, "Output error."); } } } out_byte_count_ += bytesWritten; if (log_rotate_size_kb_ > 0 && (out_byte_count_ / 1024) >= log_rotate_size_kb_) { RotateLogs(); } } void Logcat::PrintDividers(log_id_t log_id, bool print_dividers) { if (log_id == last_printed_id_) { return; } if (!printed_start_[log_id] || print_dividers) { if (dprintf(output_fd_.get(), "--------- %s %s\n", printed_start_[log_id] ? "switch to" : "beginning of", android_log_id_to_name(log_id)) < 0) { error(EXIT_FAILURE, errno, "Output error"); } } last_printed_id_ = log_id; printed_start_[log_id] = true; } void Logcat::SetupOutputAndSchedulingPolicy(bool blocking) { if (!output_file_name_) 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 = {}; 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"); } } output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_)); if (!output_fd_.ok()) { error(EXIT_FAILURE, errno, "Couldn't open output file"); } struct stat statbuf; if (fstat(output_fd_.get(), &statbuf) == -1) { error(EXIT_FAILURE, errno, "Couldn't get output file stat"); } if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) { error(EXIT_FAILURE, 0, "Invalid output file stat."); } out_byte_count_ = statbuf.st_size; } // clang-format off static void show_help() { const char* cmd = getprogname(); fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd); fprintf(stderr, R"init( General options: -b, --buffer= Request alternate ring buffer(s): main system radio events crash default all Additionally, 'kernel' for userdebug and eng builds, and 'security' for Device Owner installations. Multiple -b parameters or comma separated list of buffers are allowed. Buffers are interleaved. Default -b main,system,crash,kernel. -L, --last Dump logs from prior to last reboot from pstore. -c, --clear Clear (flush) the entire log and exit. if -f is specified, clear the specified file and its related rotated log files instead. if -L is specified, clear pstore log instead. -d Dump the log and then exit (don't block). --pid= Only print logs from the given pid. --wrap Sleep for 2 hours or when buffer about to wrap whichever comes first. Improves efficiency of polling by providing an about-to-wrap wakeup. Formatting: -v, --format= Sets log print format verb and adverbs, where is one of: brief help long process raw tag thread threadtime time Modifying adverbs can be added: color descriptive epoch monotonic printable uid usec UTC year zone Multiple -v parameters or comma separated list of format and format modifiers are allowed. -D, --dividers Print dividers between each log buffer. -B, --binary Output the log in binary. Outfile files: -f, --file= Log to file instead of stdout. -r, --rotate-kbytes= Rotate log every kbytes. Requires -f option. -n, --rotate-count= Sets max number of rotated logs to , default 4. --id= If the signature for logging to file changes, then clear the associated files and continue. Logd control: These options send a control message to the logd daemon on device, print its return message if applicable, then exit. They are incompatible with -L, as these attributes do not apply to pstore. -g, --buffer-size Get the size of the ring buffers within logd. -G, --buffer-size= Set size of a ring buffer in logd. May suffix with K or M. This can individually control each buffer's size with -b. -S, --statistics Output statistics. --pid can be used to provide pid specific stats. -p, --prune Print prune rules. Each rule is specified as UID, UID/PID or /PID. A '~' prefix indicates that elements matching the rule should be pruned with higher priority otherwise they're pruned with lower priority. All other pruning activity is oldest first. Special case ~! represents an automatic pruning for the noisiest UID as determined by the current statistics. Special case ~1000/! represents pruning of the worst PID within AID_SYSTEM when AID_SYSTEM is the noisiest UID. -P, --prune=' ...' Set prune rules, using same format as listed above. Must be quoted. Filtering: -s Set default filter to silent. Equivalent to filterspec '*:S' -e, --regex= Only print lines where the log message matches where is an ECMAScript regular expression. -m, --max-count= Quit after printing lines. This is meant to be paired with --regex, but will work on its own. --print This option is only applicable when --regex is set and only useful if --max-count is also provided. With --print, logcat will print all messages even if they do not match the regex. Logcat will quit after printing the max-count number of lines that match the regex. -t Print only the most recent lines (implies -d). -t '