1 //
2 // Copyright (C) 2017 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/aosp/metrics_reporter_android.h"
18
19 #include <stdint.h>
20
21 #include <algorithm>
22 #include <memory>
23 #include <string>
24
25 #include <android-base/properties.h>
26 #include <android-base/strings.h>
27 #include <fs_mgr.h>
28 #include <libdm/dm.h>
29 #include <liblp/builder.h>
30 #include <liblp/liblp.h>
31 #include <statslog_ue.h>
32
33 #include "update_engine/common/constants.h"
34 #include "update_engine/payload_consumer/install_plan.h"
35
36 using android::base::EndsWith;
37 using android::fs_mgr::GetPartitionGroupName;
38 using android::fs_mgr::LpMetadata;
39 using android::fs_mgr::MetadataBuilder;
40 using android::fs_mgr::ReadMetadata;
41 using android::fs_mgr::SlotNumberForSlotSuffix;
42
43 namespace {
44 // A number offset adds on top of the enum value. e.g. ErrorCode::SUCCESS will
45 // be reported as 10000, and AttemptResult::UPDATE_CANCELED will be reported as
46 // 10011. This keeps the ordering of update engine's enum definition when statsd
47 // atoms reserve the value 0 for unknown state.
48 constexpr auto kMetricsReporterEnumOffset = 10000;
49
GetStatsdEnumValue(int32_t value)50 int32_t GetStatsdEnumValue(int32_t value) {
51 return kMetricsReporterEnumOffset + value;
52 }
53
IsHashTreeEnabled(const chromeos_update_engine::InstallPlan * install_plan)54 bool IsHashTreeEnabled(
55 const chromeos_update_engine::InstallPlan* install_plan) {
56 return std::any_of(
57 install_plan->partitions.begin(),
58 install_plan->partitions.end(),
59 [](const auto& partition) { return partition.hash_tree_size > 0; });
60 }
61
IsFECEnabled(const chromeos_update_engine::InstallPlan * install_plan)62 bool IsFECEnabled(const chromeos_update_engine::InstallPlan* install_plan) {
63 return std::any_of(
64 install_plan->partitions.begin(),
65 install_plan->partitions.end(),
66 [](const auto& partition) { return partition.fec_size > 0; });
67 }
68
69 } // namespace
70
71 namespace chromeos_update_engine {
72
73 namespace metrics {
74
CreateMetricsReporter(DynamicPartitionControlInterface * dynamic_partition_control,const InstallPlan * install_plan)75 std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter(
76 DynamicPartitionControlInterface* dynamic_partition_control,
77 const InstallPlan* install_plan) {
78 return std::make_unique<MetricsReporterAndroid>(dynamic_partition_control,
79 install_plan);
80 }
81
82 } // namespace metrics
83
ReportUpdateAttemptMetrics(int attempt_number,PayloadType payload_type,base::TimeDelta duration,base::TimeDelta duration_uptime,int64_t payload_size,metrics::AttemptResult attempt_result,ErrorCode error_code)84 void MetricsReporterAndroid::ReportUpdateAttemptMetrics(
85 int attempt_number,
86 PayloadType payload_type,
87 base::TimeDelta duration,
88 base::TimeDelta duration_uptime,
89 int64_t payload_size,
90 metrics::AttemptResult attempt_result,
91 ErrorCode error_code) {
92 int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
93
94 int64_t super_partition_size_bytes = 0;
95 int64_t super_free_space = 0;
96 int64_t slot_size_bytes = 0;
97
98 if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
99 uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
100 auto super_device = fs_mgr_get_super_partition_name();
101 std::unique_ptr<LpMetadata> metadata = ReadMetadata(super_device, slot);
102 if (metadata) {
103 super_partition_size_bytes = GetTotalSuperPartitionSize(*metadata);
104
105 for (const auto& group : metadata->groups) {
106 if (EndsWith(GetPartitionGroupName(group), fs_mgr_get_slot_suffix())) {
107 slot_size_bytes += group.maximum_size;
108 }
109 }
110
111 auto metadata_builder = MetadataBuilder::New(*metadata);
112 if (metadata_builder) {
113 auto free_regions = metadata_builder->GetFreeRegions();
114 for (const auto& interval : free_regions) {
115 super_free_space += interval.length();
116 }
117 super_free_space *= android::dm::kSectorSize;
118 } else {
119 LOG(ERROR) << "Cannot create metadata builder.";
120 }
121 } else {
122 LOG(ERROR) << "Could not read dynamic partition metadata for device: "
123 << super_device;
124 }
125 }
126
127 bool vab_compression_enabled = android::base::GetBoolProperty(
128 "ro.virtual_ab.compression.enabled", false);
129 bool vab_compression_used =
130 dynamic_partition_control_->UpdateUsesSnapshotCompression();
131
132 statsd::stats_write(
133 statsd::UPDATE_ENGINE_UPDATE_ATTEMPT_REPORTED,
134 attempt_number,
135 GetStatsdEnumValue(static_cast<int32_t>(payload_type)),
136 duration.InMinutes(),
137 duration_uptime.InMinutes(),
138 payload_size_mib,
139 GetStatsdEnumValue(static_cast<int32_t>(attempt_result)),
140 GetStatsdEnumValue(static_cast<int32_t>(error_code)),
141 android::base::GetProperty("ro.build.fingerprint", "").c_str(),
142 super_partition_size_bytes,
143 slot_size_bytes,
144 super_free_space,
145 vab_compression_enabled,
146 vab_compression_used);
147 }
148
ReportUpdateAttemptDownloadMetrics(int64_t payload_bytes_downloaded,int64_t,DownloadSource,metrics::DownloadErrorCode,metrics::ConnectionType)149 void MetricsReporterAndroid::ReportUpdateAttemptDownloadMetrics(
150 int64_t payload_bytes_downloaded,
151 int64_t /* payload_download_speed_bps */,
152 DownloadSource /* download_source */,
153 metrics::DownloadErrorCode /* payload_download_error_code */,
154 metrics::ConnectionType /* connection_type */) {
155 // TODO(xunchang) add statsd reporting
156 LOG(INFO) << "Current update attempt downloads "
157 << payload_bytes_downloaded / kNumBytesInOneMiB << " bytes data";
158 }
159
ReportSuccessfulUpdateMetrics(int attempt_count,int,PayloadType payload_type,int64_t payload_size,int64_t num_bytes_downloaded[kNumDownloadSources],int download_overhead_percentage,base::TimeDelta total_duration,base::TimeDelta,int reboot_count,int)160 void MetricsReporterAndroid::ReportSuccessfulUpdateMetrics(
161 int attempt_count,
162 int /* updates_abandoned_count */,
163 PayloadType payload_type,
164 int64_t payload_size,
165 int64_t num_bytes_downloaded[kNumDownloadSources],
166 int download_overhead_percentage,
167 base::TimeDelta total_duration,
168 base::TimeDelta /* total_duration_uptime */,
169 int reboot_count,
170 int /* url_switch_count */) {
171 int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
172 int64_t total_bytes_downloaded = 0;
173 for (size_t i = 0; i < kNumDownloadSources; i++) {
174 total_bytes_downloaded += num_bytes_downloaded[i] / kNumBytesInOneMiB;
175 }
176
177 statsd::stats_write(statsd::UPDATE_ENGINE_SUCCESSFUL_UPDATE_REPORTED,
178 static_cast<int32_t>(attempt_count),
179 GetStatsdEnumValue(static_cast<int32_t>(payload_type)),
180 static_cast<int32_t>(payload_size_mib),
181 static_cast<int32_t>(total_bytes_downloaded),
182 static_cast<int32_t>(download_overhead_percentage),
183 static_cast<int32_t>(total_duration.InMinutes()),
184 static_cast<int32_t>(reboot_count),
185 IsHashTreeEnabled(install_plan_),
186 IsFECEnabled(install_plan_));
187 }
188
ReportAbnormallyTerminatedUpdateAttemptMetrics()189 void MetricsReporterAndroid::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
190 int attempt_result =
191 static_cast<int>(metrics::AttemptResult::kAbnormalTermination);
192 // TODO(xunchang) add statsd reporting
193 LOG(INFO) << "Abnormally terminated update attempt result " << attempt_result;
194 }
195
196 }; // namespace chromeos_update_engine
197