1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/metrics/chrome_browser_sampling_trials.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
#include "chrome/common/channel_info.h"
#include "components/ukm/ukm_recorder_impl.h"
#include "components/version_info/channel.h"
namespace metrics {
namespace {
// Note that the trial name must be kept in sync with the server config
// controlling sampling. If they don't match, then clients will be shuffled into
// different groups when the server config takes over from the fallback trial.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
constexpr char kSamplingTrialName[] = "MetricsAndCrashSampling";
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID)
constexpr char kPostFREFixSamplingTrialName[] =
"PostFREFixMetricsAndCrashSampling";
#endif // BUILDFLAG(IS_ANDROID)
constexpr char kUkmSamplingTrialName[] = "UkmSamplingRate";
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
// Appends a group to the sampling controlling |trial|. The group will be
// associated with a variation param for reporting sampling |rate| in per mille.
void AppendSamplingTrialGroup(const std::string& group_name,
int rate,
bool disable_crashes,
base::FieldTrial* trial) {
std::map<std::string, std::string> params = {
{metrics::internal::kRateParamName, base::NumberToString(rate)}};
if (disable_crashes) {
params.insert({"disable_crashes", "true"});
}
base::AssociateFieldTrialParams(trial->trial_name(), group_name, params);
trial->AppendGroup(group_name, rate);
}
// Unconditionally attempts to create a field trial to control client side
// metrics/crash sampling to use as a fallback when one hasn't been
// provided. This is expected to occur on first-run on platforms that don't
// have first-run variations support, or when no valid seed is available. This
// should only be called when there is no existing field trial controlling the
// sampling feature. |feature_name| is the name of the feature that determines
// sampling.
// Rates:
// |sampled_in_rate_per_mille| is the sampling rate per mille.
// |reporting_full_rate_per_mille| is the rate for clients who are sampled in,
// in the special ReportingFull group.
// All other clients are in the OutOfSampling group.
void CreateFallbackSamplingTrial(
const base::FieldTrial::EntropyProvider& entropy_provider,
const std::string& trial_name,
const std::string& feature_name,
const int sampled_in_rate_per_mille,
const int reporting_full_rate_per_mille,
const bool starts_active,
base::FeatureList* feature_list) {
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
trial_name, /*total_probability=*/1000, "Default", entropy_provider));
// Like the trial name, the order that these groups are added to the trial
// must be kept in sync with the order that they appear in the server config.
// The desired order is: OutOfReportingSample, ReportingFull,
// InReportingSample.
const char kSampledOutGroup[] = "OutOfReportingSample";
const int sampled_out_rate_per_mille =
1000 - sampled_in_rate_per_mille - reporting_full_rate_per_mille;
AppendSamplingTrialGroup(kSampledOutGroup, sampled_out_rate_per_mille,
/*disable_crashes=*/false, trial.get());
// This group uploads to UMA but does not upload crashes.
const char kReportingFullGroup[] = "ReportingFull";
AppendSamplingTrialGroup(kReportingFullGroup, reporting_full_rate_per_mille,
/*disable_crashes=*/true, trial.get());
const char kInSampleGroup[] = "InReportingSample";
AppendSamplingTrialGroup(kInSampleGroup, sampled_in_rate_per_mille,
/*disable_crashes=*/false, trial.get());
// Set up the feature. This must be done after all groups are added since
// GetGroupNameWithoutActivation() will finalize the group choice.
const std::string& group_name = trial->GetGroupNameWithoutActivation();
// Note that this will set both ReportingFull and InReportingSample to enable
// the feature.
feature_list->RegisterFieldTrialOverride(
feature_name,
group_name == kSampledOutGroup
? base::FeatureList::OVERRIDE_DISABLE_FEATURE
: base::FeatureList::OVERRIDE_ENABLE_FEATURE,
trial.get());
if (starts_active) {
trial->Activate();
}
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
// Unconditionally attempts to create a field trial to control client side
// UKM sampling to use as a fallback when one hasn't been provided. This is
// expected to occur on first-run on platforms that don't have first-run
// variations support. This should only be called when there is no existing
// field trial controlling the sampling feature.
void CreateFallbackUkmSamplingTrial(
const base::FieldTrial::EntropyProvider& entropy_provider,
bool is_stable_channel,
base::FeatureList* feature_list) {
static const char kSampledGroup_Stable[] = "Sampled_NoSeed_Stable";
static const char kSampledGroup_Other[] = "Sampled_NoSeed_Other";
const char* sampled_group = kSampledGroup_Other;
int default_sampling = 1; // Sampling is 1-in-N; this is N.
// Nothing is sampled out except for "stable" which omits almost everything
// in this configuration. This is done so that clients that fail to receive
// a configuration from the server do not bias aggregated results because
// of a relatively large number of records from them.
if (is_stable_channel) {
sampled_group = kSampledGroup_Stable;
default_sampling = 1000000;
}
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
kUkmSamplingTrialName, /*total_probability=*/100, sampled_group,
entropy_provider));
// Everybody (100%) should have a sampling configuration.
std::map<std::string, std::string> params = {
{"_default_sampling", base::NumberToString(default_sampling)}};
base::AssociateFieldTrialParams(trial->trial_name(), sampled_group, params);
trial->AppendGroup(sampled_group, 100);
// Setup the feature.
feature_list->RegisterFieldTrialOverride(
ukm::kUkmSamplingRateFeature.name,
base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
}
} // namespace
void CreateFallbackSamplingTrialsIfNeeded(
const base::FieldTrial::EntropyProvider& entropy_provider,
base::FeatureList* feature_list) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
const bool is_stable = chrome::GetChannel() == version_info::Channel::STABLE;
if (!base::FieldTrialList::TrialExists(kSamplingTrialName)) {
#if BUILDFLAG(IS_WIN)
// On all channels except stable, we sample out at a minimal rate to ensure
// the code paths are exercised in the wild before hitting stable.
const int kPreStableSampledInRatePerMille = 990; // 99%
const int kPreStableReportingFullRatePerMille = 5; // 0.5%
// This leaves 0.5% for OutOfReportingSample.
const int kStableSampledInRatePerMille = 100; // 10%
const int kStableReportingFullRatePerMille = 900; // 90%
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_ANDROID)
// On all channels except stable, we sample out at a minimal rate to ensure
// the code paths are exercised in the wild before hitting stable.
const int kPreStableSampledInRatePerMille = 995; // 99.5%
const int kPreStableReportingFullRatePerMille = 0; // 0%
// This leaves 0.5% for OutOfReportingSample.
// We use 5.3% for this set of users to work around an old bug
// (crbug.com/1306481). This should be ~10% in practice.
const int kStableSampledInRatePerMille = 53; // 5.3%
const int kStableReportingFullRatePerMille = 0; // 0%
#endif // BUILDFLAG(IS_ANDROID)
const int kSamplingInRatePerMille = is_stable
? kStableSampledInRatePerMille
: kPreStableSampledInRatePerMille;
const int kReportingFullRatePerMille =
is_stable ? kStableReportingFullRatePerMille
: kPreStableReportingFullRatePerMille;
// Note that the trial has to be activated immediately. Otherwise, it would
// be possible for this session to crash before its feature was queried, and
// the independent log produced would not contain the sampling trial.
CreateFallbackSamplingTrial(
entropy_provider, kSamplingTrialName,
metrics::internal::kMetricsReportingFeature.name,
kSamplingInRatePerMille, kReportingFullRatePerMille,
/*starts_active=*/true, feature_list);
}
#if BUILDFLAG(IS_ANDROID)
if (!base::FieldTrialList::TrialExists(kPostFREFixSamplingTrialName)) {
// On all channels except stable, we sample out at a minimal rate to ensure
// the code paths are exercised in the wild before hitting stable.
const int kPreStableSampledInRatePerMille = 995; // 99.5%
// This is meant to be 10%, and this population, unlike the set of users
// under the kSamplingTrialName trial should correctly be 10% in practice.
const int kStableSampledInRatePerMille = 100; // 10%
const int kReportingFullRatePerMille = 0;
// Note that as per the serverside config, this trial does not start active
// (so that it is possible to determine from the serverside whether the
// client used the old or new trial to determine sampling). So if Chrome
// crashes before its feature is queried, the independent log produced will
// not contain this trial, even if the client normally uses this trial to
// determine sampling.
CreateFallbackSamplingTrial(
entropy_provider, kPostFREFixSamplingTrialName,
metrics::internal::kPostFREFixMetricsReportingFeature.name,
is_stable ? kStableSampledInRatePerMille
: kPreStableSampledInRatePerMille,
kReportingFullRatePerMille,
/*starts_active=*/false, feature_list);
}
#endif // BUILDFLAG(IS_ANDROID)
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
}
void CreateFallbackUkmSamplingTrialIfNeeded(
const base::FieldTrial::EntropyProvider& entropy_provider,
base::FeatureList* feature_list) {
if (!base::FieldTrialList::TrialExists(kUkmSamplingTrialName)) {
const bool is_stable =
chrome::GetChannel() == version_info::Channel::STABLE;
CreateFallbackUkmSamplingTrial(entropy_provider, is_stable, feature_list);
}
}
} // namespace metrics
|