Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / system_logs / debug_log_writer.cc
blob1c2b0896485372fccddf3ac2265725ab24025bcb
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/files/file.h"
12 #include "base/files/file_util.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/process/kill.h"
15 #include "base/process/launch.h"
16 #include "chrome/common/logging_chrome.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "chromeos/dbus/debug_daemon_client.h"
19 #include "content/public/browser/browser_thread.h"
21 namespace chromeos {
23 namespace {
25 // Callback for returning status of executed external command.
26 typedef base::Callback<void(bool succeeded)> CommandCompletionCallback;
28 const char kGzipCommand[] = "/bin/gzip";
29 const char kTarCommand[] = "/bin/tar";
31 scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner(
32 const std::string sequence_name) {
33 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
34 return pool->GetSequencedTaskRunnerWithShutdownBehavior(
35 pool->GetNamedSequenceToken(sequence_name),
36 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
39 // Called upon completion of |WriteDebugLogToFile|. Closes file
40 // descriptor, deletes log file in the case of failure and calls
41 // |callback|.
42 void WriteDebugLogToFileCompleted(
43 const base::FilePath& file_path,
44 const std::string& sequence_token_name,
45 const DebugLogWriter::StoreLogsCallback& callback,
46 bool succeeded) {
47 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
48 if (!succeeded) {
49 bool posted = GetSequencedTaskRunner(sequence_token_name)->PostTaskAndReply(
50 FROM_HERE,
51 base::Bind(base::IgnoreResult(&base::DeleteFile), file_path, false),
52 base::Bind(callback, file_path, false));
53 DCHECK(posted);
54 return;
56 if (!callback.is_null())
57 callback.Run(file_path, true);
60 // Stores into |file_path| debug logs in the .tgz format. Calls
61 // |callback| upon completion.
62 void WriteDebugLogToFile(base::File* file,
63 const std::string& sequence_token_name,
64 const base::FilePath& file_path,
65 bool should_compress,
66 const DebugLogWriter::StoreLogsCallback& callback) {
67 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
68 if (!file->IsValid()) {
69 LOG(ERROR) << "Can't create debug log file: " << file_path.AsUTF8Unsafe()
70 << ", "
71 << "error: " << file->error_details();
72 return;
74 scoped_refptr<base::TaskRunner> task_runner =
75 GetSequencedTaskRunner(sequence_token_name);
76 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->DumpDebugLogs(
77 should_compress,
78 file->Pass(),
79 task_runner,
80 base::Bind(&WriteDebugLogToFileCompleted,
81 file_path,
82 sequence_token_name,
83 callback));
86 // Runs command with its parameters as defined in |argv|.
87 // Upon completion, it will report command run outcome via |callback| on the
88 // same thread from where it was initially called from.
89 void RunCommand(const std::vector<std::string>& argv,
90 const CommandCompletionCallback& callback) {
91 base::Process process = base::LaunchProcess(argv, base::LaunchOptions());
92 if (!process.IsValid()) {
93 LOG(ERROR) << "Failed to execute command " << argv[0];
94 if (!callback.is_null())
95 callback.Run(false);
97 return;
100 int exit_code = 0;
101 if (!process.WaitForExit(&exit_code)) {
102 LOG(ERROR) << "Can't get exit code for pid " << process.Pid();
103 if (!callback.is_null())
104 callback.Run(false);
106 return;
108 if (!callback.is_null())
109 callback.Run(exit_code == 0);
112 // Callback for handling the outcome of CompressArchive(). It reports
113 // the final outcome of log retreival process at via |callback|.
114 void OnCompressArchiveCompleted(
115 const base::FilePath& tar_file_path,
116 const base::FilePath& compressed_output_path,
117 const DebugLogWriter::StoreLogsCallback& callback,
118 bool compression_command_success) {
119 if (!compression_command_success) {
120 LOG(ERROR) << "Failed compressing " << compressed_output_path.value();
121 content::BrowserThread::PostTask(
122 content::BrowserThread::UI,
123 FROM_HERE,
124 base::Bind(callback, base::FilePath(), false));
125 base::DeleteFile(tar_file_path, true);
126 base::DeleteFile(compressed_output_path, true);
127 return;
130 content::BrowserThread::PostTask(
131 content::BrowserThread::UI,
132 FROM_HERE,
133 base::Bind(callback, compressed_output_path, true));
136 // Gzips |tar_file_path| and stores results in |compressed_output_path|.
137 void CompressArchive(const base::FilePath& tar_file_path,
138 const base::FilePath& compressed_output_path,
139 const DebugLogWriter::StoreLogsCallback& callback,
140 bool add_user_logs_command_success) {
141 if (!add_user_logs_command_success) {
142 LOG(ERROR) << "Failed adding user logs to " << tar_file_path.value();
143 content::BrowserThread::PostTask(
144 content::BrowserThread::UI,
145 FROM_HERE,
146 base::Bind(callback, base::FilePath(), false));
147 base::DeleteFile(tar_file_path, true);
148 return;
151 std::vector<std::string> argv;
152 argv.push_back(kGzipCommand);
153 argv.push_back(tar_file_path.value());
154 RunCommand(argv,
155 base::Bind(&OnCompressArchiveCompleted,
156 tar_file_path,
157 compressed_output_path,
158 callback));
161 // Adds user sessions specific logs from |user_log_dir| into tar archive file
162 // at |tar_file_path|. Upon completion, it will call CompressArchive() to
163 // produce |compressed_output_path|.
164 void AddUserLogsToArchive(const base::FilePath& user_log_dir,
165 const base::FilePath& tar_file_path,
166 const base::FilePath& compressed_output_path,
167 const DebugLogWriter::StoreLogsCallback& callback) {
168 std::vector<std::string> argv;
169 argv.push_back(kTarCommand);
170 argv.push_back("-rvf");
171 argv.push_back(tar_file_path.value());
172 argv.push_back(user_log_dir.value());
173 RunCommand(
174 argv,
175 base::Bind(
176 &CompressArchive, tar_file_path, compressed_output_path, callback));
179 // Appends user logs after system logs are archived into |tar_file_path|.
180 void OnSystemLogsAdded(const DebugLogWriter::StoreLogsCallback& callback,
181 const base::FilePath& tar_file_path,
182 bool succeeded) {
183 if (!succeeded) {
184 if (!callback.is_null())
185 callback.Run(base::FilePath(), false);
187 return;
190 base::FilePath compressed_output_path =
191 tar_file_path.AddExtension(FILE_PATH_LITERAL(".gz"));
192 base::FilePath user_log_dir =
193 logging::GetSessionLogDir(*base::CommandLine::ForCurrentProcess());
195 content::BrowserThread::PostBlockingPoolTask(
196 FROM_HERE,
197 base::Bind(&AddUserLogsToArchive,
198 user_log_dir,
199 tar_file_path,
200 compressed_output_path,
201 callback));
204 void IntializeLogFile(base::File* file,
205 const base::FilePath& file_path,
206 uint32 flags) {
207 base::FilePath dir = file_path.DirName();
208 if (!base::DirectoryExists(dir)) {
209 if (!base::CreateDirectory(dir)) {
210 LOG(ERROR) << "Can not create " << dir.value();
211 return;
215 file->Initialize(file_path, flags);
218 // Starts logs retrieval process. The output will be stored in file with name
219 // derived from |file_name_template|.
220 void StartLogRetrieval(const base::FilePath& file_name_template,
221 bool should_compress,
222 const std::string& sequence_token_name,
223 const DebugLogWriter::StoreLogsCallback& callback) {
224 base::FilePath file_path =
225 logging::GenerateTimestampedName(file_name_template, base::Time::Now());
227 int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
228 base::File* file = new base::File;
229 GetSequencedTaskRunner(sequence_token_name)->PostTaskAndReply(
230 FROM_HERE,
231 base::Bind(&IntializeLogFile, base::Unretained(file), file_path, flags),
232 base::Bind(&WriteDebugLogToFile,
233 base::Owned(file),
234 sequence_token_name,
235 file_path,
236 should_compress,
237 callback));
240 const char kDefaultSequenceName[] = "DebugLogWriter";
242 } // namespace
244 // static.
245 void DebugLogWriter::StoreLogs(const base::FilePath& fileshelf,
246 bool should_compress,
247 const StoreLogsCallback& callback) {
248 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
249 DCHECK(!callback.is_null());
251 base::FilePath file_path =
252 fileshelf.Append(should_compress ? FILE_PATH_LITERAL("debug-logs.tgz")
253 : FILE_PATH_LITERAL("debug-logs.tar"));
255 StartLogRetrieval(file_path, should_compress, kDefaultSequenceName, callback);
258 // static.
259 void DebugLogWriter::StoreCombinedLogs(const base::FilePath& fileshelf,
260 const std::string& sequence_token_name,
261 const StoreLogsCallback& callback) {
262 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
263 DCHECK(!callback.is_null());
265 base::FilePath file_path =
266 fileshelf.Append(FILE_PATH_LITERAL("combined-logs.tar"));
268 // Get system logs from /var/log first, then add user-specific stuff.
269 StartLogRetrieval(file_path,
270 false,
271 sequence_token_name,
272 base::Bind(&OnSystemLogsAdded, callback));
275 } // namespace chromeos