blob: df4a061dda2e2494688ba229444ca4ad9d49d437 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/segmentation_platform/internal/stats.h"
#include <string_view>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/lock.h"
#include "base/task/thread_pool.h"
#include "components/segmentation_platform/internal/post_processor/post_processor.h"
#include "components/segmentation_platform/public/constants.h"
#include "components/segmentation_platform/public/proto/model_metadata.pb.h"
#include "components/segmentation_platform/public/proto/output_config.pb.h"
#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
#include "components/segmentation_platform/public/proto/types.pb.h"
namespace segmentation_platform::stats {
namespace {
// Keep in sync with AdaptiveToolbarButtonVariant in enums.xml.
enum class AdaptiveToolbarButtonVariant {
kUnknown = 0,
kNone = 1,
kNewTab = 2,
kShare = 3,
kVoice = 4,
kMaxValue = kVoice,
};
// It should only used for legacy models without descriptors of return type in
// the metadata.
proto::SegmentationModelMetadata::OutputDescription
GetOptimizationTargetOutputDescription(SegmentId segment_id) {
switch (segment_id) {
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
case SegmentId::OPTIMIZATION_TARGET_CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING:
case SegmentId::OPTIMIZATION_TARGET_WEB_APP_INSTALLATION_PROMO:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION:
return proto::SegmentationModelMetadata::RETURN_TYPE_PROBABILITY;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SEARCH_USER:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_TABLET_PRODUCTIVITY_USER:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_IOS_MODULE_RANKER:
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_ANDROID_HOME_MODULE_RANKER:
return proto::SegmentationModelMetadata::RETURN_TYPE_MULTISEGMENT;
default:
return proto::SegmentationModelMetadata::UNKNOWN_RETURN_TYPE;
}
}
AdaptiveToolbarButtonVariant OptimizationTargetToAdaptiveToolbarButtonVariant(
SegmentId segment_id) {
switch (segment_id) {
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarButtonVariant::kNewTab;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarButtonVariant::kShare;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarButtonVariant::kVoice;
case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarButtonVariant::kNone;
default:
NOTREACHED();
}
}
BooleanSegmentSwitch GetBooleanSegmentSwitch(SegmentId new_selection,
SegmentId previous_selection) {
if (new_selection != SegmentId::OPTIMIZATION_TARGET_UNKNOWN &&
previous_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
return BooleanSegmentSwitch::kNoneToEnabled;
} else if (new_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN &&
previous_selection != SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
return BooleanSegmentSwitch::kEnabledToNone;
}
return BooleanSegmentSwitch::kUnknown;
}
AdaptiveToolbarSegmentSwitch GetAdaptiveToolbarSegmentSwitch(
SegmentId new_selection,
SegmentId previous_selection) {
switch (previous_selection) {
case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
switch (new_selection) {
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kNoneToNewTab;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kNoneToShare;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kNoneToVoice;
default:
NOTREACHED();
}
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
switch (new_selection) {
case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kNewTabToNone;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kNewTabToShare;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kNewTabToVoice;
default:
NOTREACHED();
}
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
switch (new_selection) {
case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kShareToNone;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kShareToNewTab;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kShareToVoice;
default:
NOTREACHED();
}
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
switch (new_selection) {
case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kVoiceToNone;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kVoiceToNewTab;
case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kVoiceToShare;
default:
NOTREACHED();
}
default:
NOTREACHED();
}
}
// Should map to ModelExecutionStatus variant string in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
std::optional<std::string_view> ModelExecutionStatusToHistogramVariant(
ModelExecutionStatus status) {
switch (status) {
case ModelExecutionStatus::kSuccess:
return "Success";
case ModelExecutionStatus::kExecutionError:
return "ExecutionError";
// Only record duration histograms when tflite model is executed. These
// cases mean the execution was skipped.
case ModelExecutionStatus::kSkippedInvalidMetadata:
case ModelExecutionStatus::kSkippedModelNotReady:
case ModelExecutionStatus::kSkippedHasFreshResults:
case ModelExecutionStatus::kSkippedNotEnoughSignals:
case ModelExecutionStatus::kSkippedResultNotExpired:
case ModelExecutionStatus::kFailedToSaveResultAfterSuccess:
return std::nullopt;
}
}
// Should map to SignalType variant string in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
std::string SignalTypeToHistogramVariant(proto::SignalType signal_type) {
switch (signal_type) {
case proto::SignalType::USER_ACTION:
return "UserAction";
case proto::SignalType::HISTOGRAM_ENUM:
return "HistogramEnum";
case proto::SignalType::HISTOGRAM_VALUE:
return "HistogramValue";
default:
NOTREACHED();
}
}
float ZeroValueFraction(const std::vector<float>& tensor) {
if (tensor.size() == 0)
return 0;
size_t zero_values = 0;
for (float feature : tensor) {
if (feature == 0)
++zero_values;
}
return static_cast<float>(zero_values) / static_cast<float>(tensor.size());
}
// For server models to keep the same name as before, empty string is returned.
std::string GetModelSourceAsString(proto::ModelSource model_source) {
// Should map to ModelSource variant string in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
return (model_source == proto::DEFAULT_MODEL_SOURCE ? "Default" : "");
}
} // namespace
void RecordModelUpdateTimeDifference(SegmentId segment_id,
int64_t model_update_time) {
// |model_update_time| might be empty for data persisted before M101.
if (model_update_time) {
base::Time model_updated_time = base::Time::FromDeltaSinceWindowsEpoch(
base::Seconds(model_update_time));
base::UmaHistogramCounts1000(
"SegmentationPlatform.Init.ModelUpdatedTimeDifferenceInDays." +
SegmentIdToHistogramVariant(segment_id),
(base::Time::Now() - model_updated_time).InDays());
}
}
void RecordSegmentSelectionComputed(
const Config& config,
SegmentId new_selection,
std::optional<SegmentId> previous_selection) {
// Special case adaptive toolbar since it already has histograms being
// recorded and updating names will affect current work.
if (config.segmentation_key == kAdaptiveToolbarSegmentationKey) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.AdaptiveToolbar.SegmentSelection.Computed",
OptimizationTargetToAdaptiveToolbarButtonVariant(new_selection));
}
std::string computed_hist =
base::StrCat({"SegmentationPlatform.", config.segmentation_uma_name,
".SegmentSelection.Computed2"});
base::UmaHistogramSparse(computed_hist, new_selection);
SegmentId prev_segment = previous_selection.has_value()
? previous_selection.value()
: SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
if (prev_segment == new_selection || !config.auto_execute_and_cache) {
return;
}
std::string switched_hist =
base::StrCat({"SegmentationPlatform.", config.segmentation_uma_name,
".SegmentSwitched"});
if (config.segmentation_key == kAdaptiveToolbarSegmentationKey) {
base::UmaHistogramEnumeration(
switched_hist,
GetAdaptiveToolbarSegmentSwitch(new_selection, prev_segment));
} else if (config.is_boolean_segment) {
base::UmaHistogramEnumeration(
switched_hist, GetBooleanSegmentSwitch(new_selection, prev_segment));
}
// Do not record switched histogram for all keys by default, the client needs
// to write custom logic for other kinds of segments.
}
void RecordClassificationResultComputed(
const Config& config,
const proto::PredictionResult& new_result) {
if (new_result.output_config().predictor().PredictorType_case() ==
proto::Predictor::kGenericPredictor) {
return;
}
PostProcessor post_processor;
int new_result_top_label = post_processor.GetIndexOfTopLabel(new_result);
std::string computed_hist =
base::StrCat({"SegmentationPlatform.", config.segmentation_uma_name,
".PostProcessing.TopLabel.Computed"});
base::UmaHistogramSparse(computed_hist, new_result_top_label);
}
void RecordClassificationResultUpdated(
const Config& config,
const proto::PredictionResult* old_result,
const proto::PredictionResult& new_result) {
PostProcessor post_processor;
int new_result_top_label = post_processor.GetIndexOfTopLabel(new_result);
int old_result_top_label =
old_result ? post_processor.GetIndexOfTopLabel(*old_result) : -2;
if (old_result_top_label == new_result_top_label) {
return;
}
std::string switched_hist =
base::StrCat({"SegmentationPlatform.", config.segmentation_uma_name,
".PostProcessing.TopLabel.Switched"});
// There is no easy way to record this metric for label switch. So we encode
// it as follows: Multiply the index value of the old value by 100 and add the
// new index value. Note, there might be negative integers, but regardless
// this will generate a unique value for each type of label switch.
// For example, for a 3-label case, any transition will look like
// none -> label 0 : -200
// none -> label 1 : -199
// none -> label 2 : -198
// label 0 -> none : -2
// label 0 -> label 1 : 1
// label 0 -> label 2 : 2
// label 1 -> none : 98
// label 1 -> label 0 : 100
// label 1 -> label 2 : 102
// label 2 -> none : 198
// label 2 -> label 0 : 200
// label 2 -> label 1 : 201
int switch_value = old_result_top_label * 100 + new_result_top_label;
base::UmaHistogramSparse(switched_hist, switch_value);
}
void RecordMaintenanceCleanupSignalSuccessCount(size_t count) {
UMA_HISTOGRAM_COUNTS_1000(
"SegmentationPlatform.Maintenance.CleanupSignalSuccessCount", count);
}
void RecordMaintenanceCompactionResult(proto::SignalType signal_type,
bool success) {
base::UmaHistogramBoolean(
"SegmentationPlatform.Maintenance.CompactionResult." +
SignalTypeToHistogramVariant(signal_type),
success);
}
void RecordMaintenanceSignalIdentifierCount(size_t count) {
UMA_HISTOGRAM_COUNTS_1000(
"SegmentationPlatform.Maintenance.SignalIdentifierCount", count);
}
void RecordModelDeliveryHasMetadata(SegmentId segment_id, bool has_metadata) {
base::UmaHistogramBoolean("SegmentationPlatform.ModelDelivery.HasMetadata." +
SegmentIdToHistogramVariant(segment_id),
has_metadata);
}
void RecordModelDeliveryMetadataFeatureCount(SegmentId segment_id,
ModelSource model_source,
size_t count) {
base::UmaHistogramCounts1000("SegmentationPlatform." +
GetModelSourceAsString(model_source) +
"ModelDelivery.Metadata.FeatureCount." +
SegmentIdToHistogramVariant(segment_id),
count);
}
void RecordModelDeliveryMetadataValidation(
SegmentId segment_id,
proto::ModelSource model_source,
bool processed,
metadata_utils::ValidationResult validation_result) {
// Should map to ValidationPhase variant string in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
std::string validation_phase = processed ? "Processed" : "Incoming";
base::UmaHistogramEnumeration(
"SegmentationPlatform." + GetModelSourceAsString(model_source) +
"ModelDelivery.Metadata.Validation." + validation_phase + "." +
SegmentIdToHistogramVariant(segment_id),
validation_result);
}
void RecordModelDeliveryReceived(SegmentId segment_id,
proto::ModelSource model_source) {
base::UmaHistogramSparse("SegmentationPlatform." +
GetModelSourceAsString(model_source) +
"ModelDelivery.Received",
segment_id);
}
void RecordModelDeliverySaveResult(SegmentId segment_id,
proto::ModelSource model_source,
bool success) {
base::UmaHistogramBoolean(
"SegmentationPlatform." + GetModelSourceAsString(model_source) +
"ModelDelivery.SaveResult." + SegmentIdToHistogramVariant(segment_id),
success);
}
void RecordModelDeliveryDeleteResult(SegmentId segment_id,
proto::ModelSource model_source,
bool success) {
base::UmaHistogramBoolean("SegmentationPlatform." +
GetModelSourceAsString(model_source) +
"ModelDelivery.DeleteResult." +
SegmentIdToHistogramVariant(segment_id),
success);
}
void RecordModelDeliverySegmentIdMatches(SegmentId segment_id,
proto::ModelSource model_source,
bool matches) {
base::UmaHistogramBoolean("SegmentationPlatform." +
GetModelSourceAsString(model_source) +
"ModelDelivery.SegmentIdMatches." +
SegmentIdToHistogramVariant(segment_id),
matches);
}
void RecordModelExecutionDurationFeatureProcessing(SegmentId segment_id,
base::TimeDelta duration) {
base::UmaHistogramTimes(
"SegmentationPlatform.ModelExecution.Duration.FeatureProcessing." +
SegmentIdToHistogramVariant(segment_id),
duration);
}
void RecordModelExecutionDurationModel(SegmentId segment_id,
bool success,
base::TimeDelta duration) {
ModelExecutionStatus status = success ? ModelExecutionStatus::kSuccess
: ModelExecutionStatus::kExecutionError;
std::optional<std::string_view> status_variant =
ModelExecutionStatusToHistogramVariant(status);
if (!status_variant)
return;
base::UmaHistogramTimes(
base::StrCat({"SegmentationPlatform.ModelExecution.Duration.Model.",
SegmentIdToHistogramVariant(segment_id), ".",
*status_variant}),
duration);
}
void RecordModelExecutionDurationTotal(SegmentId segment_id,
ModelExecutionStatus status,
base::TimeDelta duration) {
std::optional<std::string_view> status_variant =
ModelExecutionStatusToHistogramVariant(status);
if (!status_variant)
return;
base::UmaHistogramTimes(
base::StrCat({"SegmentationPlatform.ModelExecution.Duration.Total.",
SegmentIdToHistogramVariant(segment_id), ".",
*status_variant}),
duration);
}
void RecordClassificationRequestTotalDuration(const Config& config,
base::TimeDelta duration) {
std::string histogram_name =
base::StrCat({"SegmentationPlatform.ClassificationRequest.TotalDuration.",
config.segmentation_uma_name});
base::UmaHistogramTimes(histogram_name, duration);
}
void RecordOnDemandSegmentSelectionDuration(
const Config& config,
const SegmentSelectionResult& result,
base::TimeDelta duration) {
std::string histogram_prefix =
base::StrCat({"SegmentationPlatform.SegmentSelectionOnDemand.Duration.",
config.segmentation_uma_name});
base::UmaHistogramTimes(base::StrCat({histogram_prefix, "Any"}), duration);
}
void RecordModelExecutionResult(
SegmentId segment_id,
float result,
proto::SegmentationModelMetadata::OutputDescription return_type) {
if (return_type == proto::SegmentationModelMetadata::UNKNOWN_RETURN_TYPE) {
return_type = GetOptimizationTargetOutputDescription(segment_id);
}
if (return_type ==
proto::SegmentationModelMetadata::RETURN_TYPE_MULTISEGMENT) {
// This type of model return score between 0 and 100.
base::UmaHistogramPercentage("SegmentationPlatform.ModelExecution.Result." +
SegmentIdToHistogramVariant(segment_id),
base::ClampRound(result));
return;
} else if (return_type ==
proto::SegmentationModelMetadata::RETURN_TYPE_INTEGER) {
// This type of model return an unbound float score.
base::UmaHistogramPercentage("SegmentationPlatform.ModelExecution.Result." +
SegmentIdToHistogramVariant(segment_id),
static_cast<int>(result));
return;
}
// All other models type return score between 0 and 1.
base::UmaHistogramPercentage("SegmentationPlatform.ModelExecution.Result." +
SegmentIdToHistogramVariant(segment_id),
result * 100);
}
void RecordModelExecutionResult(SegmentId segment_id,
const ModelProvider::Response& result,
proto::OutputConfig output_config) {
// Only for binary and multi-class classifier, we treat the score as a
// probability score and multiply by 100. For others, it's kept as is.
bool is_probability_score = false;
switch (output_config.predictor().PredictorType_case()) {
case proto::Predictor::kBinaryClassifier:
[[fallthrough]];
case proto::Predictor::kMultiClassClassifier:
is_probability_score = true;
break;
case proto::Predictor::kBinnedClassifier:
[[fallthrough]];
case proto::Predictor::kRegressor:
[[fallthrough]];
case proto::Predictor::kGenericPredictor:
is_probability_score = false;
break;
default:
NOTREACHED();
}
for (size_t i = 0; i < result.size(); i++) {
std::string histogram_name = "SegmentationPlatform.ModelExecution.Result." +
base::NumberToString(i) + "." +
SegmentIdToHistogramVariant(segment_id);
int scaled_model_score = is_probability_score ? result[i] * 100 : result[i];
base::UmaHistogramPercentage(histogram_name, scaled_model_score);
}
}
void RecordModelExecutionSaveResult(SegmentId segment_id, bool success) {
base::UmaHistogramBoolean("SegmentationPlatform.ModelExecution.SaveResult." +
SegmentIdToHistogramVariant(segment_id),
success);
}
void RecordModelExecutionStatus(SegmentId segment_id,
bool default_provider,
ModelExecutionStatus status) {
if (!default_provider) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.ModelExecution.Status." +
SegmentIdToHistogramVariant(segment_id),
status);
} else {
base::UmaHistogramEnumeration(
"SegmentationPlatform.ModelExecution.DefaultProvider.Status." +
SegmentIdToHistogramVariant(segment_id),
status);
}
}
void RecordModelExecutionZeroValuePercent(SegmentId segment_id,
const std::vector<float>& tensor) {
BackgroundUmaRecorder::GetInstance().AddMetric(base::BindOnce(
[](const std::string& name, int value) {
base::UmaHistogramPercentage(name, value);
},
"SegmentationPlatform.ModelExecution.ZeroValuePercent." +
SegmentIdToHistogramVariant(segment_id),
ZeroValueFraction(tensor) * 100));
}
void RecordSignalDatabaseGetSamplesDatabaseEntryCount(size_t count) {
UMA_HISTOGRAM_COUNTS_1000(
"SegmentationPlatform.SignalDatabase.GetSamples.DatabaseEntryCount",
count);
}
void RecordSignalDatabaseGetSamplesResult(bool success) {
UMA_HISTOGRAM_BOOLEAN("SegmentationPlatform.SignalDatabase.GetSamples.Result",
success);
}
void RecordSignalDatabaseGetSamplesSampleCount(size_t count) {
UMA_HISTOGRAM_COUNTS_10000(
"SegmentationPlatform.SignalDatabase.GetSamples.SampleCount", count);
}
void RecordSegmentInfoDatabaseUpdateEntriesResult(SegmentId segment_id,
bool success) {
base::UmaHistogramBoolean(
"SegmentationPlatform.SegmentInfoDatabase.ProtoDBUpdateResult." +
SegmentIdToHistogramVariant(segment_id),
success);
}
void RecordSignalsListeningCount(
const std::set<uint64_t>& user_actions,
const std::set<std::pair<std::string, proto::SignalType>>& histograms) {
uint64_t user_action_count = user_actions.size();
uint64_t histogram_enum_count = 0;
uint64_t histogram_value_count = 0;
for (auto& s : histograms) {
if (s.second == proto::SignalType::HISTOGRAM_ENUM)
++histogram_enum_count;
if (s.second == proto::SignalType::HISTOGRAM_VALUE)
++histogram_value_count;
}
base::UmaHistogramCounts1000(
"SegmentationPlatform.Signals.ListeningCount." +
SignalTypeToHistogramVariant(proto::SignalType::USER_ACTION),
user_action_count);
base::UmaHistogramCounts1000(
"SegmentationPlatform.Signals.ListeningCount." +
SignalTypeToHistogramVariant(proto::SignalType::HISTOGRAM_ENUM),
histogram_enum_count);
base::UmaHistogramCounts1000(
"SegmentationPlatform.Signals.ListeningCount." +
SignalTypeToHistogramVariant(proto::SignalType::HISTOGRAM_VALUE),
histogram_value_count);
}
void RecordSegmentSelectionFailure(const Config& config,
SegmentationSelectionFailureReason reason) {
base::UmaHistogramEnumeration(
base::StrCat({"SegmentationPlatform.SelectionFailedReason.",
config.segmentation_uma_name}),
reason);
}
std::string FeatureProcessingErrorToString(FeatureProcessingError error) {
switch (error) {
case FeatureProcessingError::kUkmEngineDisabled:
return "UkmEngineDisabled";
case FeatureProcessingError::kUmaValidationError:
return "UmaValidationError";
case FeatureProcessingError::kSqlValidationError:
return "SqlValidationError";
case FeatureProcessingError::kCustomInputError:
return "CustomInputError";
case FeatureProcessingError::kSqlBindValuesError:
return "SqlBindValuesError";
case FeatureProcessingError::kSqlQueryRunError:
return "SqlQueryRunError";
case FeatureProcessingError::kResultTensorError:
return "ResultTensorError";
default:
return "Other";
}
}
void RecordFeatureProcessingError(SegmentId segment_id,
FeatureProcessingError error) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.FeatureProcessing.Error." +
SegmentIdToHistogramVariant(segment_id),
error);
}
void RecordModelAvailability(SegmentId segment_id,
SegmentationModelAvailability availability) {
base::UmaHistogramEnumeration("SegmentationPlatform.ModelAvailability." +
SegmentIdToHistogramVariant(segment_id),
availability);
}
void RecordTooManyInputTensors(int tensor_size) {
UMA_HISTOGRAM_COUNTS_100(
"SegmentationPlatform.StructuredMetrics.TooManyTensors.Count",
tensor_size);
}
std::string TrainingDataCollectionEventToErrorMsg(
TrainingDataCollectionEvent event) {
switch (event) {
case TrainingDataCollectionEvent::kImmediateCollectionStart:
return "Immediate Collection Start";
case TrainingDataCollectionEvent::kImmediateCollectionSuccess:
return "Immediate Collection Success";
case TrainingDataCollectionEvent::kModelInfoMissing:
return "Model Info Missing";
case TrainingDataCollectionEvent::kMetadataValidationFailed:
return "Metadata Validation Failed";
case TrainingDataCollectionEvent::kGetInputTensorsFailed:
return "Get Input Tensors Failed";
case TrainingDataCollectionEvent::kNotEnoughCollectionTime:
return "Not Enough Collection Time";
case TrainingDataCollectionEvent::kUkmReportingFailed:
return "UKM Reporting Failed";
case TrainingDataCollectionEvent::kPartialDataNotAllowed:
return "Partial Data Not Allowed";
case TrainingDataCollectionEvent::kContinousCollectionStart:
return "Continuous Collection Start";
case TrainingDataCollectionEvent::kContinousCollectionSuccess:
return "Continuous Collection Success";
case TrainingDataCollectionEvent::kCollectAndStoreInputsSuccess:
return "Collect and Store Inputs Success";
case TrainingDataCollectionEvent::kObservationTimeReached:
return "Observation Time Reached";
case TrainingDataCollectionEvent::kDelayedTaskPosted:
return "Delayed Task Posted";
case TrainingDataCollectionEvent::kImmediateObservationPosted:
return "Immediate Observation Posted";
case TrainingDataCollectionEvent::kWaitingForNonDelayedTrigger:
return "Waiting for Non Delayed Trigger";
case TrainingDataCollectionEvent::kHistogramTriggerHit:
return "Histogram Trigger Hit";
case TrainingDataCollectionEvent::kNoSegmentInfo:
return "No Segment Info";
case TrainingDataCollectionEvent::kDisallowedForRecording:
return "Disallowed for Recording";
case TrainingDataCollectionEvent::kObservationDisallowed:
return "Observation Disallowed";
case TrainingDataCollectionEvent::kTrainingDataMissing:
return "Training Data Missing";
case TrainingDataCollectionEvent::kOnDecisionTimeTypeMistmatch:
return "On Decision Time Type Mismatch";
case TrainingDataCollectionEvent::kDelayTriggerSampled:
return "Delay Trigger Sampled";
case TrainingDataCollectionEvent::
kContinousExactPredictionTimeCollectionStart:
return "Continuous Exact Prediction Time Collection Start";
case TrainingDataCollectionEvent::
kContinousExactPredictionTimeCollectionSuccess:
return "Continuous Exact Prediction Time Collection Success";
default:
return "";
}
}
void RecordTrainingDataCollectionEvent(SegmentId segment_id,
TrainingDataCollectionEvent event) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.TrainingDataCollectionEvents." +
SegmentIdToHistogramVariant(segment_id),
event);
VLOG(1) << "Training Data event for "
<< SegmentIdToHistogramVariant(segment_id) << ": "
<< TrainingDataCollectionEventToErrorMsg(event);
}
// This conversion exists because segment selector uses the result state
// differently. TODO(ritikagup): Remove this conversion when selector is
// deleted.
SegmentationSelectionFailureReason GetSuccessOrFailureReason(
SegmentResultProvider::ResultState result_state) {
switch (result_state) {
case SegmentResultProvider::ResultState::kUnknown:
NOTREACHED();
case SegmentResultProvider::ResultState::kServerModelDatabaseScoreUsed:
return SegmentationSelectionFailureReason::kServerModelDatabaseScoreUsed;
case SegmentResultProvider::ResultState::kDefaultModelDatabaseScoreUsed:
return SegmentationSelectionFailureReason::kDefaultModelDatabaseScoreUsed;
case SegmentResultProvider::ResultState::kDefaultModelExecutionScoreUsed:
return SegmentationSelectionFailureReason::
kDefaultModelExecutionScoreUsed;
case SegmentResultProvider::ResultState::kServerModelExecutionScoreUsed:
return SegmentationSelectionFailureReason::kServerModelExecutionScoreUsed;
case SegmentResultProvider::ResultState::kDefaultModelDatabaseScoreNotReady:
return SegmentationSelectionFailureReason::
kDefaultModelDatabaseScoreNotReady;
case SegmentResultProvider::ResultState::kServerModelDatabaseScoreNotReady:
return SegmentationSelectionFailureReason::
kServerModelDatabaseScoreNotReady;
case SegmentResultProvider::ResultState::
kDefaultModelSegmentInfoNotAvailable:
return SegmentationSelectionFailureReason::
kDefaultModelSegmentInfoNotAvailable;
case SegmentResultProvider::ResultState::
kServerModelSegmentInfoNotAvailable:
return SegmentationSelectionFailureReason::
kServerModelSegmentInfoNotAvailable;
case SegmentResultProvider::ResultState::kDefaultModelSignalsNotCollected:
return SegmentationSelectionFailureReason::
kDefaultModelSignalsNotCollected;
case SegmentResultProvider::ResultState::kServerModelSignalsNotCollected:
return SegmentationSelectionFailureReason::
kServerModelSignalsNotCollected;
case SegmentResultProvider::ResultState::kDefaultModelExecutionFailed:
return SegmentationSelectionFailureReason::kDefaultModelExecutionFailed;
case SegmentResultProvider::ResultState::kServerModelExecutionFailed:
return SegmentationSelectionFailureReason::kServerModelExecutionFailed;
}
}
// static
BackgroundUmaRecorder& BackgroundUmaRecorder::GetInstance() {
static base::NoDestructor<BackgroundUmaRecorder> instance;
return *instance;
}
BackgroundUmaRecorder::BackgroundUmaRecorder() = default;
BackgroundUmaRecorder::~BackgroundUmaRecorder() = default;
void BackgroundUmaRecorder::Initialize() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
if (!bg_task_runner_) {
// Mark user visible priority so that lock held on bg thread will not block
// main thread.
bg_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE});
}
}
void BackgroundUmaRecorder::InitializeForTesting(
scoped_refptr<base::SequencedTaskRunner> bg_task_runner) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
bg_task_runner_ = bg_task_runner;
}
void BackgroundUmaRecorder::AddMetric(base::OnceClosure add_sample) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_check_);
{
base::AutoLock l(lock_);
if (bg_task_runner_) {
add_samples_.push_back(std::move(add_sample));
if (!pending_task_) {
pending_task_ = true;
bg_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&BackgroundUmaRecorder::FlushSamples,
weak_factory_.GetWeakPtr()),
kMetricsCollectionDelay);
}
return;
}
}
std::move(add_sample).Run();
}
void BackgroundUmaRecorder::FlushSamples() {
std::list<base::OnceClosure> samples;
{
base::AutoLock l(lock_);
samples.swap(add_samples_);
pending_task_ = false;
}
for (auto& it : samples) {
std::move(it).Run();
}
}
} // namespace segmentation_platform::stats