blob: 7cf506acd00eb02bf83824801332943610d54f4d [file] [log] [blame]
// Copyright 2021 The Chromium 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 "components/segmentation_platform/internal/stats.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "components/segmentation_platform/public/config.h"
namespace segmentation_platform {
namespace stats {
namespace {
// Should map to SegmentationModel variant in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
std::string OptimizationTargetToHistogramVariant(
OptimizationTarget segment_id) {
switch (segment_id) {
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return "NewTab";
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return "Share";
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return "Voice";
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
return "Dummy";
case OptimizationTarget::
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
return "ChromeStartAndroid";
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
return "QueryTiles";
default:
NOTREACHED();
return "Unknown";
}
}
// Keep in sync with AdaptiveToolbarButtonVariant in enums.xml.
enum class AdaptiveToolbarButtonVariant {
kUnknown = 0,
kNone = 1,
kNewTab = 2,
kShare = 3,
kVoice = 4,
kMaxValue = kVoice,
};
// This is the segmentation subset of
// optimization_guide::proto::OptimizationTarget.
// Keep in sync with SegmentationPlatformSegmenationModel in
// //tools/metrics/histograms/enums.xml.
// See also SegmentationModel variant in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
enum class SegmentationModel {
kUnknown = 0,
kNewTab = 4,
kShare = 5,
kVoice = 6,
kDummy = 10,
kChromeStartAndroid = 11,
kQueryTiles = 12,
kMaxValue = kQueryTiles,
};
AdaptiveToolbarButtonVariant OptimizationTargetToAdaptiveToolbarButtonVariant(
OptimizationTarget segment_id) {
switch (segment_id) {
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarButtonVariant::kNewTab;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarButtonVariant::kShare;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarButtonVariant::kVoice;
case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarButtonVariant::kNone;
default:
return AdaptiveToolbarButtonVariant::kUnknown;
}
}
bool IsBooleanSegment(const std::string& segmentation_key) {
return segmentation_key == kChromeStartAndroidSegmentationKey ||
segmentation_key == kQueryTilesSegmentationKey;
}
BooleanSegmentSwitch GetBooleanSegmentSwitch(
OptimizationTarget new_selection,
OptimizationTarget previous_selection) {
if (new_selection != OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN &&
previous_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
return BooleanSegmentSwitch::kNoneToEnabled;
} else if (new_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN &&
previous_selection !=
OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
return BooleanSegmentSwitch::kEnabledToNone;
}
return BooleanSegmentSwitch::kUnknown;
}
AdaptiveToolbarSegmentSwitch GetAdaptiveToolbarSegmentSwitch(
OptimizationTarget new_selection,
OptimizationTarget previous_selection) {
switch (previous_selection) {
case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
switch (new_selection) {
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kNoneToNewTab;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kNoneToShare;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kNoneToVoice;
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
switch (new_selection) {
case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kNewTabToNone;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kNewTabToShare;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kNewTabToVoice;
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
switch (new_selection) {
case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kShareToNone;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kShareToNewTab;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return AdaptiveToolbarSegmentSwitch::kShareToVoice;
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
switch (new_selection) {
case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
return AdaptiveToolbarSegmentSwitch::kVoiceToNone;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return AdaptiveToolbarSegmentSwitch::kVoiceToNewTab;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return AdaptiveToolbarSegmentSwitch::kVoiceToShare;
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
default:
NOTREACHED();
return AdaptiveToolbarSegmentSwitch::kUnknown;
}
}
SegmentationModel OptimizationTargetToSegmentationModel(
OptimizationTarget segment_id) {
switch (segment_id) {
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
return SegmentationModel::kNewTab;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
return SegmentationModel::kShare;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
return SegmentationModel::kVoice;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
return SegmentationModel::kDummy;
case OptimizationTarget::
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
return SegmentationModel::kChromeStartAndroid;
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
return SegmentationModel::kQueryTiles;
default:
return SegmentationModel::kUnknown;
}
}
// Should map to ModelExecutionStatus variant string in
// //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
std::string ModelExecutionStatusToHistogramVariant(
ModelExecutionStatus status) {
switch (status) {
case ModelExecutionStatus::kSuccess:
return "Success";
case ModelExecutionStatus::kExecutionError:
return "ExecutionError";
case ModelExecutionStatus::kInvalidMetadata:
return "InvalidMetadata";
default:
NOTREACHED();
return "Unknown";
}
}
// 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();
return "Unknown";
}
}
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());
}
const char* SegmentationKeyToUmaName(const std::string& segmentation_key) {
if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
return "AdaptiveToolbar";
} else if (segmentation_key == kDummySegmentationKey) {
return "DummyFeature";
} else if (segmentation_key == kChromeStartAndroidSegmentationKey) {
return "ChromeStartAndroid";
} else if (segmentation_key == kQueryTilesSegmentationKey) {
return "QueryTiles";
} else if (base::StartsWith(segmentation_key, "test_key")) {
return "TestKey";
}
NOTREACHED();
return "Unknown";
}
} // namespace
void RecordModelScore(OptimizationTarget segment_id, float score) {
// Special case adaptive toolbar models since it already has histograms being
// recorded and updating names will affect current work.
switch (segment_id) {
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
base::UmaHistogramPercentage(
"SegmentationPlatform.AdaptiveToolbar.ModelScore." +
OptimizationTargetToHistogramVariant(segment_id),
score * 100);
break;
default:
break;
}
switch (segment_id) {
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
case OptimizationTarget::
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
// Assumes all models return score between 0 and 1. This is true for all
// the models we have currently.
base::UmaHistogramPercentage(
"SegmentationPlatform.ModelScore." +
OptimizationTargetToHistogramVariant(segment_id),
score * 100);
break;
default:
break;
}
}
void RecordSegmentSelectionComputed(
const std::string& segmentation_key,
OptimizationTarget new_selection,
absl::optional<OptimizationTarget> previous_selection) {
// Special case adaptive toolbar since it already has histograms being
// recorded and updating names will affect current work.
if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.AdaptiveToolbar.SegmentSelection.Computed",
OptimizationTargetToAdaptiveToolbarButtonVariant(new_selection));
}
std::string computed_hist = base::StrCat(
{"SegmentationPlatform.", SegmentationKeyToUmaName(segmentation_key),
".SegmentSelection.Computed2"});
base::UmaHistogramEnumeration(
computed_hist, OptimizationTargetToSegmentationModel(new_selection));
OptimizationTarget prev_segment =
previous_selection.has_value()
? previous_selection.value()
: OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
if (prev_segment == new_selection)
return;
std::string switched_hist = base::StrCat(
{"SegmentationPlatform.", SegmentationKeyToUmaName(segmentation_key),
".SegmentSwitched"});
if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
base::UmaHistogramEnumeration(
switched_hist,
GetAdaptiveToolbarSegmentSwitch(new_selection, prev_segment));
} else if (IsBooleanSegment(segmentation_key)) {
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 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(OptimizationTarget segment_id,
bool has_metadata) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelDelivery.HasMetadata." +
OptimizationTargetToHistogramVariant(segment_id),
has_metadata);
}
void RecordModelDeliveryMetadataFeatureCount(OptimizationTarget segment_id,
size_t count) {
base::UmaHistogramCounts1000(
"SegmentationPlatform.ModelDelivery.Metadata.FeatureCount." +
OptimizationTargetToHistogramVariant(segment_id),
count);
}
void RecordModelDeliveryMetadataValidation(
OptimizationTarget segment_id,
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.ModelDelivery.Metadata.Validation." +
validation_phase + "." +
OptimizationTargetToHistogramVariant(segment_id),
validation_result);
}
void RecordModelDeliveryReceived(OptimizationTarget segment_id) {
UMA_HISTOGRAM_ENUMERATION("SegmentationPlatform.ModelDelivery.Received",
OptimizationTargetToSegmentationModel(segment_id));
}
void RecordModelDeliverySaveResult(OptimizationTarget segment_id,
bool success) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelDelivery.SaveResult." +
OptimizationTargetToHistogramVariant(segment_id),
success);
}
void RecordModelDeliverySegmentIdMatches(OptimizationTarget segment_id,
bool matches) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelDelivery.SegmentIdMatches." +
OptimizationTargetToHistogramVariant(segment_id),
matches);
}
void RecordModelExecutionDurationFeatureProcessing(
OptimizationTarget segment_id,
base::TimeDelta duration) {
base::UmaHistogramTimes(
"SegmentationPlatform.ModelExecution.Duration.FeatureProcessing." +
OptimizationTargetToHistogramVariant(segment_id),
duration);
}
void RecordModelExecutionDurationModel(OptimizationTarget segment_id,
bool success,
base::TimeDelta duration) {
ModelExecutionStatus status = success ? ModelExecutionStatus::kSuccess
: ModelExecutionStatus::kExecutionError;
base::UmaHistogramTimes(
"SegmentationPlatform.ModelExecution.Duration.Model." +
OptimizationTargetToHistogramVariant(segment_id) + "." +
ModelExecutionStatusToHistogramVariant(status),
duration);
}
void RecordModelExecutionDurationTotal(OptimizationTarget segment_id,
ModelExecutionStatus status,
base::TimeDelta duration) {
base::UmaHistogramTimes(
"SegmentationPlatform.ModelExecution.Duration.Total." +
OptimizationTargetToHistogramVariant(segment_id) + "." +
ModelExecutionStatusToHistogramVariant(status),
duration);
}
void RecordModelExecutionResult(OptimizationTarget segment_id, float result) {
base::UmaHistogramPercentage(
"SegmentationPlatform.ModelExecution.Result." +
OptimizationTargetToHistogramVariant(segment_id),
result * 100);
}
void RecordModelExecutionSaveResult(OptimizationTarget segment_id,
bool success) {
base::UmaHistogramBoolean(
"SegmentationPlatform.ModelExecution.SaveResult." +
OptimizationTargetToHistogramVariant(segment_id),
success);
}
void RecordModelExecutionStatus(OptimizationTarget segment_id,
ModelExecutionStatus status) {
base::UmaHistogramEnumeration(
"SegmentationPlatform.ModelExecution.Status." +
OptimizationTargetToHistogramVariant(segment_id),
status);
}
void RecordModelExecutionZeroValuePercent(OptimizationTarget segment_id,
const std::vector<float>& tensor) {
base::UmaHistogramPercentage(
"SegmentationPlatform.ModelExecution.ZeroValuePercent." +
OptimizationTargetToHistogramVariant(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 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);
}
} // namespace stats
} // namespace segmentation_platform