Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(107)

Side by Side Diff: base/metrics/field_trial.cc

Issue 6883102: Add one-time randomization support for FieldTrial, and the ability to (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge to head. Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/metrics/field_trial.h ('k') | base/metrics/field_trial_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/metrics/field_trial.h" 5 #include "base/metrics/field_trial.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/rand_util.h" 8 #include "base/rand_util.h"
9 #include "base/sha1.h"
10 #include "base/string_util.h"
9 #include "base/stringprintf.h" 11 #include "base/stringprintf.h"
10 #include "base/utf_string_conversions.h" 12 #include "base/utf_string_conversions.h"
11 13
12 namespace base { 14 namespace base {
13 15
14 // static 16 // static
15 const int FieldTrial::kNotFinalized = -1; 17 const int FieldTrial::kNotFinalized = -1;
16 18
17 // static 19 // static
18 const int FieldTrial::kDefaultGroupNumber = 0; 20 const int FieldTrial::kDefaultGroupNumber = 0;
(...skipping 15 matching lines...) Expand all
34 36
35 FieldTrial::FieldTrial(const std::string& name, 37 FieldTrial::FieldTrial(const std::string& name,
36 const Probability total_probability, 38 const Probability total_probability,
37 const std::string& default_group_name, 39 const std::string& default_group_name,
38 const int year, 40 const int year,
39 const int month, 41 const int month,
40 const int day_of_month) 42 const int day_of_month)
41 : name_(name), 43 : name_(name),
42 divisor_(total_probability), 44 divisor_(total_probability),
43 default_group_name_(default_group_name), 45 default_group_name_(default_group_name),
44 random_(static_cast<Probability>(divisor_ * base::RandDouble())), 46 random_(static_cast<Probability>(divisor_ * RandDouble())),
45 accumulated_group_probability_(0), 47 accumulated_group_probability_(0),
46 next_group_number_(kDefaultGroupNumber+1), 48 next_group_number_(kDefaultGroupNumber + 1),
47 group_(kNotFinalized) { 49 group_(kNotFinalized),
50 enable_field_trial_(true) {
48 DCHECK_GT(total_probability, 0); 51 DCHECK_GT(total_probability, 0);
52 DCHECK(!name_.empty());
49 DCHECK(!default_group_name_.empty()); 53 DCHECK(!default_group_name_.empty());
50 FieldTrialList::Register(this); 54 FieldTrialList::Register(this);
51 55
52 DCHECK_GT(year, 1970); 56 DCHECK_GT(year, 1970);
53 DCHECK_GT(month, 0); 57 DCHECK_GT(month, 0);
54 DCHECK_LT(month, 13); 58 DCHECK_LT(month, 13);
55 DCHECK_GT(day_of_month, 0); 59 DCHECK_GT(day_of_month, 0);
56 DCHECK_LT(day_of_month, 32); 60 DCHECK_LT(day_of_month, 32);
57 61
58 base::Time::Exploded exploded; 62 Time::Exploded exploded;
59 exploded.year = year; 63 exploded.year = year;
60 exploded.month = month; 64 exploded.month = month;
61 exploded.day_of_week = 0; // Should be unused. 65 exploded.day_of_week = 0; // Should be unused.
62 exploded.day_of_month = day_of_month; 66 exploded.day_of_month = day_of_month;
63 exploded.hour = 0; 67 exploded.hour = 0;
64 exploded.minute = 0; 68 exploded.minute = 0;
65 exploded.second = 0; 69 exploded.second = 0;
66 exploded.millisecond = 0; 70 exploded.millisecond = 0;
67 71
68 base::Time expiration_time = Time::FromLocalExploded(exploded); 72 Time expiration_time = Time::FromLocalExploded(exploded);
69 disable_field_trial_ = (GetBuildTime() > expiration_time) ? true : false; 73 if (GetBuildTime() > expiration_time)
74 Disable();
75 }
76
77 void FieldTrial::UseOneTimeRandomization() {
78 DCHECK_EQ(group_, kNotFinalized);
79 DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_);
80 if (!FieldTrialList::IsOneTimeRandomizationEnabled()) {
81 NOTREACHED();
82 Disable();
83 return;
84 }
85
86 random_ = static_cast<Probability>(
87 divisor_ * HashClientId(FieldTrialList::client_id(), name_));
88 }
89
90 void FieldTrial::Disable() {
91 enable_field_trial_ = false;
92
93 // In case we are disabled after initialization, we need to switch
94 // the trial to the default group.
95 if (group_ != kNotFinalized) {
96 group_ = kDefaultGroupNumber;
97 group_name_ = default_group_name_;
98 }
70 } 99 }
71 100
72 int FieldTrial::AppendGroup(const std::string& name, 101 int FieldTrial::AppendGroup(const std::string& name,
73 Probability group_probability) { 102 Probability group_probability) {
74 DCHECK_LE(group_probability, divisor_); 103 DCHECK_LE(group_probability, divisor_);
75 DCHECK_GE(group_probability, 0); 104 DCHECK_GE(group_probability, 0);
76 105
77 if (enable_benchmarking_ || disable_field_trial_) 106 if (enable_benchmarking_ || !enable_field_trial_)
78 group_probability = 0; 107 group_probability = 0;
79 108
80 accumulated_group_probability_ += group_probability; 109 accumulated_group_probability_ += group_probability;
81 110
82 DCHECK_LE(accumulated_group_probability_, divisor_); 111 DCHECK_LE(accumulated_group_probability_, divisor_);
83 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { 112 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) {
84 // This is the group that crossed the random line, so we do the assignment. 113 // This is the group that crossed the random line, so we do the assignment.
85 group_ = next_group_number_; 114 group_ = next_group_number_;
86 if (name.empty()) 115 if (name.empty())
87 base::StringAppendF(&group_name_, "%d", group_); 116 StringAppendF(&group_name_, "%d", group_);
88 else 117 else
89 group_name_ = name; 118 group_name_ = name;
90 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); 119 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
91 } 120 }
92 return next_group_number_++; 121 return next_group_number_++;
93 } 122 }
94 123
95 int FieldTrial::group() { 124 int FieldTrial::group() {
96 if (group_ == kNotFinalized) { 125 if (group_ == kNotFinalized) {
97 accumulated_group_probability_ = divisor_; 126 accumulated_group_probability_ = divisor_;
98 group_ = kDefaultGroupNumber; 127 group_ = kDefaultGroupNumber;
99 group_name_ = default_group_name_; 128 group_name_ = default_group_name_;
100 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_); 129 FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
101 } 130 }
102 return group_; 131 return group_;
103 } 132 }
104 133
105 std::string FieldTrial::group_name() { 134 std::string FieldTrial::group_name() {
106 group(); // call group() to make group assignment was done. 135 group(); // call group() to make sure group assignment was done.
136 DCHECK(!group_name_.empty());
107 return group_name_; 137 return group_name_;
108 } 138 }
109 139
110 // static 140 // static
111 std::string FieldTrial::MakeName(const std::string& name_prefix, 141 std::string FieldTrial::MakeName(const std::string& name_prefix,
112 const std::string& trial_name) { 142 const std::string& trial_name) {
113 std::string big_string(name_prefix); 143 std::string big_string(name_prefix);
114 big_string.append(1, kHistogramFieldTrialSeparator); 144 big_string.append(1, kHistogramFieldTrialSeparator);
115 return big_string.append(FieldTrialList::FindFullName(trial_name)); 145 return big_string.append(FieldTrialList::FindFullName(trial_name));
116 } 146 }
117 147
118 // static 148 // static
119 void FieldTrial::EnableBenchmarking() { 149 void FieldTrial::EnableBenchmarking() {
120 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); 150 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
121 enable_benchmarking_ = true; 151 enable_benchmarking_ = true;
122 } 152 }
123 153
124 FieldTrial::~FieldTrial() {} 154 FieldTrial::~FieldTrial() {}
125 155
126 // static 156 // static
127 Time FieldTrial::GetBuildTime() { 157 Time FieldTrial::GetBuildTime() {
128 Time integral_build_time; 158 Time integral_build_time;
129 const char* kDateTime = __DATE__ " " __TIME__; 159 const char* kDateTime = __DATE__ " " __TIME__;
130 bool result = Time::FromString(ASCIIToWide(kDateTime).c_str(), 160 bool result = Time::FromString(ASCIIToWide(kDateTime).c_str(),
131 &integral_build_time); 161 &integral_build_time);
132 DCHECK(result); 162 DCHECK(result);
133 return integral_build_time; 163 return integral_build_time;
134 } 164 }
135 165
166 // static
167 double FieldTrial::HashClientId(const std::string& client_id,
168 const std::string& trial_name) {
169 // SHA-1 is designed to produce a uniformly random spread in its output space,
170 // even for nearly-identical inputs, so it helps massage whatever client_id
171 // and trial_name we get into something with a uniform distribution, which
172 // is desirable so that we don't skew any part of the 0-100% spectrum.
173 std::string input(client_id + trial_name);
174 unsigned char sha1_hash[SHA1_LENGTH];
175 SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
176 input.size(),
177 sha1_hash);
178
179 COMPILE_ASSERT(sizeof(uint64) < sizeof(sha1_hash), need_more_data);
180 uint64* bits = reinterpret_cast<uint64*>(&sha1_hash[0]);
181
182 return BitsToOpenEndedUnitInterval(*bits);
183 }
184
136 //------------------------------------------------------------------------------ 185 //------------------------------------------------------------------------------
137 // FieldTrialList methods and members. 186 // FieldTrialList methods and members.
138 187
139 // static 188 // static
140 FieldTrialList* FieldTrialList::global_ = NULL; 189 FieldTrialList* FieldTrialList::global_ = NULL;
141 190
142 // static 191 // static
143 bool FieldTrialList::register_without_global_ = false; 192 bool FieldTrialList::register_without_global_ = false;
144 193
145 FieldTrialList::FieldTrialList() 194 FieldTrialList::FieldTrialList(const std::string& client_id)
146 : application_start_time_(TimeTicks::Now()), 195 : application_start_time_(TimeTicks::Now()),
196 client_id_(client_id),
147 observer_list_(ObserverList<Observer>::NOTIFY_EXISTING_ONLY) { 197 observer_list_(ObserverList<Observer>::NOTIFY_EXISTING_ONLY) {
148 DCHECK(!global_); 198 DCHECK(!global_);
149 DCHECK(!register_without_global_); 199 DCHECK(!register_without_global_);
150 global_ = this; 200 global_ = this;
151 201
152 Time::Exploded exploded; 202 Time::Exploded exploded;
153 Time two_years_from_now = 203 Time two_years_from_now =
154 Time::NowFromSystemTime() + TimeDelta::FromDays(730); 204 Time::NowFromSystemTime() + TimeDelta::FromDays(730);
155 two_years_from_now.LocalExplode(&exploded); 205 two_years_from_now.LocalExplode(&exploded);
156 kExpirationYearInFuture = exploded.year; 206 kExpirationYearInFuture = exploded.year;
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 247
198 // static 248 // static
199 std::string FieldTrialList::FindFullName(const std::string& name) { 249 std::string FieldTrialList::FindFullName(const std::string& name) {
200 FieldTrial* field_trial = Find(name); 250 FieldTrial* field_trial = Find(name);
201 if (field_trial) 251 if (field_trial)
202 return field_trial->group_name(); 252 return field_trial->group_name();
203 return ""; 253 return "";
204 } 254 }
205 255
206 // static 256 // static
257 bool FieldTrialList::TrialExists(const std::string& name) {
258 return Find(name) != NULL;
259 }
260
261 // static
207 void FieldTrialList::StatesToString(std::string* output) { 262 void FieldTrialList::StatesToString(std::string* output) {
208 if (!global_) 263 if (!global_)
209 return; 264 return;
210 DCHECK(output->empty()); 265 DCHECK(output->empty());
211 AutoLock auto_lock(global_->lock_); 266 AutoLock auto_lock(global_->lock_);
267
212 for (RegistrationList::iterator it = global_->registered_.begin(); 268 for (RegistrationList::iterator it = global_->registered_.begin();
213 it != global_->registered_.end(); ++it) { 269 it != global_->registered_.end(); ++it) {
214 const std::string name = it->first; 270 const std::string name = it->first;
215 std::string group_name = it->second->group_name_internal(); 271 std::string group_name = it->second->group_name_internal();
216 if (group_name.empty()) 272 if (group_name.empty())
217 // No definitive winner in this trial, use default_group_name as the 273 continue; // Should not include uninitialized trials.
218 // group_name.
219 group_name = it->second->default_group_name();
220 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); 274 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos);
221 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); 275 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos);
222 output->append(name); 276 output->append(name);
223 output->append(1, kPersistentStringSeparator); 277 output->append(1, kPersistentStringSeparator);
224 output->append(group_name); 278 output->append(group_name);
225 output->append(1, kPersistentStringSeparator); 279 output->append(1, kPersistentStringSeparator);
226 } 280 }
227 } 281 }
228 282
229 // static 283 // static
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
306 } 360 }
307 361
308 // static 362 // static
309 size_t FieldTrialList::GetFieldTrialCount() { 363 size_t FieldTrialList::GetFieldTrialCount() {
310 if (!global_) 364 if (!global_)
311 return 0; 365 return 0;
312 AutoLock auto_lock(global_->lock_); 366 AutoLock auto_lock(global_->lock_);
313 return global_->registered_.size(); 367 return global_->registered_.size();
314 } 368 }
315 369
370 // static
371 bool FieldTrialList::IsOneTimeRandomizationEnabled() {
372 DCHECK(global_);
373 if (!global_)
374 return false;
375
376 return !global_->client_id_.empty();
377 }
378
379 // static
380 const std::string& FieldTrialList::client_id() {
381 DCHECK(global_);
382 if (!global_)
383 return EmptyString();
384
385 return global_->client_id_;
386 }
387
316 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 388 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
317 RegistrationList::iterator it = registered_.find(name); 389 RegistrationList::iterator it = registered_.find(name);
318 if (registered_.end() == it) 390 if (registered_.end() == it)
319 return NULL; 391 return NULL;
320 return it->second; 392 return it->second;
321 } 393 }
322 394
323 } // namespace base 395 } // namespace base
OLDNEW
« no previous file with comments | « base/metrics/field_trial.h ('k') | base/metrics/field_trial_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698