| // |
| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "update_engine/metrics_reporter_omaha.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include <base/logging.h> |
| #include <metrics/metrics_library.h> |
| |
| #include "update_engine/common/clock_interface.h" |
| #include "update_engine/common/constants.h" |
| #include "update_engine/common/prefs_interface.h" |
| #include "update_engine/common/utils.h" |
| #include "update_engine/metrics_utils.h" |
| #include "update_engine/system_state.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 kMetricAttemptConnectionType[] = |
| "UpdateEngine.Attempt.ConnectionType"; |
| 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 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.Rollback.* metric. |
| const char kMetricRollbackResult[] = "UpdateEngine.Rollback.Result"; |
| |
| // UpdateEngine.CertificateCheck.* metrics. |
| const char kMetricCertificateCheckUpdateCheck[] = |
| "UpdateEngine.CertificateCheck.UpdateCheck"; |
| const char kMetricCertificateCheckDownload[] = |
| "UpdateEngine.CertificateCheck.Download"; |
| |
| // UpdateEngine.* metrics. |
| const char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount"; |
| const char kMetricInstallDateProvisioningSource[] = |
| "UpdateEngine.InstallDateProvisioningSource"; |
| const char kMetricTimeToRebootMinutes[] = "UpdateEngine.TimeToRebootMinutes"; |
| |
| } // namespace metrics |
| |
| MetricsReporterOmaha::MetricsReporterOmaha() |
| : metrics_lib_(new MetricsLibrary()) {} |
| |
| void MetricsReporterOmaha::Initialize() { |
| metrics_lib_->Init(); |
| } |
| |
| void MetricsReporterOmaha::ReportDailyMetrics(base::TimeDelta os_age) { |
| string metric = metrics::kMetricDailyOSAgeDays; |
| LOG(INFO) << "Uploading " << utils::FormatTimeDelta(os_age) << " for metric " |
| << metric; |
| metrics_lib_->SendToUMA(metric, |
| static_cast<int>(os_age.InDays()), |
| 0, // min: 0 days |
| 6 * 30, // max: 6 months (approx) |
| 50); // num_buckets |
| } |
| |
| void MetricsReporterOmaha::ReportUpdateCheckMetrics( |
| SystemState* system_state, |
| metrics::CheckResult result, |
| metrics::CheckReaction reaction, |
| metrics::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)"; |
| 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)"; |
| metrics_lib_->SendEnumToUMA(metric, value, max_value); |
| } |
| if (download_error_code != metrics::DownloadErrorCode::kUnset) { |
| metric = metrics::kMetricCheckDownloadErrorCode; |
| value = static_cast<int>(download_error_code); |
| LOG(INFO) << "Sending " << value << " for metric " << metric << " (sparse)"; |
| metrics_lib_->SendSparseToUMA(metric, value); |
| } |
| |
| base::TimeDelta time_since_last; |
| if (metrics_utils::WallclockDurationHelper( |
| system_state, |
| kPrefsMetricsCheckLastReportingTime, |
| &time_since_last)) { |
| metric = metrics::kMetricCheckTimeSinceLastCheckMinutes; |
| LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last) |
| << " for metric " << metric; |
| 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 (metrics_utils::MonotonicDurationHelper( |
| system_state, &uptime_since_last_storage, &uptime_since_last)) { |
| metric = metrics::kMetricCheckTimeSinceLastCheckUptimeMinutes; |
| LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last) |
| << " for metric " << metric; |
| metrics_lib_->SendToUMA(metric, |
| uptime_since_last.InMinutes(), |
| 0, // min: 0 min |
| 30 * 24 * 60, // max: 30 days |
| 50); // num_buckets |
| } |
| } |
| |
| void MetricsReporterOmaha::ReportAbnormallyTerminatedUpdateAttemptMetrics() { |
| string metric = metrics::kMetricAttemptResult; |
| metrics::AttemptResult attempt_result = |
| metrics::AttemptResult::kAbnormalTermination; |
| |
| LOG(INFO) << "Uploading " << static_cast<int>(attempt_result) |
| << " for metric " << metric; |
| metrics_lib_->SendEnumToUMA( |
| metric, |
| static_cast<int>(attempt_result), |
| static_cast<int>(metrics::AttemptResult::kNumConstants)); |
| } |
| |
| void MetricsReporterOmaha::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, |
| metrics::AttemptResult attempt_result, |
| ErrorCode internal_error_code, |
| metrics::DownloadErrorCode payload_download_error_code, |
| metrics::ConnectionType connection_type) { |
| string metric = metrics::kMetricAttemptNumber; |
| LOG(INFO) << "Uploading " << attempt_number << " for metric " << metric; |
| 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; |
| metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes); |
| |
| metric = metrics::kMetricAttemptDurationMinutes; |
| LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration) |
| << " for metric " << metric; |
| 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; |
| 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; |
| 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; |
| 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; |
| 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; |
| metrics_lib_->SendEnumToUMA(metric, download_source, kNumDownloadSources); |
| |
| metric = metrics::kMetricAttemptResult; |
| LOG(INFO) << "Uploading " << static_cast<int>(attempt_result) |
| << " for metric " << metric; |
| metrics_lib_->SendEnumToUMA( |
| metric, |
| static_cast<int>(attempt_result), |
| static_cast<int>(metrics::AttemptResult::kNumConstants)); |
| |
| if (internal_error_code != ErrorCode::kSuccess) { |
| metric = metrics::kMetricAttemptInternalErrorCode; |
| LOG(INFO) << "Uploading " << internal_error_code << " for metric " |
| << metric; |
| metrics_lib_->SendEnumToUMA(metric, |
| static_cast<int>(internal_error_code), |
| static_cast<int>(ErrorCode::kUmaReportedMax)); |
| } |
| |
| if (payload_download_error_code != metrics::DownloadErrorCode::kUnset) { |
| metric = metrics::kMetricAttemptDownloadErrorCode; |
| LOG(INFO) << "Uploading " << static_cast<int>(payload_download_error_code) |
| << " for metric " << metric << " (sparse)"; |
| metrics_lib_->SendSparseToUMA( |
| metric, static_cast<int>(payload_download_error_code)); |
| } |
| |
| base::TimeDelta time_since_last; |
| if (metrics_utils::WallclockDurationHelper( |
| system_state, |
| kPrefsMetricsAttemptLastReportingTime, |
| &time_since_last)) { |
| metric = metrics::kMetricAttemptTimeSinceLastAttemptMinutes; |
| LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last) |
| << " for metric " << metric; |
| 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 (metrics_utils::MonotonicDurationHelper( |
| system_state, &uptime_since_last_storage, &uptime_since_last)) { |
| metric = metrics::kMetricAttemptTimeSinceLastAttemptUptimeMinutes; |
| LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last) |
| << " for metric " << metric; |
| metrics_lib_->SendToUMA(metric, |
| uptime_since_last.InMinutes(), |
| 0, // min: 0 min |
| 30 * 24 * 60, // max: 30 days |
| 50); // num_buckets |
| } |
| |
| metric = metrics::kMetricAttemptConnectionType; |
| LOG(INFO) << "Uploading " << static_cast<int>(connection_type) |
| << " for metric " << metric; |
| metrics_lib_->SendEnumToUMA( |
| metric, |
| static_cast<int>(connection_type), |
| static_cast<int>(metrics::ConnectionType::kNumConstants)); |
| } |
| |
| void MetricsReporterOmaha::ReportSuccessfulUpdateMetrics( |
| 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 = metrics::kMetricSuccessfulUpdatePayloadSizeMiB; |
| int64_t mbs = payload_size / kNumBytesInOneMiB; |
| LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric; |
| 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 |
| // 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; |
| 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; |
| 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; |
| 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; |
| 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; |
| 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; |
| metrics_lib_->SendToUMA(metric, |
| reboot_count, |
| 0, // min: 0 reboots |
| 49, // max: 49 reboots |
| 50); // num_buckets |
| |
| metric = metrics::kMetricSuccessfulUpdatePayloadType; |
| metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes); |
| LOG(INFO) << "Uploading " << utils::ToString(payload_type) << " for metric " |
| << metric; |
| |
| metric = metrics::kMetricSuccessfulUpdateAttemptCount; |
| 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; |
| metrics_lib_->SendToUMA(metric, |
| updates_abandoned_count, |
| 0, // min: 0 counts |
| 49, // max: 49 counts |
| 50); // num_buckets |
| } |
| |
| void MetricsReporterOmaha::ReportRollbackMetrics( |
| metrics::RollbackResult result) { |
| string metric = metrics::kMetricRollbackResult; |
| int value = static_cast<int>(result); |
| LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)"; |
| metrics_lib_->SendEnumToUMA( |
| metric, value, static_cast<int>(metrics::RollbackResult::kNumConstants)); |
| } |
| |
| void MetricsReporterOmaha::ReportCertificateCheckMetrics( |
| ServerToCheck server_to_check, CertificateCheckResult result) { |
| string metric; |
| switch (server_to_check) { |
| case ServerToCheck::kUpdate: |
| metric = metrics::kMetricCertificateCheckUpdateCheck; |
| break; |
| case ServerToCheck::kDownload: |
| metric = metrics::kMetricCertificateCheckDownload; |
| break; |
| case ServerToCheck::kNone: |
| return; |
| } |
| LOG(INFO) << "Uploading " << static_cast<int>(result) << " for metric " |
| << metric; |
| metrics_lib_->SendEnumToUMA( |
| metric, |
| static_cast<int>(result), |
| static_cast<int>(CertificateCheckResult::kNumConstants)); |
| } |
| |
| void MetricsReporterOmaha::ReportFailedUpdateCount(int target_attempt) { |
| string metric = metrics::kMetricFailedUpdateCount; |
| metrics_lib_->SendToUMA(metric, |
| target_attempt, |
| 1, // min value |
| 50, // max value |
| kNumDefaultUmaBuckets); |
| |
| LOG(INFO) << "Uploading " << target_attempt << " (count) for metric " |
| << metric; |
| } |
| |
| void MetricsReporterOmaha::ReportTimeToReboot(int time_to_reboot_minutes) { |
| string metric = metrics::kMetricTimeToRebootMinutes; |
| metrics_lib_->SendToUMA(metric, |
| time_to_reboot_minutes, |
| 0, // min: 0 minute |
| 30 * 24 * 60, // max: 1 month (approx) |
| kNumDefaultUmaBuckets); |
| |
| LOG(INFO) << "Uploading " << time_to_reboot_minutes << " for metric " |
| << metric; |
| } |
| |
| void MetricsReporterOmaha::ReportInstallDateProvisioningSource(int source, |
| int max) { |
| metrics_lib_->SendEnumToUMA(metrics::kMetricInstallDateProvisioningSource, |
| source, // Sample. |
| max); |
| } |
| |
| } // namespace chromeos_update_engine |