blob: 7cf8ff7469e566c509616d38a51339b3135f562a [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2022 The Chromium Authors
Min Qin75bee1b2022-02-05 03:57:342// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
6
7#include "base/bit_cast.h"
8#include "base/metrics/field_trial_params.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/string_split.h"
Min Qincae984cb2022-05-20 03:08:3411#include "base/time/clock.h"
12#include "components/segmentation_platform/internal/constants.h"
Min Qin642ab222022-05-19 21:54:5313#include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
Min Qin75bee1b2022-02-05 03:57:3414#include "components/segmentation_platform/internal/stats.h"
15#include "components/segmentation_platform/public/config.h"
16#include "components/segmentation_platform/public/features.h"
Min Qincae984cb2022-05-20 03:08:3417#include "components/segmentation_platform/public/local_state_helper.h"
Min Qin75bee1b2022-02-05 03:57:3418#include "services/metrics/public/cpp/ukm_builders.h"
19#include "services/metrics/public/cpp/ukm_recorder.h"
20
21#define CALL_MEMBER_FN(obj, func) ((obj).*(func))
22#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x)[0])
23
ssid8386fc72022-05-21 00:34:1724using segmentation_platform::proto::SegmentId;
Min Qin75bee1b2022-02-05 03:57:3425using ukm::builders::Segmentation_ModelExecution;
26
27namespace {
28using UkmMemberFn =
29 Segmentation_ModelExecution& (Segmentation_ModelExecution::*)(int64_t);
30
Xing Liu282dd0d2022-02-24 00:20:3931const UkmMemberFn kSegmentationUkmInputMethods[] = {
Min Qin75bee1b2022-02-05 03:57:3432 &Segmentation_ModelExecution::SetInput0,
33 &Segmentation_ModelExecution::SetInput1,
34 &Segmentation_ModelExecution::SetInput2,
35 &Segmentation_ModelExecution::SetInput3,
36 &Segmentation_ModelExecution::SetInput4,
37 &Segmentation_ModelExecution::SetInput5,
38 &Segmentation_ModelExecution::SetInput6,
39 &Segmentation_ModelExecution::SetInput7,
40 &Segmentation_ModelExecution::SetInput8,
41 &Segmentation_ModelExecution::SetInput9,
42 &Segmentation_ModelExecution::SetInput10,
43 &Segmentation_ModelExecution::SetInput11,
44 &Segmentation_ModelExecution::SetInput12,
45 &Segmentation_ModelExecution::SetInput13,
46 &Segmentation_ModelExecution::SetInput14,
47 &Segmentation_ModelExecution::SetInput15,
48 &Segmentation_ModelExecution::SetInput16,
49 &Segmentation_ModelExecution::SetInput17,
50 &Segmentation_ModelExecution::SetInput18,
51 &Segmentation_ModelExecution::SetInput19,
52 &Segmentation_ModelExecution::SetInput20,
53 &Segmentation_ModelExecution::SetInput21,
54 &Segmentation_ModelExecution::SetInput22,
55 &Segmentation_ModelExecution::SetInput23,
56 &Segmentation_ModelExecution::SetInput24,
57 &Segmentation_ModelExecution::SetInput25,
58 &Segmentation_ModelExecution::SetInput26,
59 &Segmentation_ModelExecution::SetInput27,
60 &Segmentation_ModelExecution::SetInput28,
Min Qin81e6bda2022-12-07 06:44:2761 &Segmentation_ModelExecution::SetInput29,
62 &Segmentation_ModelExecution::SetInput30,
63 &Segmentation_ModelExecution::SetInput31,
64 &Segmentation_ModelExecution::SetInput32,
65 &Segmentation_ModelExecution::SetInput33,
66 &Segmentation_ModelExecution::SetInput34,
67 &Segmentation_ModelExecution::SetInput35,
68 &Segmentation_ModelExecution::SetInput36,
69 &Segmentation_ModelExecution::SetInput37,
70 &Segmentation_ModelExecution::SetInput38,
71 &Segmentation_ModelExecution::SetInput39,
72 &Segmentation_ModelExecution::SetInput40,
73 &Segmentation_ModelExecution::SetInput41,
74 &Segmentation_ModelExecution::SetInput42,
75 &Segmentation_ModelExecution::SetInput43,
76 &Segmentation_ModelExecution::SetInput44,
77 &Segmentation_ModelExecution::SetInput45,
78 &Segmentation_ModelExecution::SetInput46,
79 &Segmentation_ModelExecution::SetInput47,
80 &Segmentation_ModelExecution::SetInput48,
81 &Segmentation_ModelExecution::SetInput49};
Min Qin75bee1b2022-02-05 03:57:3482
Xing Liu282dd0d2022-02-24 00:20:3983const UkmMemberFn kSegmentationUkmOutputMethods[] = {
84 &Segmentation_ModelExecution::SetActualResult,
85 &Segmentation_ModelExecution::SetActualResult2,
86 &Segmentation_ModelExecution::SetActualResult3,
87 &Segmentation_ModelExecution::SetActualResult4,
88 &Segmentation_ModelExecution::SetActualResult5,
89 &Segmentation_ModelExecution::SetActualResult6};
90
ssid8386fc72022-05-21 00:34:1791base::flat_set<SegmentId> GetSegmentIdsAllowedForReporting() {
Min Qin75bee1b2022-02-05 03:57:3492 std::vector<std::string> segment_ids = base::SplitString(
93 base::GetFieldTrialParamValueByFeature(
94 segmentation_platform::features::
95 kSegmentationStructuredMetricsFeature,
96 segmentation_platform::kSegmentIdsAllowedForReportingKey),
97 ",;", base::WhitespaceHandling::TRIM_WHITESPACE,
98 base::SplitResult::SPLIT_WANT_NONEMPTY);
ssid8386fc72022-05-21 00:34:1799 base::flat_set<SegmentId> result;
Min Qin75bee1b2022-02-05 03:57:34100 for (const auto& id : segment_ids) {
101 int segment_id;
102 if (base::StringToInt(id, &segment_id))
ssid8386fc72022-05-21 00:34:17103 result.emplace(static_cast<SegmentId>(segment_id));
Min Qin75bee1b2022-02-05 03:57:34104 }
105 return result;
106}
107
108} // namespace
109
110namespace segmentation_platform {
111
112SegmentationUkmHelper::SegmentationUkmHelper() {
113 Initialize();
114}
115
116SegmentationUkmHelper::~SegmentationUkmHelper() = default;
117
118void SegmentationUkmHelper::Initialize() {
119 allowed_segment_ids_ = GetSegmentIdsAllowedForReporting();
120}
121
122// static
123SegmentationUkmHelper* SegmentationUkmHelper::GetInstance() {
124 static base::NoDestructor<SegmentationUkmHelper> helper;
125 return helper.get();
126}
127
128ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
ssid8386fc72022-05-21 00:34:17129 SegmentId segment_id,
Min Qin75bee1b2022-02-05 03:57:34130 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04131 const ModelProvider::Request& input_tensor,
Min Qin75bee1b2022-02-05 03:57:34132 float result) {
Min Qin75bee1b2022-02-05 03:57:34133 ukm::SourceId source_id = ukm::NoURLSourceId();
Min Qin75bee1b2022-02-05 03:57:34134 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
135
Xing Liu282dd0d2022-02-24 00:20:39136 // Add inputs to ukm message.
137 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
Min Qinf254aa92022-08-24 18:48:42138 input_tensor)) {
Xing Liu282dd0d2022-02-24 00:20:39139 return ukm::kInvalidSourceId;
Min Qinf254aa92022-08-24 18:48:42140 }
Min Qin75bee1b2022-02-05 03:57:34141
Xing Liu282dd0d2022-02-24 00:20:39142 // TODO(xingliu): Also record continuous outputs for model execution.
Min Qin75bee1b2022-02-05 03:57:34143 execution_result.SetPredictionResult(FloatToInt64(result))
144 .Record(ukm::UkmRecorder::Get());
145 return source_id;
146}
147
Xing Liu282dd0d2022-02-24 00:20:39148ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
ssid8386fc72022-05-21 00:34:17149 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39150 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04151 const ModelProvider::Request& input_tensor,
152 const ModelProvider::Response& outputs,
Min Qind52d150c2022-04-22 05:32:13153 const std::vector<int>& output_indexes,
Min Qin642ab222022-05-19 21:54:53154 absl::optional<proto::PredictionResult> prediction_result,
155 absl::optional<SelectedSegment> selected_segment) {
Xing Liu282dd0d2022-02-24 00:20:39156 ukm::SourceId source_id = ukm::NoURLSourceId();
157 ukm::builders::Segmentation_ModelExecution execution_result(source_id);
158 if (!AddInputsToUkm(&execution_result, segment_id, model_version,
159 input_tensor)) {
160 return ukm::kInvalidSourceId;
161 }
162
163 if (!AddOutputsToUkm(&execution_result, outputs, output_indexes)) {
164 return ukm::kInvalidSourceId;
165 }
166
ritikagupeaf525c2022-11-11 00:53:24167 if (prediction_result.has_value() && prediction_result->result_size() > 0) {
168 // TODO(ritikagup): Add support for uploading multiple outputs.
Min Qind52d150c2022-04-22 05:32:13169 execution_result.SetPredictionResult(
ritikagupeaf525c2022-11-11 00:53:24170 FloatToInt64(prediction_result->result()[0]));
Min Qind52d150c2022-04-22 05:32:13171 }
Min Qin642ab222022-05-19 21:54:53172 if (selected_segment.has_value()) {
173 execution_result.SetSelectionResult(selected_segment->segment_id);
174 execution_result.SetOutputDelaySec(
175 (base::Time::Now() - selected_segment->selection_time).InSeconds());
176 }
177
Xing Liu282dd0d2022-02-24 00:20:39178 execution_result.Record(ukm::UkmRecorder::Get());
179 return source_id;
180}
181
182bool SegmentationUkmHelper::AddInputsToUkm(
183 ukm::builders::Segmentation_ModelExecution* ukm_builder,
ssid8386fc72022-05-21 00:34:17184 SegmentId segment_id,
Xing Liu282dd0d2022-02-24 00:20:39185 int64_t model_version,
Jan Turecek9deb2252022-11-23 23:25:04186 const ModelProvider::Request& input_tensor) {
Xing Liu282dd0d2022-02-24 00:20:39187 if (input_tensor.size() > ARRAY_SIZE(kSegmentationUkmInputMethods)) {
188 // Don't record UKM if there are too many tensors.
189 stats::RecordTooManyInputTensors(input_tensor.size());
190 return false;
191 }
192
193 ukm_builder->SetOptimizationTarget(segment_id).SetModelVersion(model_version);
194 for (size_t i = 0; i < input_tensor.size(); ++i) {
195 CALL_MEMBER_FN(*ukm_builder, kSegmentationUkmInputMethods[i])
196 (FloatToInt64(input_tensor[i]));
197 }
198 return true;
199}
200
201bool SegmentationUkmHelper::AddOutputsToUkm(
202 ukm::builders::Segmentation_ModelExecution* ukm_builder,
Jan Turecek9deb2252022-11-23 23:25:04203 const ModelProvider::Response& outputs,
Xing Liu282dd0d2022-02-24 00:20:39204 const std::vector<int>& output_indexes) {
205 DCHECK(!outputs.empty());
206 if (outputs.size() != output_indexes.size())
207 return false;
208
209 const int output_methods_size = ARRAY_SIZE(kSegmentationUkmOutputMethods);
210 if (outputs.size() > output_methods_size)
211 return false;
212
213 for (size_t i = 0; i < outputs.size(); ++i) {
214 if (output_indexes[i] >= output_methods_size)
215 return false;
216 CALL_MEMBER_FN(*ukm_builder,
217 kSegmentationUkmOutputMethods[output_indexes[i]])
218 (FloatToInt64(outputs[i]));
219 }
220
221 return true;
222}
223
Min Qinf254aa92022-08-24 18:48:42224bool SegmentationUkmHelper::CanUploadTensors(
225 const proto::SegmentInfo& segment_info) const {
226 if (!base::FeatureList::IsEnabled(
227 features::kSegmentationStructuredMetricsFeature)) {
228 return false;
229 }
230 return segment_info.model_metadata().upload_tensors() ||
231 allowed_segment_ids_.contains(
232 static_cast<int>(segment_info.segment_id()));
233}
234
Min Qin75bee1b2022-02-05 03:57:34235// static
236int64_t SegmentationUkmHelper::FloatToInt64(float f) {
237 // Encode the float number in IEEE754 double precision.
Peter Kastingcc88ac052022-05-03 09:58:01238 return base::bit_cast<int64_t>(static_cast<double>(f));
Min Qin75bee1b2022-02-05 03:57:34239}
240
Min Qincae984cb2022-05-20 03:08:34241// static
242bool SegmentationUkmHelper::AllowedToUploadData(
243 base::TimeDelta signal_storage_length,
244 base::Clock* clock) {
Min Qin0b78c782022-05-21 01:42:13245 base::Time most_recent_allowed = LocalStateHelper::GetInstance().GetPrefTime(
246 kSegmentationUkmMostRecentAllowedTimeKey);
247 // If the local state is never set, return false.
248 if (most_recent_allowed.is_null() ||
249 most_recent_allowed == base::Time::Max()) {
250 return false;
251 }
252 return most_recent_allowed + signal_storage_length < clock->Now();
Min Qincae984cb2022-05-20 03:08:34253}
254
Min Qin75bee1b2022-02-05 03:57:34255} // namespace segmentation_platform