| /* |
| * |
| * Copyright (c) 2017 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * This document is the property of Nest. It is considered |
| * confidential and proprietary information. |
| * |
| * This document may not be reproduced or transmitted in any form, |
| * in whole or in part, without the express written permission of |
| * Nest. |
| * |
| */ |
| |
| #include "logcat_reader.h" |
| |
| #include <fcntl.h> |
| |
| #include <base/logging.h> |
| #include <base/files/file.h> |
| |
| #include <log/logger.h> |
| #include <log/logprint.h> |
| |
| using base::File; |
| |
| static const char *kDefaultLogFormat = "threadtime"; |
| |
| struct LogcatReader::LogcatImpl |
| { |
| AndroidLogFormat *log_format{nullptr}; |
| struct logger_list *logger_list{nullptr}; |
| struct logger *logger{nullptr}; |
| |
| ~LogcatImpl() |
| { |
| // TODO(fyhuang): do we not need android_logger_close? |
| /*if (logger) { |
| android_logger_close(logger); |
| logger = nullptr; |
| }*/ |
| |
| if (logger_list) { |
| android_logger_list_free(logger_list); |
| logger_list = nullptr; |
| } |
| |
| if (log_format) { |
| android_log_format_free(log_format); |
| log_format = nullptr; |
| } |
| } |
| }; |
| |
| LogcatReader::LogcatReader() |
| : impl(nullptr) |
| { |
| } |
| |
| bool LogcatReader::Initialize(const std::string &log_name, |
| unsigned int tail_num_lines) |
| { |
| impl = std::make_shared<LogcatImpl>(); |
| |
| impl->log_format = android_log_format_new(); |
| if (!impl->log_format) { |
| LOG(ERROR) << "android_log_format_new failed"; |
| return false; |
| } |
| |
| // Use the "standard" logcat format |
| AndroidLogPrintFormat standard_format = android_log_formatFromString(kDefaultLogFormat); |
| if (standard_format == FORMAT_OFF) { |
| LOG(WARNING) << "Couldn't set log format to " << kDefaultLogFormat; |
| } |
| else { |
| android_log_setPrintFormat(impl->log_format, standard_format); |
| } |
| |
| constexpr int kLoggerListMode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK; |
| impl->logger_list = android_logger_list_alloc(kLoggerListMode, tail_num_lines, (pid_t)0); |
| if (!impl->logger_list) { |
| LOG(ERROR) << "android_logger_list_alloc failed"; |
| return false; |
| } |
| |
| log_id_t log_id = android_name_to_log_id(log_name.c_str()); |
| // TODO(fyhuang): is it possible for the above call to fail? If so, what is |
| // the return value on error? |
| impl->logger = android_logger_open(impl->logger_list, log_id); |
| if (!impl->logger) { |
| LOG(ERROR) << "android_logger_open failed"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| size_t LogcatReader::DumpLogToFile(const base::FilePath &target_file_path) |
| { |
| size_t lines_read = 0; |
| size_t lines_with_errors = 0; |
| |
| base::File target_file(target_file_path, |
| File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE); |
| if (!target_file.IsValid()) { |
| LOG(ERROR) << "Couldn't open file for write: " << target_file_path.value(); |
| return 0; |
| } |
| |
| while (true) { |
| struct log_msg log_msg; |
| int err = android_logger_list_read(impl->logger_list, &log_msg); |
| if (err == -EAGAIN) { |
| // Signals that we've reached the end of the log (we're using |
| // NONBLOCK mode) |
| break; |
| } |
| else if (err < 0) { |
| LOG(WARNING) << "Error during android_logger_list_read: " << err; |
| break; // TODO(fyhuang): should we try to continue instead? |
| } |
| |
| AndroidLogEntry entry; |
| err = android_log_processLogBuffer(&log_msg.entry_v1, &entry); |
| if (err < 0) { |
| lines_with_errors++; |
| continue; |
| } |
| |
| // TODO(fyhuang): if we wanted to filter log messages by tag, this is |
| // where we would do it... |
| |
| err = android_log_printLogLine(impl->log_format, target_file.GetPlatformFile(), &entry); |
| if (err < 0) { |
| lines_with_errors++; |
| continue; |
| } |
| |
| lines_read++; |
| } |
| |
| // Log # of lines with errors at the end, so that we only produce one |
| // warning line no matter the number of loglines with errors |
| if (lines_with_errors > 0) { |
| LOG(WARNING) << lines_with_errors << " log lines could not be captured"; |
| } |
| |
| return lines_read; |
| } |