Write "logout-started" event on next boot.
Writing logout-started on process exit is unstable.
So moving it to next boot.
BUG=352130
TEST=manual
Review URL: https://codereview.chromium.org/303233004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274649 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/chromeos/boot_times_loader.cc b/chrome/browser/chromeos/boot_times_loader.cc
index 94a675d..db542f0 100644
--- a/chrome/browser/chromeos/boot_times_loader.cc
+++ b/chrome/browser/chromeos/boot_times_loader.cc
@@ -10,11 +10,14 @@
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
+#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
@@ -29,6 +32,7 @@
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_service.h"
@@ -43,6 +47,9 @@
namespace {
+const char kUptime[] = "uptime";
+const char kDisk[] = "disk";
+
RenderWidgetHost* GetRenderWidgetHost(NavigationController* tab) {
WebContents* web_contents = tab->GetWebContents();
if (web_contents) {
@@ -70,6 +77,19 @@
return std::string();
}
+// Appends the given buffer into the file. Returns the number of bytes
+// written, or -1 on error.
+// TODO(satorux): Move this to file_util.
+int AppendFile(const base::FilePath& file_path, const char* data, int size) {
+ FILE* file = base::OpenFile(file_path, "a");
+ if (!file)
+ return -1;
+
+ const int num_bytes_written = fwrite(data, 1, size, file);
+ base::CloseFile(file);
+ return num_bytes_written;
+}
+
} // namespace
namespace chromeos {
@@ -112,6 +132,109 @@
static base::LazyInstance<BootTimesLoader> g_boot_times_loader =
LAZY_INSTANCE_INITIALIZER;
+// static
+BootTimesLoader::Stats BootTimesLoader::Stats::GetCurrentStats() {
+ const base::FilePath kProcUptime(FPL("/proc/uptime"));
+ const base::FilePath kDiskStat(FPL("/sys/block/sda/stat"));
+ Stats stats;
+ // Callers of this method expect synchronous behavior.
+ // It's safe to allow IO here, because only virtual FS are accessed.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ base::ReadFileToString(kProcUptime, &stats.uptime_);
+ base::ReadFileToString(kDiskStat, &stats.disk_);
+ return stats;
+}
+
+std::string BootTimesLoader::Stats::SerializeToString() const {
+ if (uptime_.empty() || disk_.empty())
+ return std::string();
+ base::DictionaryValue dictionary;
+ dictionary.SetString(kUptime, uptime_);
+ dictionary.SetString(kDisk, disk_);
+
+ std::string result;
+ if (!base::JSONWriter::Write(&dictionary, &result)) {
+ LOG(WARNING) << "BootTimesLoader::Stats::SerializeToString(): failed.";
+ return std::string();
+ }
+
+ return result;
+}
+
+// static
+BootTimesLoader::Stats BootTimesLoader::Stats::DeserializeFromString(
+ const std::string& source) {
+ if (source.empty())
+ return Stats();
+
+ scoped_ptr<base::Value> value(base::JSONReader::Read(source));
+ base::DictionaryValue* dictionary;
+ if (!value || !value->GetAsDictionary(&dictionary)) {
+ LOG(ERROR) << "BootTimesLoader::Stats::DeserializeFromString(): not a "
+ "dictionary: '" << source << "'";
+ return Stats();
+ }
+
+ Stats result;
+ if (!dictionary->GetString(kUptime, &result.uptime_) ||
+ !dictionary->GetString(kDisk, &result.disk_)) {
+ LOG(ERROR)
+ << "BootTimesLoader::Stats::DeserializeFromString(): format error: '"
+ << source << "'";
+ return Stats();
+ }
+
+ return result;
+}
+
+bool BootTimesLoader::Stats::UptimeDouble(double* result) const {
+ std::string uptime = uptime_;
+ const size_t space_at = uptime.find_first_of(' ');
+ if (space_at == std::string::npos)
+ return false;
+
+ uptime.resize(space_at);
+
+ if (base::StringToDouble(uptime, result))
+ return true;
+
+ return false;
+}
+
+void BootTimesLoader::Stats::RecordStats(const std::string& name) const {
+ BrowserThread::PostBlockingPoolTask(
+ FROM_HERE,
+ base::Bind(&BootTimesLoader::Stats::RecordStatsImpl,
+ base::Owned(new Stats(*this)),
+ name));
+}
+
+void BootTimesLoader::Stats::RecordStatsWithCallback(
+ const std::string& name,
+ const base::Closure& callback) const {
+ BrowserThread::PostBlockingPoolTaskAndReply(
+ FROM_HERE,
+ base::Bind(&BootTimesLoader::Stats::RecordStatsImpl,
+ base::Owned(new Stats(*this)),
+ name),
+ callback);
+}
+
+void BootTimesLoader::Stats::RecordStatsImpl(
+ const base::FilePath::StringType& name) const {
+ DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+
+ const base::FilePath log_path(kLogPath);
+ const base::FilePath uptime_output =
+ log_path.Append(base::FilePath(kUptimePrefix + name));
+ const base::FilePath disk_output =
+ log_path.Append(base::FilePath(kDiskPrefix + name));
+
+ // Append numbers to the files.
+ AppendFile(uptime_output, uptime_.data(), uptime_.size());
+ AppendFile(disk_output, disk_.data(), disk_.size());
+}
+
BootTimesLoader::BootTimesLoader()
: backend_(new Backend()),
have_registered_(false),
@@ -121,42 +244,14 @@
logout_time_markers_.reserve(30);
}
-BootTimesLoader::~BootTimesLoader() {}
+BootTimesLoader::~BootTimesLoader() {
+}
// static
BootTimesLoader* BootTimesLoader::Get() {
return g_boot_times_loader.Pointer();
}
-// Appends the given buffer into the file. Returns the number of bytes
-// written, or -1 on error.
-// TODO(satorux): Move this to file_util.
-static int AppendFile(const base::FilePath& file_path,
- const char* data,
- int size) {
- FILE* file = base::OpenFile(file_path, "a");
- if (!file) {
- return -1;
- }
- const int num_bytes_written = fwrite(data, 1, size, file);
- base::CloseFile(file);
- return num_bytes_written;
-}
-
-static void RecordStatsDelayed(const base::FilePath::StringType& name,
- const std::string& uptime,
- const std::string& disk) {
- const base::FilePath log_path(kLogPath);
- const base::FilePath uptime_output =
- log_path.Append(base::FilePath(kUptimePrefix + name));
- const base::FilePath disk_output =
- log_path.Append(base::FilePath(kDiskPrefix + name));
-
- // Append numbers to the files.
- AppendFile(uptime_output, uptime.data(), uptime.size());
- AppendFile(disk_output, disk.data(), disk.size());
-}
-
// static
void BootTimesLoader::WriteTimes(
const std::string base_name,
@@ -265,32 +360,63 @@
logout_time_markers_);
}
-void BootTimesLoader::RecordStats(const std::string& name, const Stats& stats) {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&RecordStatsDelayed, name, stats.uptime, stats.disk));
+// static
+void BootTimesLoader::ClearLogoutStartedLastPreference() {
+ PrefService* local_state = g_browser_process->local_state();
+ local_state->ClearPref(prefs::kLogoutStartedLast);
}
-BootTimesLoader::Stats BootTimesLoader::GetCurrentStats() {
- const base::FilePath kProcUptime(FPL("/proc/uptime"));
- const base::FilePath kDiskStat(FPL("/sys/block/sda/stat"));
- Stats stats;
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- base::ReadFileToString(kProcUptime, &stats.uptime);
- base::ReadFileToString(kDiskStat, &stats.disk);
- return stats;
+void BootTimesLoader::OnChromeProcessStart() {
+ PrefService* local_state = g_browser_process->local_state();
+ const std::string logout_started_last_str =
+ local_state->GetString(prefs::kLogoutStartedLast);
+ if (logout_started_last_str.empty())
+ return;
+
+ // Note that kLogoutStartedLast is not cleared on format error to stay in
+ // logs in case of other fatal system errors.
+
+ const Stats logout_started_last_stats =
+ Stats::DeserializeFromString(logout_started_last_str);
+ if (logout_started_last_stats.uptime().empty())
+ return;
+
+ double logout_started_last;
+ double uptime;
+ if (!logout_started_last_stats.UptimeDouble(&logout_started_last) ||
+ !Stats::GetCurrentStats().UptimeDouble(&uptime)) {
+ return;
+ }
+
+ if (logout_started_last >= uptime) {
+ // Reboot happened.
+ ClearLogoutStartedLastPreference();
+ return;
+ }
+
+ // Write /tmp/uptime-logout-started as well.
+ const char kLogoutStarted[] = "logout-started";
+ logout_started_last_stats.RecordStatsWithCallback(
+ kLogoutStarted,
+ base::Bind(&BootTimesLoader::ClearLogoutStartedLastPreference));
+}
+
+void BootTimesLoader::OnLogoutStarted(PrefService* state) {
+ const std::string uptime = Stats::GetCurrentStats().SerializeToString();
+ if (!uptime.empty())
+ state->SetString(prefs::kLogoutStartedLast, uptime);
}
void BootTimesLoader::RecordCurrentStats(const std::string& name) {
- RecordStats(name, GetCurrentStats());
+ Stats::GetCurrentStats().RecordStats(name);
}
void BootTimesLoader::SaveChromeMainStats() {
- chrome_main_stats_ = GetCurrentStats();
+ chrome_main_stats_ = Stats::GetCurrentStats();
}
void BootTimesLoader::RecordChromeMainStats() {
- RecordStats(kChromeMain, chrome_main_stats_);
+ chrome_main_stats_.RecordStats(kChromeMain);
}
void BootTimesLoader::RecordLoginAttempted() {
diff --git a/chrome/browser/chromeos/boot_times_loader.h b/chrome/browser/chromeos/boot_times_loader.h
index 854dccd..1df7e1c 100644
--- a/chrome/browser/chromeos/boot_times_loader.h
+++ b/chrome/browser/chromeos/boot_times_loader.h
@@ -17,6 +17,8 @@
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/render_widget_host.h"
+class PrefService;
+
namespace chromeos {
// BootTimesLoader loads the bootimes of Chrome OS from the file system.
@@ -82,6 +84,12 @@
// Mark that WriteLogoutTimes should handle restart.
void set_restart_requested() { restart_requested_ = true; }
+ // This is called on Chrome process startup to write saved logout stats.
+ void OnChromeProcessStart();
+
+ // This saves logout-started metric to Local State.
+ void OnLogoutStarted(PrefService* state);
+
private:
// BootTimesLoader calls into the Backend on the file thread to load
// the boot times.
@@ -119,21 +127,46 @@
bool send_to_uma_;
};
- struct Stats {
+ class Stats {
public:
- std::string uptime;
- std::string disk;
+ // Initializes stats with current /proc values.
+ static Stats GetCurrentStats();
+
+ // Returns JSON representation.
+ std::string SerializeToString() const;
+
+ // Creates new object from JSON representation.
+ static Stats DeserializeFromString(const std::string& value);
+
+ const std::string& uptime() const { return uptime_; }
+ const std::string& disk() const { return disk_; }
+
+ // Writes "uptime in seconds" to result. (This is first field in uptime_.)
+ // Returns true on successful conversion.
+ bool UptimeDouble(double* result) const;
+
+ void RecordStats(const std::string& name) const;
+ void RecordStatsWithCallback(const std::string& name,
+ const base::Closure& callback) const;
+
+ private:
+ // Runs on BlockingPool
+ void RecordStatsImpl(const std::string& name) const;
+
+ std::string uptime_;
+ std::string disk_;
};
- static void RecordStats(
- const std::string& name, const Stats& stats);
- static Stats GetCurrentStats();
static void WriteTimes(const std::string base_name,
const std::string uma_name,
const std::string uma_prefix,
std::vector<TimeMarker> login_times);
static void AddMarker(std::vector<TimeMarker>* vector, TimeMarker marker);
+ // Clear saved logout-started metric in Local State.
+ // This method is called when logout-state was writen to file.
+ static void ClearLogoutStartedLastPreference();
+
// Used to hold the stats at main().
Stats chrome_main_stats_;
scoped_refptr<Backend> backend_;
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index ea9f3ba..e73622f 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -613,6 +613,8 @@
// -- This used to be in ChromeBrowserMainParts::PreMainMessageLoopRun()
// -- just after CreateProfile().
+ BootTimesLoader::Get()->OnChromeProcessStart();
+
// Restarting Chrome inside existing user session. Possible cases:
// 1. Chrome is restarted after crash.
// 2. Chrome is started in browser_tests skipping the login flow
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 2fa7fe0..d0e04df 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -83,6 +83,7 @@
registry->RegisterBooleanPref(prefs::kOwnerPrimaryMouseButtonRight, false);
registry->RegisterBooleanPref(prefs::kOwnerTapToClickEnabled, true);
registry->RegisterBooleanPref(prefs::kVirtualKeyboardEnabled, false);
+ registry->RegisterStringPref(prefs::kLogoutStartedLast, std::string());
}
// static
diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc
index 6731cb5..7e377a8 100644
--- a/chrome/browser/lifetime/application_lifetime.cc
+++ b/chrome/browser/lifetime/application_lifetime.cc
@@ -143,13 +143,12 @@
#if defined(OS_CHROMEOS)
StartShutdownTracing();
chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("LogoutStarted", false);
- // Write /tmp/uptime-logout-started as well.
- const char kLogoutStarted[] = "logout-started";
- chromeos::BootTimesLoader::Get()->RecordCurrentStats(kLogoutStarted);
- // Login screen should show up in owner's locale.
PrefService* state = g_browser_process->local_state();
if (state) {
+ chromeos::BootTimesLoader::Get()->OnLogoutStarted(state);
+
+ // Login screen should show up in owner's locale.
std::string owner_locale = state->GetString(prefs::kOwnerLocale);
if (!owner_locale.empty() &&
state->GetString(prefs::kApplicationLocale) != owner_locale &&