Add Installer.OSAgeDays metric
This adds a new metric conveying the age of the running OS instance,
where this is defined as the time-span between the current wall-clock
time and the time-stamp of the /etc/lsb-release file. This metric is
reported daily.
BUG=chromium:304950
TEST=New unit tests for daily metrics + unit tests pass. Manual test
for Installer.OSAgeDays by inspecting chrome://histograms.
Change-Id: I6713bed6730641a95443372a3e3166c4e1dc64ee
Reviewed-on: https://chromium-review.googlesource.com/172162
Reviewed-by: Chris Sosa <sosa@chromium.org>
Commit-Queue: David Zeuthen <zeuthen@chromium.org>
Tested-by: David Zeuthen <zeuthen@chromium.org>
diff --git a/constants.cc b/constants.cc
index 767dc47..7f9fdf4 100644
--- a/constants.cc
+++ b/constants.cc
@@ -30,6 +30,8 @@
const char kPrefsCurrentResponseSignature[] = "current-response-signature";
const char kPrefsCurrentUrlFailureCount[] = "current-url-failure-count";
const char kPrefsCurrentUrlIndex[] = "current-url-index";
+const char kPrefsDailyMetricsLastReportedAt[] =
+ "daily-metrics-last-reported-at";
const char kPrefsDeltaUpdateFailures[] = "delta-update-failures";
const char kPrefsFullPayloadAttemptNumber[] = "full-payload-attempt-number";
const char kPrefsLastActivePingDay[] = "last-active-ping-day";
diff --git a/constants.h b/constants.h
index 1e69df8..5fbfa06 100644
--- a/constants.h
+++ b/constants.h
@@ -34,6 +34,7 @@
extern const char kPrefsCurrentResponseSignature[];
extern const char kPrefsCurrentUrlFailureCount[];
extern const char kPrefsCurrentUrlIndex[];
+extern const char kPrefsDailyMetricsLastReportedAt[];
extern const char kPrefsDeltaUpdateFailures[];
extern const char kPrefsFullPayloadAttemptNumber[];
extern const char kPrefsLastActivePingDay[];
diff --git a/update_attempter.cc b/update_attempter.cc
index ae9be74..7d2500b 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -16,6 +16,7 @@
#include <policy/device_policy.h>
#include "update_engine/certificate_checker.h"
+#include "update_engine/clock_interface.h"
#include "update_engine/constants.h"
#include "update_engine/dbus_service.h"
#include "update_engine/download_action.h"
@@ -162,11 +163,83 @@
CleanupCpuSharesManagement();
}
+bool UpdateAttempter::CheckAndReportDailyMetrics() {
+ int64_t stored_value;
+ base::Time now = system_state_->clock()->GetWallclockTime();
+ if (system_state_->prefs()->Exists(kPrefsDailyMetricsLastReportedAt) &&
+ system_state_->prefs()->GetInt64(kPrefsDailyMetricsLastReportedAt,
+ &stored_value)) {
+ base::Time last_reported_at = base::Time::FromInternalValue(stored_value);
+ base::TimeDelta time_reported_since = now - last_reported_at;
+ if (time_reported_since.InSeconds() < 0) {
+ LOG(WARNING) << "Last reported daily metrics "
+ << utils::FormatTimeDelta(time_reported_since) << " ago "
+ << "which is negative. Either the system clock is wrong or "
+ << "the kPrefsDailyMetricsLastReportedAt state variable "
+ << "is wrong.";
+ // In this case, report daily metrics to reset.
+ } else {
+ if (time_reported_since.InSeconds() < 24*60*60) {
+ LOG(INFO) << "Last reported daily metrics "
+ << utils::FormatTimeDelta(time_reported_since) << " ago.";
+ return false;
+ }
+ LOG(INFO) << "Last reported daily metrics "
+ << utils::FormatTimeDelta(time_reported_since) << " ago, "
+ << "which is more than 24 hours ago.";
+ }
+ }
+
+ LOG(INFO) << "Reporting daily metrics.";
+ system_state_->prefs()->SetInt64(kPrefsDailyMetricsLastReportedAt,
+ now.ToInternalValue());
+
+ ReportOSAge();
+
+ return true;
+}
+
+void UpdateAttempter::ReportOSAge() {
+ struct stat sb;
+
+ if (system_state_ == NULL)
+ return;
+
+ if (stat("/etc/lsb-release", &sb) != 0) {
+ PLOG(ERROR) << "Error getting file status for /etc/lsb-release";
+ return;
+ }
+
+ base::Time lsb_release_timestamp = utils::TimeFromStructTimespec(&sb.st_ctim);
+ base::Time now = system_state_->clock()->GetWallclockTime();
+ base::TimeDelta age = now - lsb_release_timestamp;
+ if (age.InSeconds() < 0) {
+ LOG(ERROR) << "The OS age (" << utils::FormatTimeDelta(age)
+ << ") is negative. Maybe the clock is wrong?";
+ return;
+ }
+
+ std::string metric = "Installer.OSAgeDays";
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(age)
+ << " for metric " << metric;
+ system_state_->metrics_lib()->SendToUMA(
+ metric,
+ static_cast<int>(age.InDays()),
+ 0, // min: 0 days
+ 6*30, // max: 6 months (approx)
+ kNumDefaultUmaBuckets);
+}
+
void UpdateAttempter::Update(const string& app_version,
const string& omaha_url,
bool obey_proxies,
bool interactive,
bool is_test_mode) {
+ // This is called at least every 4 hours (see the constant
+ // UpdateCheckScheduler::kTimeoutMaxBackoffInterval) so it's
+ // appropriate to use as a hook for reporting daily metrics.
+ CheckAndReportDailyMetrics();
+
chrome_proxy_resolver_.Init();
fake_update_success_ = false;
if (status_ == UPDATE_STATUS_UPDATED_NEED_REBOOT) {
diff --git a/update_attempter.h b/update_attempter.h
index b2ee3b0..74b73f6 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -202,11 +202,21 @@
FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest);
FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionTest);
FRIEND_TEST(UpdateAttempterTest, UpdateTest);
+ FRIEND_TEST(UpdateAttempterTest, ReportDailyMetrics);
// Ctor helper method.
void Init(SystemState* system_state,
const std::string& update_completed_marker);
+ // Checks if it's more than 24 hours since daily metrics were last
+ // reported and, if so, reports daily metrics. Returns |true| if
+ // metrics were reported, |false| otherwise.
+ bool CheckAndReportDailyMetrics();
+
+ // Calculates and reports the age of the currently running OS. This
+ // is defined as the age of the /etc/lsb-release file.
+ void ReportOSAge();
+
// Sets the status to the given status and notifies a status update over dbus.
// Also accepts a supplement notice, which is delegated to the scheduler and
// used for making better informed scheduling decisions (e.g. retry timeout).
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 7036c83..11464d4 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -9,6 +9,7 @@
#include "update_engine/action_mock.h"
#include "update_engine/action_processor_mock.h"
+#include "update_engine/fake_clock.h"
#include "update_engine/filesystem_copier_action.h"
#include "update_engine/install_plan.h"
#include "update_engine/mock_dbus_interface.h"
@@ -24,6 +25,8 @@
#include "update_engine/update_check_scheduler.h"
#include "update_engine/utils.h"
+using base::Time;
+using base::TimeDelta;
using std::string;
using testing::_;
using testing::DoAll;
@@ -1041,4 +1044,75 @@
g_idle_add(&StaticQuitMainLoop, this);
}
+// Checks that we only report daily metrics at most every 24 hours.
+TEST_F(UpdateAttempterTest, ReportDailyMetrics) {
+ FakeClock fake_clock;
+ Prefs prefs;
+ string temp_dir;
+
+ // We need persistent preferences for this test
+ EXPECT_TRUE(utils::MakeTempDirectory("/tmp/UpdateCheckScheduler.XXXXXX",
+ &temp_dir));
+ prefs.Init(FilePath(temp_dir));
+ mock_system_state_.set_clock(&fake_clock);
+ mock_system_state_.set_prefs(&prefs);
+
+ Time epoch = Time::FromInternalValue(0);
+ fake_clock.SetWallclockTime(epoch);
+
+ // If there is no kPrefsDailyMetricsLastReportedAt state variable,
+ // we should report.
+ EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+ // We should not report again if no time has passed.
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ // We should not report if only 10 hours has passed.
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(10));
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ // We should not report if only 24 hours - 1 sec has passed.
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(24) -
+ TimeDelta::FromSeconds(1));
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ // We should report if 24 hours has passed.
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(24));
+ EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+
+ // But then we should not report again..
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ // .. until another 24 hours has passed
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(47));
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(48));
+ EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ // .. and another 24 hours
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(71));
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(72));
+ EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ // If the span between time of reporting and present time is
+ // negative, we report. This is in order to reset the timestamp and
+ // avoid an edge condition whereby a distant point in the future is
+ // in the state variable resulting in us never ever reporting again.
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(71));
+ EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ // In this case we should not update until the clock reads 71 + 24 = 95.
+ // Check that.
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(94));
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+ fake_clock.SetWallclockTime(epoch + TimeDelta::FromHours(95));
+ EXPECT_TRUE(attempter_.CheckAndReportDailyMetrics());
+ EXPECT_FALSE(attempter_.CheckAndReportDailyMetrics());
+
+ EXPECT_TRUE(utils::RecursiveUnlinkDir(temp_dir));
+}
+
} // namespace chromeos_update_engine