| Index: chrome/browser/chromeos/system_logs/debug_log_writer.cc
|
| diff --git a/chrome/browser/chromeos/system_logs/debug_log_writer.cc b/chrome/browser/chromeos/system_logs/debug_log_writer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6c13259a088337a46e56fb489c862d9109b7e597
|
| --- /dev/null
|
| +++ b/chrome/browser/chromeos/system_logs/debug_log_writer.cc
|
| @@ -0,0 +1,238 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/callback.h"
|
| +#include "base/command_line.h"
|
| +#include "base/file_util.h"
|
| +#include "base/files/file.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/process/kill.h"
|
| +#include "base/process/launch.h"
|
| +#include "chrome/common/logging_chrome.h"
|
| +#include "chromeos/dbus/dbus_thread_manager.h"
|
| +#include "chromeos/dbus/debug_daemon_client.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +
|
| +namespace chromeos {
|
| +
|
| +namespace {
|
| +
|
| +// Callback for returning status of executed external command.
|
| +typedef base::Callback<void(bool succeeded)> CommandCompletionCallback;
|
| +
|
| +const char kGzipCommand[] = "/bin/gzip";
|
| +const char kTarCommand[] = "/bin/tar";
|
| +
|
| +// Called upon completion of |WriteDebugLogToFile|. Closes file
|
| +// descriptor, deletes log file in the case of failure and calls
|
| +// |callback|.
|
| +void WriteDebugLogToFileCompleted(
|
| + const DebugLogWriter::StoreLogsCallback& callback,
|
| + const base::FilePath& file_path,
|
| + bool succeeded) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + if (!succeeded) {
|
| + bool posted = content::BrowserThread::PostBlockingPoolTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(base::IgnoreResult(&base::DeleteFile), file_path, false),
|
| + base::Bind(callback, file_path, false));
|
| + DCHECK(posted);
|
| + return;
|
| + }
|
| + callback.Run(file_path, true);
|
| +}
|
| +
|
| +// Stores into |file_path| debug logs in the .tgz format. Calls
|
| +// |callback| upon completion.
|
| +void WriteDebugLogToFile(
|
| + base::File* file,
|
| + const base::FilePath& file_path,
|
| + bool should_compress,
|
| + const DebugLogWriter::StoreLogsCallback& callback) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + if (!file->IsValid()) {
|
| + LOG(ERROR) <<
|
| + "Can't create debug log file: " << file_path.AsUTF8Unsafe() << ", " <<
|
| + "error: " << file->error_details();
|
| + return;
|
| + }
|
| + chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->DumpDebugLogs(
|
| + should_compress,
|
| + file->Pass(),
|
| + base::Bind(&WriteDebugLogToFileCompleted, callback, file_path));
|
| +}
|
| +
|
| +// Runs command with its parameters as defined in |argv|.
|
| +// Upon completion, it will report command run outcome via |callback| on the
|
| +// same thread from where it was initially called from.
|
| +void RunCommand(const std::vector<std::string>& argv,
|
| + const CommandCompletionCallback& callback) {
|
| + base::ProcessHandle handle = base::kNullProcessHandle;
|
| + if (!base::LaunchProcess(argv, base::LaunchOptions(), &handle)) {
|
| + LOG(ERROR) << "Failed to execute command " << argv[0];
|
| + callback.Run(false);
|
| + return;
|
| + }
|
| +
|
| + int exit_code = 0;
|
| + if (!base::WaitForExitCode(handle, &exit_code)) {
|
| + LOG(ERROR) << "Can't get exit code for pid " << handle;
|
| + callback.Run(false);
|
| + return;
|
| + }
|
| + callback.Run(exit_code == 0);
|
| +}
|
| +
|
| +// Callback for handling the outcome of CompressArchive(). It reports
|
| +// the final outcome of log retreival process at via |callback|.
|
| +void OnCompressArchiveCompleted(
|
| + const base::FilePath& tar_file_path,
|
| + const base::FilePath& compressed_output_path,
|
| + const DebugLogWriter::StoreLogsCallback& callback,
|
| + bool compression_command_success) {
|
| + if (!compression_command_success) {
|
| + LOG(ERROR) << "Failed compressing " << compressed_output_path.value();
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(callback, base::FilePath(), false));
|
| + base::DeleteFile(tar_file_path, true);
|
| + base::DeleteFile(compressed_output_path, true);
|
| + return;
|
| + }
|
| +
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(callback, compressed_output_path, true));
|
| +}
|
| +
|
| +// Gzips |tar_file_path| and stores results in |compressed_output_path|.
|
| +void CompressArchive(const base::FilePath& tar_file_path,
|
| + const base::FilePath& compressed_output_path,
|
| + const DebugLogWriter::StoreLogsCallback& callback,
|
| + bool add_user_logs_command_success) {
|
| + if (!add_user_logs_command_success) {
|
| + LOG(ERROR) << "Failed adding user logs to " << tar_file_path.value();
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(callback, base::FilePath(), false));
|
| + base::DeleteFile(tar_file_path, true);
|
| + return;
|
| + }
|
| +
|
| + std::vector<std::string> argv;
|
| + argv.push_back(kGzipCommand);
|
| + argv.push_back(tar_file_path.value());
|
| + RunCommand(argv,
|
| + base::Bind(&OnCompressArchiveCompleted,
|
| + tar_file_path,
|
| + compressed_output_path,
|
| + callback));
|
| +}
|
| +
|
| +// Adds user sessions specific logs from |user_log_dir| into tar archive file
|
| +// at |tar_file_path|. Upon completion, it will call CompressArchive() to
|
| +// produce |compressed_output_path|.
|
| +void AddUserLogsToArchive(const base::FilePath& user_log_dir,
|
| + const base::FilePath& tar_file_path,
|
| + const base::FilePath& compressed_output_path,
|
| + const DebugLogWriter::StoreLogsCallback& callback) {
|
| + std::vector<std::string> argv;
|
| + argv.push_back(kTarCommand);
|
| + argv.push_back("-rvf");
|
| + argv.push_back(tar_file_path.value());
|
| + argv.push_back(user_log_dir.value());
|
| + RunCommand(argv,
|
| + base::Bind(&CompressArchive,
|
| + tar_file_path,
|
| + compressed_output_path,
|
| + callback));
|
| +}
|
| +
|
| +// Appends user logs after system logs are archived into |tar_file_path|.
|
| +void OnSystemLogsAdded(
|
| + const DebugLogWriter::StoreLogsCallback& callback,
|
| + const base::FilePath& tar_file_path,
|
| + bool succeeded) {
|
| + if (!succeeded) {
|
| + callback.Run(base::FilePath(), false);
|
| + return;
|
| + }
|
| +
|
| + base::FilePath compressed_output_path =
|
| + tar_file_path.AddExtension(FILE_PATH_LITERAL(".gz"));
|
| + base::FilePath user_log_dir =
|
| + logging::GetSessionLogDir(*CommandLine::ForCurrentProcess());
|
| +
|
| + content::BrowserThread::PostBlockingPoolTask(
|
| + FROM_HERE,
|
| + base::Bind(&AddUserLogsToArchive,
|
| + user_log_dir,
|
| + tar_file_path,
|
| + compressed_output_path,
|
| + callback));
|
| +}
|
| +
|
| +// Starts logs retrieval process. The output will be stored in file with name
|
| +// derived from |file_name_template|.
|
| +void StartLogRetrieval(const base::FilePath& file_name_template,
|
| + bool should_compress,
|
| + const DebugLogWriter::StoreLogsCallback& callback) {
|
| + base::FilePath file_path =
|
| + logging::GenerateTimestampedName(file_name_template, base::Time::Now());
|
| +
|
| + int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
|
| + base::File* file = new base::File;
|
| + content::BrowserThread::PostBlockingPoolTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&base::File::Initialize,
|
| + base::Unretained(file), file_path, flags),
|
| + base::Bind(&WriteDebugLogToFile,
|
| + base::Owned(file),
|
| + file_path,
|
| + should_compress,
|
| + callback));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// static.
|
| +void DebugLogWriter::StoreLogs(
|
| + const base::FilePath& fileshelf,
|
| + bool should_compress,
|
| + const StoreLogsCallback& callback) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + DCHECK(!callback.is_null());
|
| +
|
| + base::FilePath file_path = fileshelf.Append(
|
| + should_compress ? FILE_PATH_LITERAL("debug-logs.tgz") :
|
| + FILE_PATH_LITERAL("debug-logs.tar"));
|
| +
|
| + StartLogRetrieval(file_path, should_compress, callback);
|
| +}
|
| +
|
| +// static.
|
| +void DebugLogWriter::StoreCombinedLogs(
|
| + const base::FilePath& fileshelf,
|
| + const StoreLogsCallback& callback) {
|
| +
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + DCHECK(!callback.is_null());
|
| +
|
| + base::FilePath file_path =
|
| + fileshelf.Append(FILE_PATH_LITERAL("combined-logs.tar"));
|
| +
|
| + // Get system logs from /var/log first, then add user-specific stuff.
|
| + StartLogRetrieval(file_path,
|
| + false,
|
| + base::Bind(&OnSystemLogsAdded, callback));
|
| +}
|
| +
|
| +} // namespace chromeos
|
|
|