crash-reporter: Attach Chrome logs to crash reports.

When Chrome crashes, gather the last 20 lines of each of the
latest two log files in /var/log/chrome and
/home/chronos/user/log and include them in the archive file
that's attached to crash reports.

BUG=chromium:405732
TEST=triggered crashes at the login screen and after logging
     in and verified that logs were included in crash
     reports: bdf766f0d6d1e066, a9d410e1a86f996b

Change-Id: I09e3cee23af108dc216d64aae85d78751d5649d4
Reviewed-on: https://chromium-review.googlesource.com/216427
Reviewed-by: Ben Chan <benchan@chromium.org>
Tested-by: Daniel Erat <derat@chromium.org>
Commit-Queue: Daniel Erat <derat@chromium.org>
diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc
index 438ecbc..21cc852 100644
--- a/crash_reporter/chrome_collector.cc
+++ b/crash_reporter/chrome_collector.cc
@@ -42,8 +42,25 @@
   return true;
 }
 
-bool GetDriErrorState(const chromeos::dbus::Proxy &proxy,
-                      const FilePath &error_state_path) {
+// Gets the GPU's error state from debugd and writes it to |error_state_path|.
+// Returns true on success.
+bool GetDriErrorState(const FilePath &error_state_path) {
+  chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection();
+  if (!dbus.HasConnection()) {
+    LOG(ERROR) << "Error connecting to system D-Bus";
+    return false;
+  }
+
+  chromeos::dbus::Proxy proxy(dbus,
+                              debugd::kDebugdServiceName,
+                              debugd::kDebugdServicePath,
+                              debugd::kDebugdInterface);
+  if (!proxy) {
+    LOG(ERROR) << "Error creating D-Bus proxy to interface "
+               << "'" << debugd::kDebugdServiceName << "'";
+    return false;
+  }
+
   chromeos::glib::ScopedError error;
   gchar *error_state = NULL;
   if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kGetLog,
@@ -88,46 +105,6 @@
   return true;
 }
 
-bool GetAdditionalLogs(const FilePath &log_path) {
-  chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection();
-  if (!dbus.HasConnection()) {
-    LOG(ERROR) << "Error connecting to system D-Bus";
-    return false;
-  }
-
-  chromeos::dbus::Proxy proxy(dbus,
-                              debugd::kDebugdServiceName,
-                              debugd::kDebugdServicePath,
-                              debugd::kDebugdInterface);
-  if (!proxy) {
-    LOG(ERROR) << "Error creating D-Bus proxy to interface "
-               << "'" << debugd::kDebugdServiceName << "'";
-    return false;
-  }
-
-  FilePath error_state_path =
-      log_path.DirName().Append("i915_error_state.log.xz");
-  if (!GetDriErrorState(proxy, error_state_path))
-    return false;
-
-  chromeos::ProcessImpl tar_process;
-  tar_process.AddArg(kTarPath);
-  tar_process.AddArg("cfJ");
-  tar_process.AddArg(log_path.value());
-  tar_process.AddStringOption("-C", log_path.DirName().value());
-  tar_process.AddArg(error_state_path.BaseName().value());
-  int res = tar_process.Run();
-
-  base::DeleteFile(error_state_path, false);
-
-  if (res || !base::PathExists(log_path)) {
-    LOG(ERROR) << "Could not tar file " << log_path.value();
-    return false;
-  }
-
-  return true;
-}
-
 }  // namespace
 
 
@@ -158,7 +135,7 @@
   std::string dump_basename = FormatDumpBasename(exe_name, time(NULL), pid);
   FilePath meta_path = GetCrashPath(dir, dump_basename, "meta");
   FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp");
-  FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz");
+  FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.gz");
 
   std::string data;
   if (!base::ReadFileToString(file_path, &data)) {
@@ -171,7 +148,7 @@
     return false;
   }
 
-  if (GetAdditionalLogs(log_path)) {
+  if (GetAdditionalLogs(log_path, exe_name)) {
     int64_t minidump_size = 0;
     int64_t log_size = 0;
     if (base::GetFileSize(minidump_path, &minidump_size) &&
@@ -295,5 +272,44 @@
   return at == data.size();
 }
 
+bool ChromeCollector::GetAdditionalLogs(const FilePath &log_path,
+                                        const std::string &exe_name) {
+  std::vector<base::FilePath> logs_to_compress;
+
+  // Run the command specified by the config file to gather logs.
+  const FilePath gathered_logs_path =
+      log_path.DirName().Append("gathered_logs.txt");
+  if (GetLogContents(log_config_path_, exe_name, gathered_logs_path))
+    logs_to_compress.push_back(gathered_logs_path);
+
+  // Now get the GPU state from debugd.
+  const FilePath dri_error_state_path =
+      log_path.DirName().Append("i915_error_state.log.xz");
+  if (GetDriErrorState(dri_error_state_path))
+    logs_to_compress.push_back(dri_error_state_path);
+
+  if (logs_to_compress.empty())
+    return false;
+
+  chromeos::ProcessImpl tar_process;
+  tar_process.AddArg(kTarPath);
+  tar_process.AddArg("cfz");
+  tar_process.AddArg(log_path.value());
+  tar_process.AddStringOption("-C", log_path.DirName().value());
+  for (size_t i = 0; i < logs_to_compress.size(); ++i)
+    tar_process.AddArg(logs_to_compress[i].BaseName().value());
+
+  int res = tar_process.Run();
+
+  for (size_t i = 0; i < logs_to_compress.size(); ++i)
+    base::DeleteFile(logs_to_compress[i], false);
+
+  if (res || !base::PathExists(log_path)) {
+    LOG(ERROR) << "Could not create tar archive " << log_path.value();
+    return false;
+  }
+  return true;
+}
+
 // static
 const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished";
diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h
index d8602b9..67b5aab 100644
--- a/crash_reporter/chrome_collector.h
+++ b/crash_reporter/chrome_collector.h
@@ -48,6 +48,12 @@
                      const base::FilePath &minidump,
                      const std::string &basename);
 
+  // Gathers additional logs for |exe_name| and compresses them into a .tar.gz
+  // archive at |log_path|. Returns true if the log archive was created
+  // successfully.
+  bool GetAdditionalLogs(const base::FilePath &log_path,
+                         const std::string &exe_name);
+
   FILE *output_file_ptr_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeCollector);
diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf
index e4ad207..db0c116 100644
--- a/crash_reporter/crash_reporter_logs.conf
+++ b/crash_reporter/crash_reporter_logs.conf
@@ -20,6 +20,10 @@
 # so it is handled in the same way as update_engine.
 cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000
 
+# Dump the last 20 lines of the last two files in Chrome's system and user log
+# directories.
+chrome:for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* | tail -2); do echo "===$f (tail)==="; tail -20 $f; echo EOF; done
+
 # The following rule is used for generating additional diagnostics when
 # collection of user crashes fails.  This output should not be too large
 # as it is stored in memory.  The output format specified for 'ps' is the