metrics: Add UpdateEngine.Rollback.Result metric.

Add a metric to track if rollback succeeds or fails. The main use case
for this metric is to record how often rollback actually happens.

Right now we only track success/failure but if we see a lot of
failures in the future, we could add a new metric, for example

 UpdateEngine.Rollback.FailureErrorCode

to convey more detail.

BUG=None
TEST=New unit test + unit tests pass.

Change-Id: I5c32d8585dc8e4b6d38c540d2f754ede5b2ad50b
Reviewed-on: https://chromium-review.googlesource.com/195203
Reviewed-by: David Zeuthen <zeuthen@chromium.org>
Commit-Queue: David Zeuthen <zeuthen@chromium.org>
Tested-by: David Zeuthen <zeuthen@chromium.org>
diff --git a/metrics.cc b/metrics.cc
index 72a83bd..9bcb859 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -88,6 +88,9 @@
 const char kMetricSuccessfulUpdateUrlSwitchCount[] =
     "UpdateEngine.SuccessfulUpdate.UrlSwitchCount";
 
+// UpdateEngine.Rollback.* metric.
+const char kMetricRollbackResult[] = "UpdateEngine.Rollback.Result";
+
 // UpdateEngine.* metrics.
 const char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount";
 const char kMetricInstallDateProvisioningSource[] =
@@ -451,6 +454,20 @@
                                          50);  // num_buckets
 }
 
+void ReportRollbackMetrics(SystemState *system_state,
+                           RollbackResult result) {
+  string metric;
+  int value;
+
+  metric = metrics::kMetricRollbackResult;
+  value = static_cast<int>(result);
+  LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+  system_state->metrics_lib()->SendEnumToUMA(
+      metric,
+      value,
+      static_cast<int>(metrics::RollbackResult::kNumConstants));
+}
+
 }  // namespace metrics
 
 }  // namespace chromeos_update_engine
diff --git a/metrics.h b/metrics.h
index b5dd5f1..99d1e5e 100644
--- a/metrics.h
+++ b/metrics.h
@@ -56,6 +56,9 @@
 extern const char kMetricSuccessfulUpdateUpdatesAbandonedCount[];
 extern const char kMetricSuccessfulUpdateUrlSwitchCount[];
 
+// UpdateEngine.Rollback.* metric.
+extern const char kMetricRollbackResult[];
+
 // UpdateEngine.* metrics.
 extern const char kMetricFailedUpdateCount[];
 extern const char kMetricInstallDateProvisioningSource[];
@@ -158,6 +161,23 @@
   kUnset = -1
 };
 
+// Possible ways a rollback can end.
+//
+// This is used in the UpdateEngine.Rollback histogram.
+enum class RollbackResult {
+  kFailed,
+  kSuccess,
+
+  kNumConstants
+};
+
+// Helper function to report metrics related to rollback. The
+// following metrics are reported:
+//
+//  |kMetricRollbackResult|
+void ReportRollbackMetrics(SystemState *system_state,
+                           RollbackResult result);
+
 // Helper function to report metrics reported once a day. The
 // following metrics are reported:
 //
diff --git a/payload_state.cc b/payload_state.cc
index 4c8efa0..c176238 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -203,10 +203,16 @@
   CalculateUpdateDurationUptime();
   SetUpdateTimestampEnd(system_state_->clock()->GetWallclockTime());
 
-  // Only report metrics on an update, not on a rollback.
-  if (attempt_type_ == AttemptType::kUpdate) {
-    CollectAndReportAttemptMetrics(kErrorCodeSuccess);
-    CollectAndReportSuccessfulUpdateMetrics();
+  switch (attempt_type_) {
+    case AttemptType::kUpdate:
+      CollectAndReportAttemptMetrics(kErrorCodeSuccess);
+      CollectAndReportSuccessfulUpdateMetrics();
+      break;
+
+    case AttemptType::kRollback:
+      metrics::ReportRollbackMetrics(system_state_,
+                                     metrics::RollbackResult::kSuccess);
+      break;
   }
 
   // Reset the number of responses seen since it counts from the last
@@ -229,9 +235,16 @@
     return;
   }
 
-  // Only report metrics on an update, not on a rollback.
-  if (attempt_type_ == AttemptType::kUpdate)
-    CollectAndReportAttemptMetrics(base_error);
+  switch (attempt_type_) {
+    case AttemptType::kUpdate:
+      CollectAndReportAttemptMetrics(base_error);
+      break;
+
+    case AttemptType::kRollback:
+      metrics::ReportRollbackMetrics(system_state_,
+                                     metrics::RollbackResult::kFailed);
+      break;
+  }
 
   switch (base_error) {
     // Errors which are good indicators of a problem with a particular URL or
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index 11eb4af..24aa070 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -1081,11 +1081,17 @@
   payload_state.LoadRollbackVersion();
   EXPECT_EQ(rollback_version, payload_state.GetRollbackVersion());
 
-  // Check that we don't report any metrics in UpdateSucceeded().
+  // Check that we report only UpdateEngine.Rollback.* metrics in
+  // UpdateSucceeded().
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
     .Times(0);
   EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
     .Times(0);
+  EXPECT_CALL(*fake_system_state.mock_metrics_lib(),
+              SendEnumToUMA(
+                  metrics::kMetricRollbackResult,
+                  static_cast<int>(metrics::RollbackResult::kSuccess),
+                  static_cast<int>(metrics::RollbackResult::kNumConstants)));
   payload_state.UpdateSucceeded();
 }