blob: f9af3312dee5bf9de50cf26bd70d6b6e72ddec96 [file] [log] [blame]
/*
*
* 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;
}