Add new metrics.
The current metrics (Installer.* namespace) have several shortcomings,
for example it's not immediately clear when and how frequent each
metric is reported. This CL introduces new metrics that addresses this
and other problems. The new metrics are all in the UpdateEngine.*
namespace and fall into five categories
UpdateEngine.Daily.* Reported daily.
UpdateEngine.Check.* On every check.
UpdateEngine.Attempt.* On every attempt.
UpdateEngine.SuccessfulUpdate.* With every successful update.
UpdateEngine.* Miscellaneous
Most of the new metrics mimic existing metrics and also leverage the
existing code, book-keeping and unit tests. The plan is to remove the
Installer.* metrics once we're happy with the new ones.
I've also tested this manually by performing updates and verifying
that chrome://histograms looks correct.
BUG=chromium:355745
TEST=New unit tests + unit tests pass + manual testing.
Change-Id: I7a3f68d75910384b116c7e4664776e25d3997584
Reviewed-on: https://chromium-review.googlesource.com/191314
Reviewed-by: David Zeuthen <zeuthen@chromium.org>
Tested-by: David Zeuthen <zeuthen@chromium.org>
Commit-Queue: David Zeuthen <zeuthen@chromium.org>
diff --git a/SConstruct b/SConstruct
index 96f4bfb..2f40697 100644
--- a/SConstruct
+++ b/SConstruct
@@ -224,6 +224,7 @@
install_plan.cc
libcurl_http_fetcher.cc
metadata.cc
+ metrics.cc
multi_range_http_fetcher.cc
omaha_hash_calculator.cc
omaha_request_action.cc
diff --git a/action.h b/action.h
index 45c6430..7ac4a13 100644
--- a/action.h
+++ b/action.h
@@ -81,6 +81,9 @@
// done.
virtual void PerformAction() = 0;
+ // Called on ActionProcess::ActionComplete() by ActionProcessor.
+ virtual void ActionCompleted(ErrorCode code) {}
+
// Called by the ActionProcessor to tell this Action which processor
// it belongs to.
void SetProcessor(ActionProcessor* processor) {
diff --git a/action_processor.cc b/action_processor.cc
index d4e5cc4..068a399 100644
--- a/action_processor.cc
+++ b/action_processor.cc
@@ -59,6 +59,7 @@
if (delegate_)
delegate_->ActionCompleted(this, actionptr, code);
string old_type = current_action_->Type();
+ current_action_->ActionCompleted(code);
current_action_->SetProcessor(NULL);
current_action_ = NULL;
if (actions_.empty()) {
diff --git a/constants.cc b/constants.cc
index b67707b..f094016 100644
--- a/constants.cc
+++ b/constants.cc
@@ -38,6 +38,10 @@
const char kPrefsLastActivePingDay[] = "last-active-ping-day";
const char kPrefsLastRollCallPingDay[] = "last-roll-call-ping-day";
const char kPrefsManifestMetadataSize[] = "manifest-metadata-size";
+const char kPrefsMetricsAttemptLastReportingTime[] =
+ "metrics-attempt-last-reporting-time";
+const char kPrefsMetricsCheckLastReportingTime[] =
+ "metrics-check-last-reporting-time";
const char kPrefsNumReboots[] = "num-reboots";
const char kPrefsNumResponsesSeen[] = "num-responses-seen";
const char kPrefsP2PEnabled[] = "p2p-enabled";
diff --git a/constants.h b/constants.h
index cb63d1a..59e3737 100644
--- a/constants.h
+++ b/constants.h
@@ -41,6 +41,8 @@
extern const char kPrefsLastActivePingDay[];
extern const char kPrefsLastRollCallPingDay[];
extern const char kPrefsManifestMetadataSize[];
+extern const char kPrefsMetricsAttemptLastReportingTime[];
+extern const char kPrefsMetricsCheckLastReportingTime[];
extern const char kPrefsNumReboots[];
extern const char kPrefsNumResponsesSeen[];
extern const char kPrefsP2PEnabled[];
diff --git a/metrics.cc b/metrics.cc
new file mode 100644
index 0000000..b784b61
--- /dev/null
+++ b/metrics.cc
@@ -0,0 +1,447 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/metrics.h"
+
+#include <string>
+
+#include <base/logging.h>
+
+#include "update_engine/constants.h"
+#include "update_engine/clock_interface.h"
+#include "update_engine/prefs_interface.h"
+#include "update_engine/system_state.h"
+#include "update_engine/utils.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+// UpdateEngine.Daily.* metrics.
+const char kMetricDailyOSAgeDays[] = "UpdateEngine.Daily.OSAgeDays";
+
+// UpdateEngine.Check.* metrics.
+const char kMetricCheckDownloadErrorCode[] =
+ "UpdateEngine.Check.DownloadErrorCode";
+const char kMetricCheckReaction[] = "UpdateEngine.Check.Reaction";
+const char kMetricCheckResult[] = "UpdateEngine.Check.Result";
+const char kMetricCheckTimeSinceLastCheckMinutes[] =
+ "UpdateEngine.Check.TimeSinceLastCheckMinutes";
+const char kMetricCheckTimeSinceLastCheckUptimeMinutes[] =
+ "UpdateEngine.Check.TimeSinceLastCheckUptimeMinutes";
+
+// UpdateEngine.Attempt.* metrics.
+const char kMetricAttemptNumber[] = "UpdateEngine.Attempt.Number";
+const char kMetricAttemptPayloadType[] =
+ "UpdateEngine.Attempt.PayloadType";
+const char kMetricAttemptPayloadSizeMiB[] =
+ "UpdateEngine.Attempt.PayloadSizeMiB";
+const char kMetricAttemptDurationMinutes[] =
+ "UpdateEngine.Attempt.DurationMinutes";
+const char kMetricAttemptDurationUptimeMinutes[] =
+ "UpdateEngine.Attempt.DurationUptimeMinutes";
+const char kMetricAttemptTimeSinceLastAttemptMinutes[] =
+ "UpdateEngine.Attempt.TimeSinceLastAttemptMinutes";
+const char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[] =
+ "UpdateEngine.Attempt.TimeSinceLastAttemptUptimeMinutes";
+const char kMetricAttemptPayloadBytesOffset[] =
+ "UpdateEngine.Attempt.PayloadBytesOffset";
+const char kMetricAttemptPayloadBytesNeeded[] =
+ "UpdateEngine.Attempt.PayloadBytesNeeded";
+const char kMetricAttemptPayloadBytesDownloadedMiB[] =
+ "UpdateEngine.Attempt.PayloadBytesDownloadedMiB";
+const char kMetricAttemptPayloadDownloadSpeedKBps[] =
+ "UpdateEngine.Attempt.PayloadDownloadSpeedKBps";
+const char kMetricAttemptDownloadSource[] =
+ "UpdateEngine.Attempt.DownloadSource";
+const char kMetricAttemptResult[] =
+ "UpdateEngine.Attempt.Result";
+const char kMetricAttemptInternalErrorCode[] =
+ "UpdateEngine.Attempt.InternalErrorCode";
+const char kMetricAttemptDownloadErrorCode[] =
+ "UpdateEngine.Attempt.DownloadErrorCode";
+
+// UpdateEngine.SuccessfulUpdate.* metrics.
+const char kMetricSuccessfulUpdateAttemptCount[] =
+ "UpdateEngine.SuccessfulUpdate.AttemptCount";
+const char kMetricSuccessfulUpdateBytesDownloadedMiB[] =
+ "UpdateEngine.SuccessfulUpdate.BytesDownloadedMiB";
+const char kMetricSuccessfulUpdateDownloadOverheadPercentage[] =
+ "UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage";
+const char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
+ "UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed";
+const char kMetricSuccessfulUpdatePayloadType[] =
+ "UpdateEngine.SuccessfulUpdate.PayloadType";
+const char kMetricSuccessfulUpdatePayloadSizeMiB[] =
+ "UpdateEngine.SuccessfulUpdate.PayloadSizeMiB";
+const char kMetricSuccessfulUpdateRebootCount[] =
+ "UpdateEngine.SuccessfulUpdate.RebootCount";
+const char kMetricSuccessfulUpdateTotalDurationMinutes[] =
+ "UpdateEngine.SuccessfulUpdate.TotalDurationMinutes";
+const char kMetricSuccessfulUpdateUpdatesAbandonedCount[] =
+ "UpdateEngine.SuccessfulUpdate.UpdatesAbandonedCount";
+const char kMetricSuccessfulUpdateUrlSwitchCount[] =
+ "UpdateEngine.SuccessfulUpdate.UrlSwitchCount";
+
+// UpdateEngine.* metrics.
+const char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount";
+const char kMetricInstallDateProvisioningSource[] =
+ "UpdateEngine.InstallDateProvisioningSource";
+const char kMetricTimeToRebootMinutes[] =
+ "UpdateEngine.TimeToRebootMinutes";
+
+void ReportDailyMetrics(SystemState *system_state,
+ base::TimeDelta os_age) {
+ string metric = metrics::kMetricDailyOSAgeDays;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(os_age)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(
+ metric,
+ static_cast<int>(os_age.InDays()),
+ 0, // min: 0 days
+ 6*30, // max: 6 months (approx)
+ 50); // num_buckets
+}
+
+void ReportUpdateCheckMetrics(SystemState *system_state,
+ CheckResult result,
+ CheckReaction reaction,
+ DownloadErrorCode download_error_code) {
+ string metric;
+ int value;
+ int max_value;
+
+ if (result != metrics::CheckResult::kUnset) {
+ metric = metrics::kMetricCheckResult;
+ value = static_cast<int>(result);
+ max_value = static_cast<int>(metrics::CheckResult::kNumConstants) - 1;
+ LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+ system_state->metrics_lib()->SendEnumToUMA(metric, value, max_value);
+ }
+ if (reaction != metrics::CheckReaction::kUnset) {
+ metric = metrics::kMetricCheckReaction;
+ value = static_cast<int>(reaction);
+ max_value = static_cast<int>(metrics::CheckReaction::kNumConstants) - 1;
+ LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+ system_state->metrics_lib()->SendEnumToUMA(metric, value, max_value);
+ }
+ if (download_error_code != metrics::DownloadErrorCode::kUnset) {
+ metric = metrics::kMetricCheckDownloadErrorCode;
+ value = static_cast<int>(download_error_code);
+ max_value = static_cast<int>(metrics::DownloadErrorCode::kNumConstants) - 1;
+ LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
+ system_state->metrics_lib()->SendEnumToUMA(metric, value, max_value);
+ }
+
+ base::TimeDelta time_since_last;
+ if (utils::WallclockDurationHelper(system_state,
+ kPrefsMetricsCheckLastReportingTime,
+ &time_since_last)) {
+ metric = kMetricCheckTimeSinceLastCheckMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(
+ metric,
+ time_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30*24*60, // max: 30 days
+ 50); // num_buckets
+ }
+
+ base::TimeDelta uptime_since_last;
+ static int64_t uptime_since_last_storage = 0;
+ if (utils::MonotonicDurationHelper(system_state,
+ &uptime_since_last_storage,
+ &uptime_since_last)) {
+ metric = kMetricCheckTimeSinceLastCheckUptimeMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(
+ metric,
+ uptime_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30*24*60, // max: 30 days
+ 50); // num_buckets
+ }
+}
+
+void ReportUpdateAttemptMetrics(
+ SystemState *system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ AttemptResult attempt_result,
+ ErrorCode internal_error_code,
+ DownloadErrorCode payload_download_error_code) {
+ string metric;
+
+ metric = metrics::kMetricAttemptNumber;
+ LOG(INFO) << "Uploading " << attempt_number << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ attempt_number,
+ 0, // min: 0 attempts
+ 49, // max: 49 attempts
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptPayloadType;
+ LOG(INFO) << "Uploading " << utils::ToString(payload_type)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendEnumToUMA(metric,
+ payload_type,
+ kNumPayloadTypes);
+
+ metric = metrics::kMetricAttemptDurationMinutes;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ duration.InMinutes(),
+ 0, // min: 0 min
+ 10*24*60, // max: 10 days
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptDurationUptimeMinutes;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ duration_uptime.InMinutes(),
+ 0, // min: 0 min
+ 10*24*60, // max: 10 days
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptPayloadSizeMiB;
+ int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+ LOG(INFO) << "Uploading " << payload_size_mib << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ payload_size_mib,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptPayloadBytesDownloadedMiB;
+ int64_t payload_bytes_downloaded_mib =
+ payload_bytes_downloaded / kNumBytesInOneMiB;
+ LOG(INFO) << "Uploading " << payload_bytes_downloaded_mib
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ payload_bytes_downloaded_mib,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptPayloadDownloadSpeedKBps;
+ int64_t payload_download_speed_kbps = payload_download_speed_bps / 1000;
+ LOG(INFO) << "Uploading " << payload_download_speed_kbps
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ payload_download_speed_kbps,
+ 0, // min: 0 kB/s
+ 10*1000, // max: 10000 kB/s = 10 MB/s
+ 50); // num_buckets
+
+ metric = metrics::kMetricAttemptDownloadSource;
+ LOG(INFO) << "Uploading " << download_source
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendEnumToUMA(metric,
+ download_source,
+ kNumDownloadSources);
+
+ metric = metrics::kMetricAttemptResult;
+ LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendEnumToUMA(
+ metric,
+ static_cast<int>(attempt_result),
+ static_cast<int>(AttemptResult::kNumConstants));
+
+ if (internal_error_code != kErrorCodeSuccess) {
+ metric = metrics::kMetricAttemptInternalErrorCode;
+ LOG(INFO) << "Uploading " << internal_error_code
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendEnumToUMA(
+ metric,
+ internal_error_code,
+ kErrorCodeUmaReportedMax);
+ }
+
+ if (payload_download_error_code != DownloadErrorCode::kUnset) {
+ metric = metrics::kMetricAttemptDownloadErrorCode;
+ LOG(INFO) << "Uploading " << static_cast<int>(payload_download_error_code)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendEnumToUMA(
+ metric,
+ static_cast<int>(payload_download_error_code),
+ static_cast<int>(DownloadErrorCode::kNumConstants));
+ }
+
+ base::TimeDelta time_since_last;
+ if (utils::WallclockDurationHelper(system_state,
+ kPrefsMetricsAttemptLastReportingTime,
+ &time_since_last)) {
+ metric = kMetricAttemptTimeSinceLastAttemptMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(
+ metric,
+ time_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30*24*60, // max: 30 days
+ 50); // num_buckets
+ }
+
+ static int64_t uptime_since_last_storage = 0;
+ base::TimeDelta uptime_since_last;
+ if (utils::MonotonicDurationHelper(system_state,
+ &uptime_since_last_storage,
+ &uptime_since_last)) {
+ metric = kMetricAttemptTimeSinceLastAttemptUptimeMinutes;
+ LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(
+ metric,
+ uptime_since_last.InMinutes(),
+ 0, // min: 0 min
+ 30*24*60, // max: 30 days
+ 50); // num_buckets
+ }
+}
+
+
+void ReportSuccessfulUpdateMetrics(
+ SystemState *system_state,
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count) {
+ string metric;
+ int64_t mbs;
+
+ metric = kMetricSuccessfulUpdatePayloadSizeMiB;
+ mbs = payload_size / kNumBytesInOneMiB;
+ LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ mbs,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+
+ int64_t total_bytes = 0;
+ int download_sources_used = 0;
+ for (int i = 0; i < kNumDownloadSources + 1; i++) {
+ DownloadSource source = static_cast<DownloadSource>(i);
+
+ // Only consider this download source (and send byte counts) as
+ // having been used if we downloaded a non-trivial amount of bytes
+ // (e.g. at least 1 MiB) that contributed to the the
+ // update. Otherwise we're going to end up with a lot of zero-byte
+ // events in the histogram.
+
+ metric = metrics::kMetricSuccessfulUpdateBytesDownloadedMiB;
+ if (i < kNumDownloadSources) {
+ metric += utils::ToString(source);
+ mbs = num_bytes_downloaded[i] / kNumBytesInOneMiB;
+ total_bytes += num_bytes_downloaded[i];
+ if (mbs > 0)
+ download_sources_used |= (1 << i);
+ } else {
+ mbs = total_bytes / kNumBytesInOneMiB;
+ }
+
+ if (mbs > 0) {
+ LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ mbs,
+ 0, // min: 0 MiB
+ 1024, // max: 1024 MiB = 1 GiB
+ 50); // num_buckets
+ }
+ }
+
+ metric = metrics::kMetricSuccessfulUpdateDownloadSourcesUsed;
+ LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
+ << " (bit flags) for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(
+ metric,
+ download_sources_used,
+ 0, // min
+ (1 << kNumDownloadSources) - 1, // max
+ 1 << kNumDownloadSources); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage;
+ LOG(INFO) << "Uploading " << download_overhead_percentage
+ << "% for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ download_overhead_percentage,
+ 0, // min: 0% overhead
+ 1000, // max: 1000% overhead
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateUrlSwitchCount;
+ LOG(INFO) << "Uploading " << url_switch_count
+ << " (count) for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ url_switch_count,
+ 0, // min: 0 URL switches
+ 49, // max: 49 URL switches
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateTotalDurationMinutes;
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(total_duration)
+ << " for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(
+ metric,
+ static_cast<int>(total_duration.InMinutes()),
+ 0, // min: 0 min
+ 365*24*60, // max: 365 days ~= 1 year
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdateRebootCount;
+ LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
+ << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ reboot_count,
+ 0, // min: 0 reboots
+ 49, // max: 49 reboots
+ 50); // num_buckets
+
+ metric = metrics::kMetricSuccessfulUpdatePayloadType;
+ system_state->metrics_lib()->SendEnumToUMA(metric,
+ payload_type,
+ kNumPayloadTypes);
+ LOG(INFO) << "Uploading " << utils::ToString(payload_type)
+ << " for metric " << metric;
+
+ metric = metrics::kMetricSuccessfulUpdateAttemptCount;
+ system_state->metrics_lib()->SendToUMA(metric,
+ attempt_count,
+ 1, // min: 1 attempt
+ 50, // max: 50 attempts
+ 50); // num_buckets
+ LOG(INFO) << "Uploading " << attempt_count
+ << " for metric " << metric;
+
+ metric = metrics::kMetricSuccessfulUpdateUpdatesAbandonedCount;
+ LOG(INFO) << "Uploading " << updates_abandoned_count
+ << " (count) for metric " << metric;
+ system_state->metrics_lib()->SendToUMA(metric,
+ updates_abandoned_count,
+ 0, // min: 0 counts
+ 49, // max: 49 counts
+ 50); // num_buckets
+}
+
+} // namespace metrics
+
+} // namespace chromeos_update_engine
diff --git a/metrics.h b/metrics.h
new file mode 100644
index 0000000..4e6ed04
--- /dev/null
+++ b/metrics.h
@@ -0,0 +1,256 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_METRICS_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_METRICS_H_
+
+#include <base/time/time.h>
+
+#include "update_engine/constants.h"
+#include "update_engine/error_code.h"
+
+namespace chromeos_update_engine {
+
+class SystemState;
+
+namespace metrics {
+
+// UpdateEngine.Daily.* metrics.
+extern const char kMetricDailyOSAgeDays[];
+
+// UpdateEngine.Check.* metrics.
+extern const char kMetricCheckDownloadErrorCode[];
+extern const char kMetricCheckReaction[];
+extern const char kMetricCheckResult[];
+extern const char kMetricCheckTimeSinceLastCheckMinutes[];
+extern const char kMetricCheckTimeSinceLastCheckUptimeMinutes[];
+
+// UpdateEngine.Attempt.* metrics.
+extern const char kMetricAttemptNumber[];
+extern const char kMetricAttemptPayloadType[];
+extern const char kMetricAttemptPayloadSizeMiB[];
+extern const char kMetricAttemptDurationMinutes[];
+extern const char kMetricAttemptDurationUptimeMinutes[];
+extern const char kMetricAttemptTimeSinceLastAttemptSeconds[];
+extern const char kMetricAttemptTimeSinceLastAttemptUptimeSeconds[];
+extern const char kMetricAttemptPayloadBytesOffset[];
+extern const char kMetricAttemptPayloadBytesNeeded[];
+extern const char kMetricAttemptPayloadBytesDownloaded[];
+extern const char kMetricAttemptPayloadDownloadSpeedKBps[];
+extern const char kMetricAttemptDownloadSource[];
+extern const char kMetricAttemptResult[];
+extern const char kMetricAttemptInternalErrorCode[];
+extern const char kMetricAttemptDownloadErrorCode[];
+
+// UpdateEngine.SuccessfulUpdate.* metrics.
+extern const char kMetricSuccessfulUpdateAttemptCount[];
+extern const char kMetricSuccessfulUpdateBytesDownloadedMiB[];
+extern const char kMetricSuccessfulUpdateDownloadOverheadPercentage[];
+extern const char kMetricSuccessfulUpdateDownloadSourcesUsed[];
+extern const char kMetricSuccessfulUpdatePayloadType[];
+extern const char kMetricSuccessfulUpdatePayloadSizeMiB[];
+extern const char kMetricSuccessfulUpdateTotalDurationMinutes[];
+extern const char kMetricSuccessfulUpdateRebootCount[];
+extern const char kMetricSuccessfulUpdateUpdatesAbandonedCount[];
+extern const char kMetricSuccessfulUpdateUrlSwitchCount[];
+
+// UpdateEngine.* metrics.
+extern const char kMetricFailedUpdateCount[];
+extern const char kMetricInstallDateProvisioningSource[];
+extern const char kMetricTimeToRebootMinutes[];
+
+// The possible outcomes when checking for updates.
+//
+// This is used in the UpdateEngine.Check.Result histogram.
+enum class CheckResult {
+ kUpdateAvailable, // Response indicates an update is available.
+ kNoUpdateAvailable, // Response indicates no updates are available.
+ kDownloadError, // Error downloading response from Omaha.
+ kParsingError, // Error parsing response.
+ kRebootPending, // No update check was performed a reboot is pending.
+
+ kNumConstants,
+ kUnset = -1
+};
+
+// Possible ways a device can react to a new update being available.
+//
+// This is used in the UpdateEngine.Check.Reaction histogram.
+enum class CheckReaction {
+ kUpdating, // Device proceeds to download and apply update.
+ kIgnored , // Device-policy dictates ignoring the update.
+ kDeferring, // Device-policy dictates waiting.
+ kBackingOff, // Previous errors dictates waiting.
+
+ kNumConstants,
+ kUnset = -1
+};
+
+// The possible ways that downloading from a HTTP or HTTPS server can fail.
+//
+// This is used in the UpdateEngine.Check.DownloadErrorCode and
+// UpdateEngine.Attempt.DownloadErrorCode histograms.
+enum class DownloadErrorCode {
+ // Errors that can happen in the field. See http://crbug.com/355745
+ // for how we plan to add more detail in the future.
+ kDownloadError = 0, // Error downloading data from server.
+
+ // IMPORTANT: When adding a new error code, add at the bottom of the
+ // above block and before the kInputMalformed field. This
+ // is to ensure that error codes are not reordered.
+
+ // This error code is used to convey that malformed input was given
+ // to the utils::GetDownloadErrorCode() function. This should never
+ // happen but if it does it's because of an internal update_engine
+ // error and we're interested in knowing this.
+ kInputMalformed = 100,
+
+ // Bucket for capturing HTTP status codes not in the 200-599
+ // range. This should never happen in practice but if it does we
+ // want to know.
+ kHttpStatusOther = 101,
+
+ // Above 200 and below 600, the value is the HTTP status code.
+ kHttpStatus200 = 200,
+
+ kNumConstants = 600,
+
+ kUnset = -1
+};
+
+// Possible ways an update attempt can end.
+//
+// This is used in the UpdateEngine.Attempt.Result histogram.
+enum class AttemptResult {
+ kUpdateSucceeded, // The update succeeded.
+ kInternalError, // An internal error occurred.
+ kPayloadDownloadError, // Failure while downloading payload.
+ kMetadataMalformed, // Metadata was malformed.
+ kOperationMalformed, // An operation was malformed.
+ kOperationExecutionError, // An operation failed to execute.
+ kMetadataVerificationFailed, // Metadata verification failed.
+ kPayloadVerificationFailed, // Payload verification failed.
+ kVerificationFailed, // Root or Kernel partition verification failed.
+ kPostInstallFailed, // The postinstall step failed.
+ kAbnormalTermination, // The attempt ended abnormally.
+
+ kNumConstants,
+
+ kUnset = -1
+};
+
+// Helper function to report metrics reported once a day. The
+// following metrics are reported:
+//
+// |kMetricDailyOSAgeDays|
+void ReportDailyMetrics(SystemState *system_state,
+ base::TimeDelta os_age);
+
+// Helper function to report metrics after completing an update check
+// with the ChromeOS update server ("Omaha"). The following metrics
+// are reported:
+//
+// |kMetricCheckResult|
+// |kMetricCheckReaction|
+// |kMetricCheckDownloadErrorCode|
+// |kMetricCheckTimeSinceLastCheckMinutes|
+// |kMetricCheckTimeSinceLastCheckUptimeMinutes|
+//
+// The |kMetricCheckResult| metric will only be reported if |result|
+// is not |kUnset|.
+//
+// The |kMetricCheckReaction| metric will only be reported if
+// |reaction| is not |kUnset|.
+//
+// The |kMetricCheckDownloadErrorCode| will only be reported if
+// |download_error_code| is not |kUnset|.
+//
+// The values for the |kMetricCheckTimeSinceLastCheckMinutes| and
+// |kMetricCheckTimeSinceLastCheckUptimeMinutes| metrics are
+// automatically reported and calculated by maintaining persistent
+// and process-local state variables.
+void ReportUpdateCheckMetrics(SystemState *system_state,
+ CheckResult result,
+ CheckReaction reaction,
+ DownloadErrorCode download_error_code);
+
+
+// Helper function to report metrics after the completion of each
+// update attempt. The following metrics are reported:
+//
+// |kMetricAttemptNumber|
+// |kMetricAttemptPayloadType|
+// |kMetricAttemptPayloadSizeMiB|
+// |kMetricAttemptDurationSeconds|
+// |kMetricAttemptDurationUptimeSeconds|
+// |kMetricAttemptTimeSinceLastAttemptMinutes|
+// |kMetricAttemptTimeSinceLastAttemptUptimeMinutes|
+// |kMetricAttemptPayloadBytesDownloadedMiB|
+// |kMetricAttemptPayloadDownloadSpeedKBps|
+// |kMetricAttemptDownloadSource|
+// |kMetricAttemptResult|
+// |kMetricAttemptInternalErrorCode|
+// |kMetricAttemptDownloadErrorCode|
+//
+// The |kMetricAttemptInternalErrorCode| metric will only be reported
+// if |internal_error_code| is not |kErrorSuccess|.
+//
+// The |kMetricAttemptDownloadErrorCode| metric will only be
+// reported if |payload_download_error_code| is not |kUnset|.
+//
+// The values for the |kMetricAttemptTimeSinceLastAttemptMinutes| and
+// |kMetricAttemptTimeSinceLastAttemptUptimeMinutes| metrics are
+// automatically calculated and reported by maintaining persistent and
+// process-local state variables.
+void ReportUpdateAttemptMetrics(
+ SystemState *system_state,
+ int attempt_number,
+ PayloadType payload_type,
+ base::TimeDelta duration,
+ base::TimeDelta duration_uptime,
+ int64_t payload_size,
+ int64_t payload_bytes_downloaded,
+ int64_t payload_download_speed_bps,
+ DownloadSource download_source,
+ AttemptResult attempt_result,
+ ErrorCode internal_error_code,
+ DownloadErrorCode payload_download_error_code);
+
+// Helper function to report the after the completion of a successful
+// update attempt. The following metrics are reported:
+//
+// |kMetricSuccessfulUpdateAttemptCount|
+// |kMetricSuccessfulUpdateUpdatesAbandonedCount|
+// |kMetricSuccessfulUpdatePayloadType|
+// |kMetricSuccessfulUpdatePayloadSizeMiB|
+// |kMetricSuccessfulUpdateBytesDownloadedMiBHttpsServer|
+// |kMetricSuccessfulUpdateBytesDownloadedMiBHttpServer|
+// |kMetricSuccessfulUpdateBytesDownloadedMiBHttpPeer|
+// |kMetricSuccessfulUpdateBytesDownloadedMiB|
+// |kMetricSuccessfulUpdateDownloadSourcesUsed|
+// |kMetricSuccessfulUpdateDownloadOverheadPercentage|
+// |kMetricSuccessfulUpdateTotalDurationMinutes|
+// |kMetricSuccessfulUpdateRebootCount|
+// |kMetricSuccessfulUpdateUrlSwitchCount|
+//
+// The values for the |kMetricSuccessfulUpdateDownloadSourcesUsed| are
+// |kMetricSuccessfulUpdateBytesDownloadedMiB| metrics automatically
+// calculated from examining the |num_bytes_downloaded| array.
+void ReportSuccessfulUpdateMetrics(
+ SystemState *system_state,
+ int attempt_count,
+ int updates_abandoned_count,
+ PayloadType payload_type,
+ int64_t payload_size,
+ int64_t num_bytes_downloaded[kNumDownloadSources],
+ int download_overhead_percentage,
+ base::TimeDelta total_duration,
+ int reboot_count,
+ int url_switch_count);
+
+} // namespace metrics
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_METRICS_H_
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index c706a16..8e372b5 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -1287,10 +1287,77 @@
string metric_name = "Installer.InstallDateProvisioningSource";
system_state->metrics_lib()->SendEnumToUMA(
metric_name,
- static_cast<int>(source), // Sample.
- kProvisionedMax); // Maximum.
+ static_cast<int>(source), // Sample.
+ kProvisionedMax); // Maximum.
+
+ metric_name = metrics::kMetricInstallDateProvisioningSource;
+ system_state->metrics_lib()->SendEnumToUMA(
+ metric_name,
+ static_cast<int>(source), // Sample.
+ kProvisionedMax); // Maximum.
return true;
}
+void OmahaRequestAction::ActionCompleted(ErrorCode code) {
+ // We only want to report this on "update check".
+ if (ping_only_ || event_ != nullptr)
+ return;
+
+ metrics::CheckResult result = metrics::CheckResult::kUnset;
+ metrics::CheckReaction reaction = metrics::CheckReaction::kUnset;
+ metrics::DownloadErrorCode download_error_code =
+ metrics::DownloadErrorCode::kUnset;
+
+ // Regular update attempt.
+ switch (code) {
+ case kErrorCodeSuccess:
+ // OK, we parsed the response successfully but that does
+ // necessarily mean that an update is available.
+ if (HasOutputPipe()) {
+ const OmahaResponse& response = GetOutputObject();
+ if (response.update_exists) {
+ result = metrics::CheckResult::kUpdateAvailable;
+ reaction = metrics::CheckReaction::kUpdating;
+ } else {
+ result = metrics::CheckResult::kNoUpdateAvailable;
+ }
+ } else {
+ result = metrics::CheckResult::kNoUpdateAvailable;
+ }
+ break;
+
+ case kErrorCodeOmahaUpdateIgnoredPerPolicy:
+ result = metrics::CheckResult::kUpdateAvailable;
+ reaction = metrics::CheckReaction::kIgnored;
+ break;
+
+ case kErrorCodeOmahaUpdateDeferredPerPolicy:
+ result = metrics::CheckResult::kUpdateAvailable;
+ reaction = metrics::CheckReaction::kDeferring;
+ break;
+
+ case kErrorCodeOmahaUpdateDeferredForBackoff:
+ result = metrics::CheckResult::kUpdateAvailable;
+ reaction = metrics::CheckReaction::kBackingOff;
+ break;
+
+ default:
+ // We report two flavors of errors, "Download errors" and "Parsing
+ // error". Try to convert to the former and if that doesn't work
+ // we know it's the latter.
+ metrics::DownloadErrorCode tmp_error = utils::GetDownloadErrorCode(code);
+ if (tmp_error != metrics::DownloadErrorCode::kInputMalformed) {
+ result = metrics::CheckResult::kDownloadError;
+ download_error_code = tmp_error;
+ } else {
+ result = metrics::CheckResult::kParsingError;
+ }
+ break;
+ }
+
+ metrics::ReportUpdateCheckMetrics(system_state_,
+ result, reaction, download_error_code);
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 4b8b9f3..1eba719 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -127,6 +127,7 @@
typedef ActionTraits<OmahaRequestAction>::OutputObjectType OutputObjectType;
void PerformAction();
void TerminateProcessing();
+ void ActionCompleted(ErrorCode code);
int GetHTTPResponseCode() { return http_fetcher_->http_response_code(); }
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 5628b39..fdfb590 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -34,6 +34,7 @@
using testing::NiceMock;
using testing::Return;
using testing::SetArgumentPointee;
+using testing::AnyNumber;
namespace chromeos_update_engine {
@@ -215,6 +216,12 @@
// the transfer will fail with that code. |ping_only| is passed through to the
// OmahaRequestAction constructor. out_post_data may be null; if non-null, the
// post-data received by the mock HttpFetcher is returned.
+//
+// The |expected_check_result|, |expected_check_reaction| and
+// |expected_error_code| parameters are for checking expectations
+// about reporting UpdateEngine.Check.{Result,Reaction,DownloadError}
+// UMA statistics. Use the appropriate ::kUnset value to specify that
+// the given metric should not be reported.
bool TestUpdateCheck(PrefsInterface* prefs,
PayloadStateInterface *payload_state,
P2PManager *p2p_manager,
@@ -223,6 +230,9 @@
int fail_http_response_code,
bool ping_only,
ErrorCode expected_code,
+ metrics::CheckResult expected_check_result,
+ metrics::CheckReaction expected_check_reaction,
+ metrics::DownloadErrorCode expected_download_error_code,
OmahaResponse* out_response,
vector<char>* out_post_data) {
GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
@@ -256,6 +266,25 @@
BondActions(&action, &collector_action);
processor.EnqueueAction(&collector_action);
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(),
+ SendEnumToUMA(metrics::kMetricCheckResult,
+ static_cast<int>(expected_check_result),
+ static_cast<int>(metrics::CheckResult::kNumConstants) - 1))
+ .Times(expected_check_result == metrics::CheckResult::kUnset ? 0 : 1);
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(),
+ SendEnumToUMA(metrics::kMetricCheckReaction,
+ static_cast<int>(expected_check_reaction),
+ static_cast<int>(metrics::CheckReaction::kNumConstants) - 1))
+ .Times(expected_check_reaction == metrics::CheckReaction::kUnset ? 0 : 1);
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(),
+ SendEnumToUMA(metrics::kMetricCheckDownloadErrorCode,
+ static_cast<int>(expected_download_error_code),
+ static_cast<int>(metrics::DownloadErrorCode::kNumConstants) - 1))
+ .Times(expected_download_error_code == metrics::DownloadErrorCode::kUnset
+ ? 0 : 1);
+
g_timeout_add(0, &StartProcessorInRunLoop, &processor);
g_main_loop_run(loop);
g_main_loop_unref(loop);
@@ -304,6 +333,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -329,6 +361,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -364,6 +399,9 @@
-1,
false, // ping_only
kErrorCodeOmahaUpdateIgnoredPerPolicy,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kIgnored,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -382,6 +420,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -425,6 +466,9 @@
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kDeferring,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -453,6 +497,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -499,6 +546,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -545,6 +595,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -592,6 +645,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
@@ -642,6 +698,9 @@
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kDeferring,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
@@ -674,6 +733,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -722,6 +784,9 @@
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kDeferring,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
@@ -756,6 +821,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -797,6 +865,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -813,6 +884,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestEmptyResponseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -833,6 +907,9 @@
-1,
false, // ping_only
kErrorCodeOmahaResponseInvalid,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -853,6 +930,9 @@
-1,
false, // ping_only
kErrorCodeOmahaResponseInvalid,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -873,6 +953,9 @@
-1,
false, // ping_only
kErrorCodeOmahaResponseInvalid,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -907,6 +990,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -1001,6 +1087,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
&response,
&post_data));
// convert post_data to string
@@ -1035,6 +1124,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
@@ -1064,6 +1156,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
@@ -1084,6 +1179,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL, // response
&post_data));
// convert post_data to string
@@ -1117,6 +1215,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL, // response
&post_data));
// convert post_data to string
@@ -1229,6 +1330,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
// convert post_data to string
@@ -1273,6 +1377,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
// convert post_data to string
@@ -1306,6 +1413,9 @@
TEST(OmahaRequestActionTest, PingTest) {
for (int ping_only = 0; ping_only < 2; ping_only++) {
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
// Add a few hours to the day difference to test no rounding, etc.
int64_t five_days_ago =
(Time::Now() - TimeDelta::FromHours(5 * 24 + 13)).ToInternalValue();
@@ -1327,6 +1437,9 @@
-1,
ping_only,
kErrorCodeSuccess,
+ metrics::CheckResult::kUnset,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
@@ -1344,6 +1457,9 @@
TEST(OmahaRequestActionTest, ActivePingTest) {
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
int64_t three_days_ago =
(Time::Now() - TimeDelta::FromHours(3 * 24 + 12)).ToInternalValue();
int64_t now = Time::Now().ToInternalValue();
@@ -1363,6 +1479,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
@@ -1372,6 +1491,9 @@
TEST(OmahaRequestActionTest, RollCallPingTest) {
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
int64_t four_days_ago =
(Time::Now() - TimeDelta::FromHours(4 * 24)).ToInternalValue();
int64_t now = Time::Now().ToInternalValue();
@@ -1391,6 +1513,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
@@ -1400,6 +1525,9 @@
TEST(OmahaRequestActionTest, NoPingTest) {
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
int64_t one_hour_ago =
(Time::Now() - TimeDelta::FromHours(1)).ToInternalValue();
EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
@@ -1420,6 +1548,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
@@ -1446,6 +1577,9 @@
-1,
true, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUnset,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
EXPECT_EQ(post_data.size(), 0);
@@ -1453,6 +1587,9 @@
TEST(OmahaRequestActionTest, BackInTimePingTest) {
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(kPrefsMetricsCheckLastReportingTime, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
int64_t future =
(Time::Now() + TimeDelta::FromHours(3 * 24 + 4)).ToInternalValue();
EXPECT_CALL(prefs, GetInt64(kPrefsInstallDateDays, _))
@@ -1478,6 +1615,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
&post_data));
string post_str(&post_data[0], post_data.size());
@@ -1494,6 +1634,8 @@
int64_t midnight_slack =
(Time::Now() - TimeDelta::FromSeconds(195)).ToInternalValue();
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay,
AllOf(Ge(midnight), Le(midnight_slack))))
.WillOnce(Return(true));
@@ -1512,12 +1654,17 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
NULL));
}
TEST(OmahaRequestActionTest, NoElapsedSecondsTest) {
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
ASSERT_TRUE(
@@ -1532,12 +1679,17 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
NULL));
}
TEST(OmahaRequestActionTest, BadElapsedSecondsTest) {
NiceMock<PrefsMock> prefs;
+ EXPECT_CALL(prefs, GetInt64(_, _)).Times(AnyNumber());
+ EXPECT_CALL(prefs, SetInt64(_, _)).Times(AnyNumber());
EXPECT_CALL(prefs, SetInt64(kPrefsLastActivePingDay, _)).Times(0);
EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
ASSERT_TRUE(
@@ -1552,6 +1704,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL,
NULL));
}
@@ -1566,6 +1721,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL, // response
&post_data));
// convert post_data to string
@@ -1586,6 +1744,9 @@
false, // ping_only
static_cast<ErrorCode>(
kErrorCodeOmahaRequestHTTPResponseBase + 501),
+ metrics::CheckResult::kDownloadError,
+ metrics::CheckReaction::kUnset,
+ static_cast<metrics::DownloadErrorCode>(501),
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -1603,6 +1764,9 @@
false, // ping_only
static_cast<ErrorCode>(
kErrorCodeOmahaRequestHTTPResponseBase + 999),
+ metrics::CheckResult::kDownloadError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kHttpStatusOther,
&response,
NULL));
EXPECT_FALSE(response.update_exists);
@@ -1646,6 +1810,9 @@
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kDeferring,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
@@ -1678,6 +1845,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -1726,6 +1896,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
@@ -1773,6 +1946,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL, // response
&post_data));
// convert post_data to string
@@ -1820,6 +1996,9 @@
-1,
false, // ping_only
kErrorCodeOmahaRequestXMLParseError,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
NULL, // response
&post_data));
// convert post_data to string
@@ -1880,6 +2059,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
&response,
NULL));
EXPECT_TRUE(response.update_exists);
@@ -2001,6 +2183,9 @@
-1,
false, // ping_only
kErrorCodeSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
response,
NULL);
}
diff --git a/payload_state.cc b/payload_state.cc
index 642264d..779f708 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -154,28 +154,34 @@
SetUrlFailureCount(0);
}
+void PayloadState::AttemptStarted() {
+ ClockInterface *clock = system_state_->clock();
+ attempt_start_time_boot_ = clock->GetBootTime();
+ attempt_start_time_monotonic_ = clock->GetMonotonicTime();
+
+ attempt_num_bytes_downloaded_ = 0;
+}
+
void PayloadState::UpdateResumed() {
LOG(INFO) << "Resuming an update that was previously started.";
UpdateNumReboots();
+ AttemptStarted();
}
void PayloadState::UpdateRestarted() {
LOG(INFO) << "Starting a new update";
ResetDownloadSourcesOnNewUpdate();
SetNumReboots(0);
+ AttemptStarted();
}
void PayloadState::UpdateSucceeded() {
// Send the relevant metrics that are tracked in this class to UMA.
CalculateUpdateDurationUptime();
SetUpdateTimestampEnd(system_state_->clock()->GetWallclockTime());
- ReportBytesDownloadedMetrics();
- ReportUpdateUrlSwitchesMetric();
- ReportRebootMetrics();
- ReportDurationMetrics();
- ReportUpdatesAbandonedCountMetric();
- ReportPayloadTypeMetric();
- ReportAttemptsCountMetrics();
+
+ CollectAndReportAttemptMetrics(kErrorCodeSuccess);
+ CollectAndReportSuccessfulUpdateMetrics();
// Reset the number of responses seen since it counts from the last
// successful update, e.g. now.
@@ -197,6 +203,8 @@
return;
}
+ CollectAndReportAttemptMetrics(base_error);
+
switch (base_error) {
// Errors which are good indicators of a problem with a particular URL or
// the protocol used in the URL or entities in the communication channel
@@ -486,20 +494,112 @@
current_download_source_,
GetTotalBytesDownloaded(current_download_source_) + count,
false);
+
+ attempt_num_bytes_downloaded_ += count;
}
-void PayloadState::ReportBytesDownloadedMetrics() {
- // Report metrics collected from all known download sources to UMA.
- // The reported data is in Megabytes in order to represent a larger
- // sample range.
- int download_sources_used = 0;
+PayloadType PayloadState::CalculatePayloadType() {
+ PayloadType payload_type;
+ OmahaRequestParams* params = system_state_->request_params();
+ if (response_.is_delta_payload) {
+ payload_type = kPayloadTypeDelta;
+ } else if (params->delta_okay()) {
+ payload_type = kPayloadTypeFull;
+ } else { // Full payload, delta was not allowed by request.
+ payload_type = kPayloadTypeForcedFull;
+ }
+ return payload_type;
+}
+
+// TODO(zeuthen): Currently we don't report the UpdateEngine.Attempt.*
+// metrics if the attempt ends abnormally, e.g. if the update_engine
+// process crashes or the device is rebooted. See
+// http://crbug.com/357676
+void PayloadState::CollectAndReportAttemptMetrics(ErrorCode code) {
+ int attempt_number = GetPayloadAttemptNumber();
+
+ PayloadType payload_type = CalculatePayloadType();
+
+ int64_t payload_size = response_.size;
+
+ int64_t payload_bytes_downloaded = attempt_num_bytes_downloaded_;
+
+ ClockInterface *clock = system_state_->clock();
+ base::TimeDelta duration = clock->GetBootTime() - attempt_start_time_boot_;
+ base::TimeDelta duration_uptime = clock->GetMonotonicTime() -
+ attempt_start_time_monotonic_;
+
+ int64_t payload_download_speed_bps = 0;
+ int64_t usec = duration_uptime.InMicroseconds();
+ if (usec > 0) {
+ double sec = static_cast<double>(usec) / Time::kMicrosecondsPerSecond;
+ double bps = static_cast<double>(payload_bytes_downloaded) / sec;
+ payload_download_speed_bps = static_cast<int64_t>(bps);
+ }
+
+ DownloadSource download_source = current_download_source_;
+
+ metrics::DownloadErrorCode payload_download_error_code =
+ metrics::DownloadErrorCode::kUnset;
+ ErrorCode internal_error_code = kErrorCodeSuccess;
+ metrics::AttemptResult attempt_result = utils::GetAttemptResult(code);
+
+ // Add additional detail to AttemptResult
+ switch (attempt_result) {
+ case metrics::AttemptResult::kPayloadDownloadError:
+ payload_download_error_code = utils::GetDownloadErrorCode(code);
+ break;
+
+ case metrics::AttemptResult::kInternalError:
+ internal_error_code = code;
+ break;
+
+ // Explicit fall-through for cases where we do not have additional
+ // detail. We avoid the default keyword to force people adding new
+ // AttemptResult values to visit this code and examine whether
+ // additional detail is needed.
+ case metrics::AttemptResult::kUpdateSucceeded:
+ case metrics::AttemptResult::kMetadataMalformed:
+ case metrics::AttemptResult::kOperationMalformed:
+ case metrics::AttemptResult::kOperationExecutionError:
+ case metrics::AttemptResult::kMetadataVerificationFailed:
+ case metrics::AttemptResult::kPayloadVerificationFailed:
+ case metrics::AttemptResult::kVerificationFailed:
+ case metrics::AttemptResult::kPostInstallFailed:
+ case metrics::AttemptResult::kAbnormalTermination:
+ case metrics::AttemptResult::kNumConstants:
+ case metrics::AttemptResult::kUnset:
+ break;
+ }
+
+ metrics::ReportUpdateAttemptMetrics(system_state_,
+ attempt_number,
+ payload_type,
+ duration,
+ duration_uptime,
+ payload_size,
+ payload_bytes_downloaded,
+ payload_download_speed_bps,
+ download_source,
+ attempt_result,
+ internal_error_code,
+ payload_download_error_code);
+}
+
+void PayloadState::CollectAndReportSuccessfulUpdateMetrics() {
string metric;
- uint64_t successful_mbs = 0;
- uint64_t total_mbs = 0;
+
+ // Report metrics collected from all known download sources to UMA.
+ int64_t successful_bytes_by_source[kNumDownloadSources];
+ int64_t total_bytes_by_source[kNumDownloadSources];
+ int64_t successful_bytes = 0;
+ int64_t total_bytes = 0;
+ int64_t successful_mbs = 0;
+ int64_t total_mbs = 0;
+
for (int i = 0; i < kNumDownloadSources; i++) {
DownloadSource source = static_cast<DownloadSource>(i);
- const int kMaxMiBs = 10240; // Anything above 10GB goes in the last bucket.
- uint64_t mbs;
+ int64_t bytes;
// Only consider this download source (and send byte counts) as
// having been used if we downloaded a non-trivial amount of bytes
@@ -507,26 +607,84 @@
// the update. Otherwise we're going to end up with a lot of
// zero-byte events in the histogram.
- mbs = GetCurrentBytesDownloaded(source) / kNumBytesInOneMiB;
- if (mbs > 0) {
- download_sources_used |= (1 << source);
-
- metric = "Installer.SuccessfulMBsDownloadedFrom" +
- utils::ToString(source);
- successful_mbs += mbs;
- LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(metric,
- mbs,
- 0, // min
- kMaxMiBs,
- kNumDefaultUmaBuckets);
- }
+ bytes = GetCurrentBytesDownloaded(source);
+ successful_bytes_by_source[i] = bytes;
+ successful_bytes += bytes;
+ successful_mbs += bytes / kNumBytesInOneMiB;
SetCurrentBytesDownloaded(source, 0, true);
- mbs = GetTotalBytesDownloaded(source) / kNumBytesInOneMiB;
+ bytes = GetTotalBytesDownloaded(source);
+ total_bytes_by_source[i] = bytes;
+ total_bytes += bytes;
+ total_mbs += bytes / kNumBytesInOneMiB;
+ SetTotalBytesDownloaded(source, 0, true);
+ }
+
+ int download_overhead_percentage = 0;
+ if (successful_bytes > 0) {
+ download_overhead_percentage = (total_bytes - successful_bytes) * 100ULL /
+ successful_bytes;
+ }
+
+ int url_switch_count = static_cast<int>(url_switch_count_);
+
+ int reboot_count = GetNumReboots();
+
+ SetNumReboots(0);
+
+ TimeDelta duration = GetUpdateDuration();
+ TimeDelta duration_uptime = GetUpdateDurationUptime();
+
+ prefs_->Delete(kPrefsUpdateTimestampStart);
+ prefs_->Delete(kPrefsUpdateDurationUptime);
+
+ PayloadType payload_type = CalculatePayloadType();
+
+ int64_t payload_size = response_.size;
+
+ int attempt_count = GetPayloadAttemptNumber();
+
+ int updates_abandoned_count = num_responses_seen_ - 1;
+
+ metrics::ReportSuccessfulUpdateMetrics(system_state_,
+ attempt_count,
+ updates_abandoned_count,
+ payload_type,
+ payload_size,
+ total_bytes_by_source,
+ download_overhead_percentage,
+ duration,
+ reboot_count,
+ url_switch_count);
+
+ // TODO(zeuthen): This is the old metric reporting code which is
+ // slated for removal soon. See http://crbug.com/355745 for details.
+
+ // The old metrics code is using MiB's instead of bytes to calculate
+ // the overhead which due to rounding makes the numbers slightly
+ // different.
+ download_overhead_percentage = 0;
+ if (successful_mbs > 0) {
+ download_overhead_percentage = (total_mbs - successful_mbs) * 100ULL /
+ successful_mbs;
+ }
+
+ int download_sources_used = 0;
+ for (int i = 0; i < kNumDownloadSources; i++) {
+ DownloadSource source = static_cast<DownloadSource>(i);
+ const int kMaxMiBs = 10240; // Anything above 10GB goes in the last bucket.
+ int64_t mbs;
+
+ // Only consider this download source (and send byte counts) as
+ // having been used if we downloaded a non-trivial amount of bytes
+ // (e.g. at least 1 MiB) that contributed to the final success of
+ // the update. Otherwise we're going to end up with a lot of
+ // zero-byte events in the histogram.
+
+ mbs = successful_bytes_by_source[i] / kNumBytesInOneMiB;
if (mbs > 0) {
- metric = "Installer.TotalMBsDownloadedFrom" + utils::ToString(source);
- total_mbs += mbs;
+ metric = "Installer.SuccessfulMBsDownloadedFrom" +
+ utils::ToString(source);
LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
system_state_->metrics_lib()->SendToUMA(metric,
mbs,
@@ -534,7 +692,18 @@
kMaxMiBs,
kNumDefaultUmaBuckets);
}
- SetTotalBytesDownloaded(source, 0, true);
+
+ mbs = total_bytes_by_source[i] / kNumBytesInOneMiB;
+ if (mbs > 0) {
+ metric = "Installer.TotalMBsDownloadedFrom" + utils::ToString(source);
+ LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
+ system_state_->metrics_lib()->SendToUMA(metric,
+ mbs,
+ 0, // min
+ kMaxMiBs,
+ kNumDefaultUmaBuckets);
+ download_sources_used |= (1 << i);
+ }
}
metric = "Installer.DownloadSourcesUsed";
@@ -547,44 +716,82 @@
1 << kNumDownloadSources,
num_buckets);
- if (successful_mbs) {
- metric = "Installer.DownloadOverheadPercentage";
- int percent_overhead = (total_mbs - successful_mbs) * 100 / successful_mbs;
- LOG(INFO) << "Uploading " << percent_overhead << "% for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(metric,
- percent_overhead,
- 0, // min: 0% overhead
- 1000, // max: 1000% overhead
- kNumDefaultUmaBuckets);
- }
-}
+ metric = "Installer.DownloadOverheadPercentage";
+ LOG(INFO) << "Uploading " << download_overhead_percentage
+ << "% for metric " << metric;
+ system_state_->metrics_lib()->SendToUMA(metric,
+ download_overhead_percentage,
+ 0, // min: 0% overhead
+ 1000, // max: 1000% overhead
+ kNumDefaultUmaBuckets);
-void PayloadState::ReportUpdateUrlSwitchesMetric() {
- string metric = "Installer.UpdateURLSwitches";
- int value = static_cast<int>(url_switch_count_);
-
- LOG(INFO) << "Uploading " << value << " (count) for metric " << metric;
+ metric = "Installer.UpdateURLSwitches";
+ LOG(INFO) << "Uploading " << url_switch_count
+ << " (count) for metric " << metric;
system_state_->metrics_lib()->SendToUMA(
metric,
- value,
+ url_switch_count,
0, // min value
100, // max value
kNumDefaultUmaBuckets);
-}
-void PayloadState::ReportRebootMetrics() {
- // Report the number of num_reboots.
- string metric = "Installer.UpdateNumReboots";
- uint32_t num_reboots = GetNumReboots();
- LOG(INFO) << "Uploading reboot count of " << num_reboots << " for metric "
+ metric = "Installer.UpdateNumReboots";
+ LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
<< metric;
system_state_->metrics_lib()->SendToUMA(
metric,
- static_cast<int>(num_reboots), // sample
- 0, // min = 0.
- 50, // max
+ reboot_count, // sample
+ 0, // min = 0.
+ 50, // max
25); // buckets
- SetNumReboots(0);
+
+ metric = "Installer.UpdateDurationMinutes";
+ system_state_->metrics_lib()->SendToUMA(
+ metric,
+ static_cast<int>(duration.InMinutes()),
+ 1, // min: 1 minute
+ 365*24*60, // max: 1 year (approx)
+ kNumDefaultUmaBuckets);
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
+ << " for metric " << metric;
+
+ metric = "Installer.UpdateDurationUptimeMinutes";
+ system_state_->metrics_lib()->SendToUMA(
+ metric,
+ static_cast<int>(duration_uptime.InMinutes()),
+ 1, // min: 1 minute
+ 30*24*60, // max: 1 month (approx)
+ kNumDefaultUmaBuckets);
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
+ << " for metric " << metric;
+
+ metric = "Installer.PayloadFormat";
+ system_state_->metrics_lib()->SendEnumToUMA(
+ metric,
+ payload_type,
+ kNumPayloadTypes);
+ LOG(INFO) << "Uploading " << utils::ToString(payload_type)
+ << " for metric " << metric;
+
+ metric = "Installer.AttemptsCount.Total";
+ system_state_->metrics_lib()->SendToUMA(
+ metric,
+ attempt_count,
+ 1, // min
+ 50, // max
+ kNumDefaultUmaBuckets);
+ LOG(INFO) << "Uploading " << attempt_count
+ << " for metric " << metric;
+
+ metric = "Installer.UpdatesAbandonedCount";
+ LOG(INFO) << "Uploading " << updates_abandoned_count
+ << " (count) for metric " << metric;
+ system_state_->metrics_lib()->SendToUMA(
+ metric,
+ updates_abandoned_count,
+ 0, // min value
+ 100, // max value
+ kNumDefaultUmaBuckets);
}
void PayloadState::UpdateNumReboots() {
@@ -930,72 +1137,6 @@
SetUpdateDurationUptimeExtended(new_uptime, now, false);
}
-void PayloadState::ReportDurationMetrics() {
- TimeDelta duration = GetUpdateDuration();
- TimeDelta duration_uptime = GetUpdateDurationUptime();
- string metric;
-
- metric = "Installer.UpdateDurationMinutes";
- system_state_->metrics_lib()->SendToUMA(
- metric,
- static_cast<int>(duration.InMinutes()),
- 1, // min: 1 minute
- 365*24*60, // max: 1 year (approx)
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
- << " for metric " << metric;
-
- metric = "Installer.UpdateDurationUptimeMinutes";
- system_state_->metrics_lib()->SendToUMA(
- metric,
- static_cast<int>(duration_uptime.InMinutes()),
- 1, // min: 1 minute
- 30*24*60, // max: 1 month (approx)
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
- << " for metric " << metric;
-
- prefs_->Delete(kPrefsUpdateTimestampStart);
- prefs_->Delete(kPrefsUpdateDurationUptime);
-}
-
-void PayloadState::ReportPayloadTypeMetric() {
- string metric;
- PayloadType uma_payload_type;
- OmahaRequestParams* params = system_state_->request_params();
-
- if (response_.is_delta_payload) {
- uma_payload_type = kPayloadTypeDelta;
- } else if (params->delta_okay()) {
- uma_payload_type = kPayloadTypeFull;
- } else { // Full payload, delta was not allowed by request.
- uma_payload_type = kPayloadTypeForcedFull;
- }
-
- metric = "Installer.PayloadFormat";
- system_state_->metrics_lib()->SendEnumToUMA(
- metric,
- uma_payload_type,
- kNumPayloadTypes);
- LOG(INFO) << "Uploading " << utils::ToString(uma_payload_type)
- << " for metric " << metric;
-}
-
-void PayloadState::ReportAttemptsCountMetrics() {
- string metric;
- int total_attempts = GetPayloadAttemptNumber();
-
- metric = "Installer.AttemptsCount.Total";
- system_state_->metrics_lib()->SendToUMA(
- metric,
- total_attempts,
- 1, // min
- 50, // max
- kNumDefaultUmaBuckets);
- LOG(INFO) << "Uploading " << total_attempts
- << " for metric " << metric;
-}
-
string PayloadState::GetPrefsKey(const string& prefix, DownloadSource source) {
return prefix + "-from-" + utils::ToString(source);
}
@@ -1060,19 +1201,6 @@
prefs_->SetInt64(kPrefsNumResponsesSeen, num_responses_seen_);
}
-void PayloadState::ReportUpdatesAbandonedCountMetric() {
- string metric = "Installer.UpdatesAbandonedCount";
- int value = num_responses_seen_ - 1;
-
- LOG(INFO) << "Uploading " << value << " (count) for metric " << metric;
- system_state_->metrics_lib()->SendToUMA(
- metric,
- value,
- 0, // min value
- 100, // max value
- kNumDefaultUmaBuckets);
-}
-
void PayloadState::ReportUpdatesAbandonedEventCountMetric() {
string metric = "Installer.UpdatesAbandonedEventCount";
int value = num_responses_seen_ - 1;
@@ -1133,6 +1261,15 @@
kNumDefaultUmaBuckets);
LOG(INFO) << "Uploading " << utils::FormatTimeDelta(time_to_reboot)
<< " for metric " << metric;
+
+ metric = metrics::kMetricTimeToRebootMinutes;
+ system_state_->metrics_lib()->SendToUMA(metric,
+ time_to_reboot.InMinutes(),
+ 0, // min: 0 minute
+ 30*24*60, // max: 1 month (approx)
+ kNumDefaultUmaBuckets);
+ LOG(INFO) << "Uploading " << utils::FormatTimeDelta(time_to_reboot)
+ << " for metric " << metric;
}
void PayloadState::UpdateEngineStarted() {
@@ -1196,6 +1333,16 @@
1, // min value
50, // max value
kNumDefaultUmaBuckets);
+
+ metric = metrics::kMetricFailedUpdateCount;
+ LOG(INFO) << "Uploading " << target_attempt
+ << " (count) for metric " << metric;
+ system_state_->metrics_lib()->SendToUMA(
+ metric,
+ target_attempt,
+ 1, // min value
+ 50, // max value
+ kNumDefaultUmaBuckets);
} else {
prefs_->Delete(kPrefsTargetVersionAttempt);
prefs_->Delete(kPrefsTargetVersionUniqueId);
diff --git a/payload_state.h b/payload_state.h
index bb47f1a..1035404 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -117,6 +117,10 @@
FRIEND_TEST(PayloadStateTest, RollbackVersion);
FRIEND_TEST(PayloadStateTest, UpdateSuccessWithWipedPrefs);
+ // Helper called when an attempt has begun, is called by
+ // UpdateResumed() and UpdateRestarted().
+ void AttemptStarted();
+
// Increments the payload attempt number used for metrics.
void IncrementPayloadAttemptNumber();
@@ -149,23 +153,14 @@
// that were downloaded recently.
void UpdateBytesDownloaded(size_t count);
- // Reports the various metrics related to the number of bytes downloaded.
- void ReportBytesDownloadedMetrics();
+ // Calculates the PayloadType we're using.
+ PayloadType CalculatePayloadType();
- // Reports the metric related to number of URL switches.
- void ReportUpdateUrlSwitchesMetric();
+ // Collects and reports the various metrics related to an update attempt.
+ void CollectAndReportAttemptMetrics(ErrorCode code);
- // Reports the various metrics related to rebooting during an update.
- void ReportRebootMetrics();
-
- // Reports the various metrics related to update duration.
- void ReportDurationMetrics();
-
- // Reports the metric related to the applied payload type.
- void ReportPayloadTypeMetric();
-
- // Reports the various metrics related to update attempts counts.
- void ReportAttemptsCountMetrics();
+ // Collects and reports the various metrics related to a successful update.
+ void CollectAndReportSuccessfulUpdateMetrics();
// Checks if we were expecting to be running in the new version but the
// boot into the new version failed for some reason. If that's the case, an
@@ -327,11 +322,6 @@
// Initializes |num_responses_seen_| from persisted state.
void LoadNumResponsesSeen();
- // Reports metric conveying how many times updates were abandoned
- // before an update was applied. This metric is reported when an update is
- // successfully applied.
- void ReportUpdatesAbandonedCountMetric();
-
// Reports metric conveying how many times updates were abandoned since
// the last update was applied. The difference between this metric and the
// previous ReportUpdatesAbandonedCountMetric() one is that this metric is
@@ -503,6 +493,15 @@
// The cached value of |kPrefsP2PNumAttempts|.
int p2p_num_attempts_;
+ // The number of bytes downloaded per attempt.
+ int64_t attempt_num_bytes_downloaded_;
+
+ // The boot time when the attempt was started.
+ base::Time attempt_start_time_boot_;
+
+ // The monotonic time when the attempt was started.
+ base::Time attempt_start_time_monotonic_;
+
DISALLOW_COPY_AND_ASSIGN(PayloadState);
};
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index f804599..a6d6d83 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -307,6 +307,11 @@
EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+ .Times(AnyNumber());
+
// The first response doesn't send an abandoned event.
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.UpdatesAbandonedEventCount", 0, _, _, _)).Times(0);
@@ -881,6 +886,8 @@
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(_, _, _, _, _))
.Times(AnyNumber());
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+ .Times(AnyNumber());
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.SuccessfulMBsDownloadedFromHttpServer",
http_chunk / kNumBytesInOneMiB, _, _, _));
@@ -903,9 +910,15 @@
"Installer.UpdateURLSwitches",
3, _, _, _));
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricSuccessfulUpdateUrlSwitchCount,
+ 3, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.UpdateDurationMinutes",
_, _, _, _));
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricSuccessfulUpdateTotalDurationMinutes,
+ _, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.UpdateDurationUptimeMinutes",
_, _, _, _));
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
@@ -915,10 +928,20 @@
_, _, _));
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.DownloadOverheadPercentage", 318, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage,
+ 314, _, _, _));
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
"Installer.PayloadFormat", kPayloadTypeFull, kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricAttemptPayloadType, kPayloadTypeFull, kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
+ kNumPayloadTypes));
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.AttemptsCount.Total", 1, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricSuccessfulUpdateAttemptCount, 1, _, _, _));
payload_state.UpdateSucceeded();
@@ -959,6 +982,10 @@
"Installer.DownloadSourcesUsed",
(1 << kDownloadSourceHttpServer),
_, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricSuccessfulUpdateDownloadSourcesUsed,
+ (1 << kDownloadSourceHttpServer),
+ _, _, _));
payload_state.UpdateSucceeded();
}
@@ -1161,6 +1188,9 @@
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.TimeToRebootMinutes",
7, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricTimeToRebootMinutes,
+ 7, _, _, _));
EXPECT_CALL(mock_system_state, system_rebooted())
.WillRepeatedly(Return(true));
@@ -1282,8 +1312,15 @@
// Simulate a successful download and update.
payload_state.DownloadComplete();
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+ .Times(AnyNumber());
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
"Installer.PayloadFormat", kPayloadTypeDelta, kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricAttemptPayloadType, kPayloadTypeDelta, kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
+ kNumPayloadTypes));
payload_state.UpdateSucceeded();
// Mock the request to a request where the delta was disabled but Omaha sends
@@ -1299,6 +1336,12 @@
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
"Installer.PayloadFormat", kPayloadTypeDelta, kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricAttemptPayloadType, kPayloadTypeDelta,
+ kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeDelta,
+ kNumPayloadTypes));
payload_state.UpdateSucceeded();
}
@@ -1319,8 +1362,16 @@
// Simulate a successful download and update.
payload_state.DownloadComplete();
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+ .Times(AnyNumber());
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
"Installer.PayloadFormat", kPayloadTypeForcedFull, kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricAttemptPayloadType, kPayloadTypeForcedFull,
+ kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeForcedFull,
+ kNumPayloadTypes));
payload_state.UpdateSucceeded();
}
@@ -1342,8 +1393,16 @@
// Simulate a successful download and update.
payload_state.DownloadComplete();
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(_, _, _))
+ .Times(AnyNumber());
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
"Installer.PayloadFormat", kPayloadTypeFull, kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricAttemptPayloadType, kPayloadTypeFull,
+ kNumPayloadTypes));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
+ metrics::kMetricSuccessfulUpdatePayloadType, kPayloadTypeFull,
+ kNumPayloadTypes));
payload_state.UpdateSucceeded();
}
@@ -1374,6 +1433,8 @@
// Reboot into the same environment to get an UMA metric with a value of 1.
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.RebootToNewPartitionAttempt", 1, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricFailedUpdateCount, 1, _, _, _));
payload_state.ReportFailedBootIfNeeded();
Mock::VerifyAndClearExpectations(mock_system_state.mock_metrics_lib());
@@ -1383,6 +1444,8 @@
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.RebootToNewPartitionAttempt", 2, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricFailedUpdateCount, 2, _, _, _));
payload_state.ReportFailedBootIfNeeded();
Mock::VerifyAndClearExpectations(mock_system_state.mock_metrics_lib());
@@ -1391,6 +1454,8 @@
payload_state.ExpectRebootInNewVersion("Version:3141592");
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.RebootToNewPartitionAttempt", 1, _, _, _));
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricFailedUpdateCount, 1, _, _, _));
payload_state.ReportFailedBootIfNeeded();
Mock::VerifyAndClearExpectations(mock_system_state.mock_metrics_lib());
@@ -1427,6 +1492,9 @@
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.RebootToNewPartitionAttempt", _, _, _, _))
.Times(0);
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricFailedUpdateCount, _, _, _, _))
+ .Times(0);
payload_state.ReportFailedBootIfNeeded();
// A second reboot in eiher partition should not send a metric.
@@ -1461,6 +1529,9 @@
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.RebootToNewPartitionAttempt", _, _, _, _))
.Times(0);
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricFailedUpdateCount, _, _, _, _))
+ .Times(0);
// Cancel the applied update.
payload_state.ResetUpdateStatus();
@@ -1488,6 +1559,9 @@
EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
"Installer.RebootToNewPartitionAttempt", _, _, _, _))
.Times(0);
+ EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+ metrics::kMetricFailedUpdateCount, _, _, _, _))
+ .Times(0);
// Simulate a reboot in this environment.
payload_state.ReportFailedBootIfNeeded();
diff --git a/update_attempter.cc b/update_attempter.cc
index e71d104..237e43c 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -29,6 +29,7 @@
#include "update_engine/gpio_handler.h"
#include "update_engine/hardware_interface.h"
#include "update_engine/libcurl_http_fetcher.h"
+#include "update_engine/metrics.h"
#include "update_engine/multi_range_http_fetcher.h"
#include "update_engine/omaha_request_action.h"
#include "update_engine/omaha_request_params.h"
@@ -236,6 +237,8 @@
0, // min: 0 days
6*30, // max: 6 months (approx)
kNumDefaultUmaBuckets);
+
+ metrics::ReportDailyMetrics(system_state_, age);
}
void UpdateAttempter::Update(const string& app_version,
@@ -253,8 +256,15 @@
if (status_ == UPDATE_STATUS_UPDATED_NEED_REBOOT) {
// Although we have applied an update, we still want to ping Omaha
// to ensure the number of active statistics is accurate.
+ //
+ // Also convey to the UpdateEngine.Check.Result metric that we're
+ // not performing an update check because of this.
LOG(INFO) << "Not updating b/c we already updated and we're waiting for "
<< "reboot, we'll ping Omaha instead";
+ metrics::ReportUpdateCheckMetrics(system_state_,
+ metrics::CheckResult::kRebootPending,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset);
PingOmaha();
return;
}
diff --git a/utils.cc b/utils.cc
index e67abb2..e379aae 100644
--- a/utils.cc
+++ b/utils.cc
@@ -33,9 +33,11 @@
#include <glib.h>
#include <google/protobuf/stubs/common.h>
+#include "update_engine/clock_interface.h"
#include "update_engine/constants.h"
#include "update_engine/file_writer.h"
#include "update_engine/omaha_request_params.h"
+#include "update_engine/prefs_interface.h"
#include "update_engine/subprocess.h"
#include "update_engine/system_state.h"
#include "update_engine/update_attempter.h"
@@ -929,6 +931,189 @@
return base_code;
}
+metrics::AttemptResult GetAttemptResult(ErrorCode code) {
+ ErrorCode base_code = static_cast<ErrorCode>(code & ~kErrorCodeSpecialFlags);
+
+ switch (base_code) {
+ case kErrorCodeSuccess:
+ return metrics::AttemptResult::kUpdateSucceeded;
+
+ case kErrorCodeDownloadTransferError:
+ return metrics::AttemptResult::kPayloadDownloadError;
+
+ case kErrorCodeDownloadInvalidMetadataSize:
+ case kErrorCodeDownloadInvalidMetadataMagicString:
+ case kErrorCodeDownloadMetadataSignatureError:
+ case kErrorCodeDownloadMetadataSignatureVerificationError:
+ case kErrorCodePayloadMismatchedType:
+ case kErrorCodeUnsupportedMajorPayloadVersion:
+ case kErrorCodeUnsupportedMinorPayloadVersion:
+ case kErrorCodeDownloadNewPartitionInfoError:
+ case kErrorCodeDownloadSignatureMissingInManifest:
+ case kErrorCodeDownloadManifestParseError:
+ case kErrorCodeDownloadOperationHashMissingError:
+ return metrics::AttemptResult::kMetadataMalformed;
+
+ case kErrorCodeDownloadOperationHashMismatch:
+ case kErrorCodeDownloadOperationHashVerificationError:
+ return metrics::AttemptResult::kOperationMalformed;
+
+ case kErrorCodeDownloadOperationExecutionError:
+ case kErrorCodeInstallDeviceOpenError:
+ case kErrorCodeKernelDeviceOpenError:
+ case kErrorCodeDownloadWriteError:
+ case kErrorCodeFilesystemCopierError:
+ return metrics::AttemptResult::kOperationExecutionError;
+
+ case kErrorCodeDownloadMetadataSignatureMismatch:
+ return metrics::AttemptResult::kMetadataVerificationFailed;
+
+ case kErrorCodePayloadSizeMismatchError:
+ case kErrorCodePayloadHashMismatchError:
+ case kErrorCodeDownloadPayloadVerificationError:
+ case kErrorCodeSignedDeltaPayloadExpectedError:
+ case kErrorCodeDownloadPayloadPubKeyVerificationError:
+ return metrics::AttemptResult::kPayloadVerificationFailed;
+
+ case kErrorCodeNewRootfsVerificationError:
+ case kErrorCodeNewKernelVerificationError:
+ return metrics::AttemptResult::kVerificationFailed;
+
+ case kErrorCodePostinstallRunnerError:
+ case kErrorCodePostinstallBootedFromFirmwareB:
+ case kErrorCodePostinstallFirmwareRONotUpdatable:
+ return metrics::AttemptResult::kPostInstallFailed;
+
+ // We should never get these errors in the update-attempt stage so
+ // return internal error if this happens.
+ case kErrorCodeError:
+ case kErrorCodeOmahaRequestXMLParseError:
+ case kErrorCodeOmahaRequestError:
+ case kErrorCodeOmahaResponseHandlerError:
+ case kErrorCodeDownloadStateInitializationError:
+ case kErrorCodeOmahaRequestEmptyResponseError:
+ case kErrorCodeDownloadInvalidMetadataSignature:
+ case kErrorCodeOmahaResponseInvalid:
+ case kErrorCodeOmahaUpdateIgnoredPerPolicy:
+ case kErrorCodeOmahaUpdateDeferredPerPolicy:
+ case kErrorCodeOmahaErrorInHTTPResponse:
+ case kErrorCodeDownloadMetadataSignatureMissingError:
+ case kErrorCodeOmahaUpdateDeferredForBackoff:
+ case kErrorCodePostinstallPowerwashError:
+ case kErrorCodeUpdateCanceledByChannelChange:
+ return metrics::AttemptResult::kInternalError;
+
+ // Special flags. These can't happen (we mask them out above) but
+ // the compiler doesn't know that. Just break out so we can warn and
+ // return |kInternalError|.
+ case kErrorCodeUmaReportedMax:
+ case kErrorCodeOmahaRequestHTTPResponseBase:
+ case kErrorCodeDevModeFlag:
+ case kErrorCodeResumedFlag:
+ case kErrorCodeTestImageFlag:
+ case kErrorCodeTestOmahaUrlFlag:
+ case kErrorCodeSpecialFlags:
+ break;
+ }
+
+ LOG(ERROR) << "Unexpected error code " << base_code;
+ return metrics::AttemptResult::kInternalError;
+}
+
+
+metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code) {
+ ErrorCode base_code = static_cast<ErrorCode>(code & ~kErrorCodeSpecialFlags);
+
+ if (base_code >= kErrorCodeOmahaRequestHTTPResponseBase) {
+ int http_status = base_code - kErrorCodeOmahaRequestHTTPResponseBase;
+ if (http_status >= 200 && http_status <= 599) {
+ return static_cast<metrics::DownloadErrorCode>(
+ static_cast<int>(metrics::DownloadErrorCode::kHttpStatus200) +
+ http_status - 200);
+ } else if (http_status == 0) {
+ // The code is using HTTP Status 0 for "Unable to get http
+ // response code."
+ return metrics::DownloadErrorCode::kDownloadError;
+ }
+ LOG(WARNING) << "Unexpected HTTP status code " << http_status;
+ return metrics::DownloadErrorCode::kHttpStatusOther;
+ }
+
+ switch (base_code) {
+ // Unfortunately, kErrorCodeDownloadTransferError is returned for a wide
+ // variety of errors (proxy errors, host not reachable, timeouts etc.).
+ //
+ // For now just map that to kDownloading. See http://crbug.com/355745
+ // for how we plan to add more detail in the future.
+ case kErrorCodeDownloadTransferError:
+ return metrics::DownloadErrorCode::kDownloadError;
+
+ // All of these error codes are not related to downloading so break
+ // out so we can warn and return InputMalformed.
+ case kErrorCodeSuccess:
+ case kErrorCodeError:
+ case kErrorCodeOmahaRequestError:
+ case kErrorCodeOmahaResponseHandlerError:
+ case kErrorCodeFilesystemCopierError:
+ case kErrorCodePostinstallRunnerError:
+ case kErrorCodePayloadMismatchedType:
+ case kErrorCodeInstallDeviceOpenError:
+ case kErrorCodeKernelDeviceOpenError:
+ case kErrorCodePayloadHashMismatchError:
+ case kErrorCodePayloadSizeMismatchError:
+ case kErrorCodeDownloadPayloadVerificationError:
+ case kErrorCodeDownloadNewPartitionInfoError:
+ case kErrorCodeDownloadWriteError:
+ case kErrorCodeNewRootfsVerificationError:
+ case kErrorCodeNewKernelVerificationError:
+ case kErrorCodeSignedDeltaPayloadExpectedError:
+ case kErrorCodeDownloadPayloadPubKeyVerificationError:
+ case kErrorCodePostinstallBootedFromFirmwareB:
+ case kErrorCodeDownloadStateInitializationError:
+ case kErrorCodeDownloadInvalidMetadataMagicString:
+ case kErrorCodeDownloadSignatureMissingInManifest:
+ case kErrorCodeDownloadManifestParseError:
+ case kErrorCodeDownloadMetadataSignatureError:
+ case kErrorCodeDownloadMetadataSignatureVerificationError:
+ case kErrorCodeDownloadMetadataSignatureMismatch:
+ case kErrorCodeDownloadOperationHashVerificationError:
+ case kErrorCodeDownloadOperationExecutionError:
+ case kErrorCodeDownloadOperationHashMismatch:
+ case kErrorCodeOmahaRequestEmptyResponseError:
+ case kErrorCodeOmahaRequestXMLParseError:
+ case kErrorCodeDownloadInvalidMetadataSize:
+ case kErrorCodeDownloadInvalidMetadataSignature:
+ case kErrorCodeOmahaResponseInvalid:
+ case kErrorCodeOmahaUpdateIgnoredPerPolicy:
+ case kErrorCodeOmahaUpdateDeferredPerPolicy:
+ case kErrorCodeOmahaErrorInHTTPResponse:
+ case kErrorCodeDownloadOperationHashMissingError:
+ case kErrorCodeDownloadMetadataSignatureMissingError:
+ case kErrorCodeOmahaUpdateDeferredForBackoff:
+ case kErrorCodePostinstallPowerwashError:
+ case kErrorCodeUpdateCanceledByChannelChange:
+ case kErrorCodePostinstallFirmwareRONotUpdatable:
+ case kErrorCodeUnsupportedMajorPayloadVersion:
+ case kErrorCodeUnsupportedMinorPayloadVersion:
+ break;
+
+ // Special flags. These can't happen (we mask them out above) but
+ // the compiler doesn't know that. Just break out so we can warn and
+ // return |kInputMalformed|.
+ case kErrorCodeUmaReportedMax:
+ case kErrorCodeOmahaRequestHTTPResponseBase:
+ case kErrorCodeDevModeFlag:
+ case kErrorCodeResumedFlag:
+ case kErrorCodeTestImageFlag:
+ case kErrorCodeTestOmahaUrlFlag:
+ case kErrorCodeSpecialFlags:
+ LOG(ERROR) << "Unexpected error code " << base_code;
+ break;
+ }
+
+ return metrics::DownloadErrorCode::kInputMalformed;
+}
+
// Returns a printable version of the various flags denoted in the higher order
// bits of the given code. Returns an empty string if none of those bits are
// set.
@@ -1278,6 +1463,48 @@
return true;
}
+bool WallclockDurationHelper(SystemState* system_state,
+ const std::string& state_variable_key,
+ base::TimeDelta* out_duration) {
+ bool ret = false;
+
+ base::Time now = system_state->clock()->GetWallclockTime();
+ int64_t stored_value;
+ if (system_state->prefs()->GetInt64(state_variable_key, &stored_value)) {
+ base::Time stored_time = base::Time::FromInternalValue(stored_value);
+ if (stored_time > now) {
+ LOG(ERROR) << "Stored time-stamp used for " << state_variable_key
+ << " is in the future.";
+ } else {
+ *out_duration = now - stored_time;
+ ret = true;
+ }
+ }
+
+ if (!system_state->prefs()->SetInt64(state_variable_key,
+ now.ToInternalValue())) {
+ LOG(ERROR) << "Error storing time-stamp in " << state_variable_key;
+ }
+
+ return ret;
+}
+
+bool MonotonicDurationHelper(SystemState* system_state,
+ int64_t* storage,
+ base::TimeDelta* out_duration) {
+ bool ret = false;
+
+ base::Time now = system_state->clock()->GetMonotonicTime();
+ if (*storage != 0) {
+ base::Time stored_time = base::Time::FromInternalValue(*storage);
+ *out_duration = now - stored_time;
+ ret = true;
+ }
+ *storage = now.ToInternalValue();
+
+ return ret;
+}
+
} // namespace utils
} // namespace chromeos_update_engine
diff --git a/utils.h b/utils.h
index 0a4f7b4..482f81a 100644
--- a/utils.h
+++ b/utils.h
@@ -21,6 +21,7 @@
#include "update_engine/action.h"
#include "update_engine/constants.h"
+#include "update_engine/metrics.h"
#include "update_engine/action_processor.h"
namespace chromeos_update_engine {
@@ -337,6 +338,18 @@
// it'll return the same value again.
ErrorCode GetBaseErrorCode(ErrorCode code);
+// Transforms a ErrorCode value into a metrics::DownloadErrorCode.
+// This obviously only works for errors related to downloading so if |code|
+// is e.g. |kErrorCodeFilesystemCopierError| then
+// |kDownloadErrorCodeInputMalformed| is returned.
+metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code);
+
+// Transforms a ErrorCode value into a metrics::AttemptResult.
+//
+// If metrics::AttemptResult::kPayloadDownloadError is returned, you
+// can use utils::GetDownloadError() to get more detail.
+metrics::AttemptResult GetAttemptResult(ErrorCode code);
+
// Sends the error code to UMA using the metrics interface object in the given
// system state. It also uses the system state to determine the right UMA
// bucket for the error code.
@@ -395,6 +408,29 @@
// available in GLib 2.26 or later.)
bool ConvertToOmahaInstallDate(base::Time time, int *out_num_days);
+// This function returns the duration on the wallclock since the last
+// time it was called for the same |state_variable_key| value.
+//
+// If the function returns |true|, the duration (always non-negative)
+// is returned in |out_duration|. If the function returns |false|
+// something went wrong or there was no previous measurement.
+bool WallclockDurationHelper(SystemState* system_state,
+ const std::string& state_variable_key,
+ base::TimeDelta* out_duration);
+
+// This function returns the duration on the monotonic clock since the
+// last time it was called for the same |storage| pointer.
+//
+// You should pass a pointer to a 64-bit integer in |storage| which
+// should be initialized to 0.
+//
+// If the function returns |true|, the duration (always non-negative)
+// is returned in |out_duration|. If the function returns |false|
+// something went wrong or there was no previous measurement.
+bool MonotonicDurationHelper(SystemState* system_state,
+ int64_t* storage,
+ base::TimeDelta* out_duration);
+
} // namespace utils
@@ -533,6 +569,7 @@
void set_should_complete(bool should_complete) {
should_complete_ = should_complete;
}
+ ErrorCode get_code() const { return code_; }
private:
ActionProcessor* processor_;
diff --git a/utils_unittest.cc b/utils_unittest.cc
index e89f069..7105d3a 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -16,6 +16,9 @@
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
+#include "update_engine/fake_clock.h"
+#include "update_engine/mock_system_state.h"
+#include "update_engine/prefs.h"
#include "update_engine/test_utils.h"
#include "update_engine/utils.h"
@@ -527,4 +530,139 @@
EXPECT_EQ(value1, value2 - 7);
}
+TEST(UtilsTest, WallclockDurationHelper) {
+ MockSystemState mock_system_state;
+ FakeClock fake_clock;
+ base::TimeDelta duration;
+ string state_variable_key = "test-prefs";
+ string temp_dir;
+ Prefs fake_prefs;
+
+ EXPECT_TRUE(utils::MakeTempDirectory("DurationPrefs.XXXXXX", &temp_dir));
+ fake_prefs.Init(base::FilePath(temp_dir));
+
+ mock_system_state.set_clock(&fake_clock);
+ mock_system_state.set_prefs(&fake_prefs);
+
+ // Initialize wallclock to 1 sec.
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+
+ // First time called so no previous measurement available.
+ EXPECT_FALSE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+
+ // Next time, we should get zero since the clock didn't advance.
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+
+ // We can also call it as many times as we want with it being
+ // considered a failure.
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+
+ // Advance the clock one second, then we should get 1 sec on the
+ // next call and 0 sec on the subsequent call.
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(2000000));
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 1);
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+
+ // Advance clock two seconds and we should get 2 sec and then 0 sec.
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(4000000));
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 2);
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+
+ // There's a possibility that the wallclock can go backwards (NTP
+ // adjustments, for example) so check that we properly handle this
+ // case.
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(3000000));
+ EXPECT_FALSE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ fake_clock.SetWallclockTime(base::Time::FromInternalValue(4000000));
+ EXPECT_TRUE(utils::WallclockDurationHelper(&mock_system_state,
+ state_variable_key,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 1);
+
+ EXPECT_TRUE(utils::RecursiveUnlinkDir(temp_dir));
+}
+
+TEST(UtilsTest, MonotonicDurationHelper) {
+ int64_t storage = 0;
+ MockSystemState mock_system_state;
+ FakeClock fake_clock;
+ base::TimeDelta duration;
+
+ mock_system_state.set_clock(&fake_clock);
+
+ // Initialize monotonic clock to 1 sec.
+ fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
+
+ // First time called so no previous measurement available.
+ EXPECT_FALSE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+
+ // Next time, we should get zero since the clock didn't advance.
+ EXPECT_TRUE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+
+ // We can also call it as many times as we want with it being
+ // considered a failure.
+ EXPECT_TRUE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+ EXPECT_TRUE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+
+ // Advance the clock one second, then we should get 1 sec on the
+ // next call and 0 sec on the subsequent call.
+ fake_clock.SetMonotonicTime(base::Time::FromInternalValue(2000000));
+ EXPECT_TRUE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 1);
+ EXPECT_TRUE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+
+ // Advance clock two seconds and we should get 2 sec and then 0 sec.
+ fake_clock.SetMonotonicTime(base::Time::FromInternalValue(4000000));
+ EXPECT_TRUE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 2);
+ EXPECT_TRUE(utils::MonotonicDurationHelper(&mock_system_state,
+ &storage,
+ &duration));
+ EXPECT_EQ(duration.InSeconds(), 0);
+}
+
} // namespace chromeos_update_engine