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 |