OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/build_time.h" | 7 #include "base/build_time.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/rand_util.h" | 9 #include "base/rand_util.h" |
10 #include "base/sha1.h" | 10 #include "base/sha1.h" |
11 #include "base/stringprintf.h" | 11 #include "base/stringprintf.h" |
12 #include "base/string_util.h" | 12 #include "base/string_util.h" |
13 #include "base/sys_byteorder.h" | 13 #include "base/sys_byteorder.h" |
14 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
15 | 15 |
16 namespace base { | 16 namespace base { |
17 | 17 |
18 static const char kHistogramFieldTrialSeparator('_'); | 18 static const char kHistogramFieldTrialSeparator('_'); |
19 | 19 |
20 // statics | 20 // statics |
21 const int FieldTrial::kNotFinalized = -1; | 21 const int FieldTrial::kNotFinalized = -1; |
22 const int FieldTrial::kDefaultGroupNumber = 0; | 22 const int FieldTrial::kDefaultGroupNumber = 0; |
23 const uint32 FieldTrial::kReservedHashValue = 0; | |
24 bool FieldTrial::enable_benchmarking_ = false; | 23 bool FieldTrial::enable_benchmarking_ = false; |
25 | 24 |
26 const char FieldTrialList::kPersistentStringSeparator('/'); | 25 const char FieldTrialList::kPersistentStringSeparator('/'); |
27 int FieldTrialList::kExpirationYearInFuture = 0; | 26 int FieldTrialList::kExpirationYearInFuture = 0; |
28 | 27 |
29 //------------------------------------------------------------------------------ | 28 //------------------------------------------------------------------------------ |
30 // FieldTrial methods and members. | 29 // FieldTrial methods and members. |
31 | 30 |
32 FieldTrial::FieldTrial(const std::string& name, | 31 FieldTrial::FieldTrial(const std::string& name, |
33 const Probability total_probability, | 32 const Probability total_probability, |
34 const std::string& default_group_name, | 33 const std::string& default_group_name, |
35 const int year, | 34 const int year, |
36 const int month, | 35 const int month, |
37 const int day_of_month) | 36 const int day_of_month) |
38 : name_(name), | 37 : name_(name), |
39 name_hash_(HashName(name)), | |
40 divisor_(total_probability), | 38 divisor_(total_probability), |
41 default_group_name_(default_group_name), | 39 default_group_name_(default_group_name), |
42 random_(static_cast<Probability>(divisor_ * RandDouble())), | 40 random_(static_cast<Probability>(divisor_ * RandDouble())), |
43 accumulated_group_probability_(0), | 41 accumulated_group_probability_(0), |
44 next_group_number_(kDefaultGroupNumber + 1), | 42 next_group_number_(kDefaultGroupNumber + 1), |
45 group_(kNotFinalized), | 43 group_(kNotFinalized), |
46 group_name_hash_(kReservedHashValue), | |
47 enable_field_trial_(true), | 44 enable_field_trial_(true), |
48 forced_(false) { | 45 forced_(false) { |
49 DCHECK_GT(total_probability, 0); | 46 DCHECK_GT(total_probability, 0); |
50 DCHECK(!name_.empty()); | 47 DCHECK(!name_.empty()); |
51 DCHECK(!default_group_name_.empty()); | 48 DCHECK(!default_group_name_.empty()); |
52 | 49 |
53 DCHECK_GT(year, 1970); | 50 DCHECK_GT(year, 1970); |
54 DCHECK_GT(month, 0); | 51 DCHECK_GT(month, 0); |
55 DCHECK_LT(month, 13); | 52 DCHECK_LT(month, 13); |
56 DCHECK_GT(day_of_month, 0); | 53 DCHECK_GT(day_of_month, 0); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
144 } | 141 } |
145 return group_; | 142 return group_; |
146 } | 143 } |
147 | 144 |
148 std::string FieldTrial::group_name() { | 145 std::string FieldTrial::group_name() { |
149 group(); // call group() to make sure group assignment was done. | 146 group(); // call group() to make sure group assignment was done. |
150 DCHECK(!group_name_.empty()); | 147 DCHECK(!group_name_.empty()); |
151 return group_name_; | 148 return group_name_; |
152 } | 149 } |
153 | 150 |
154 bool FieldTrial::GetNameGroupId(NameGroupId* name_group_id) { | 151 bool FieldTrial::GetSelectedGroup(SelectedGroup* selected_group) { |
155 if (group_name_hash_ == kReservedHashValue) | 152 if (group_ == kNotFinalized) |
156 return false; | 153 return false; |
157 name_group_id->name = name_hash_; | 154 selected_group->trial = name_; |
158 name_group_id->group = group_name_hash_; | 155 selected_group->group = group_name_; |
159 return true; | 156 return true; |
160 } | 157 } |
161 | 158 |
162 // static | 159 // static |
163 std::string FieldTrial::MakeName(const std::string& name_prefix, | 160 std::string FieldTrial::MakeName(const std::string& name_prefix, |
164 const std::string& trial_name) { | 161 const std::string& trial_name) { |
165 std::string big_string(name_prefix); | 162 std::string big_string(name_prefix); |
166 big_string.append(1, kHistogramFieldTrialSeparator); | 163 big_string.append(1, kHistogramFieldTrialSeparator); |
167 return big_string.append(FieldTrialList::FindFullName(trial_name)); | 164 return big_string.append(FieldTrialList::FindFullName(trial_name)); |
168 } | 165 } |
169 | 166 |
170 // static | 167 // static |
171 FieldTrial::NameGroupId FieldTrial::MakeNameGroupId( | |
172 const std::string& trial_name, | |
173 const std::string& group_name) { | |
174 NameGroupId id; | |
175 id.name = HashName(trial_name); | |
176 id.group = HashName(group_name); | |
177 return id; | |
178 } | |
179 | |
180 // static | |
181 void FieldTrial::EnableBenchmarking() { | 168 void FieldTrial::EnableBenchmarking() { |
182 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); | 169 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); |
183 enable_benchmarking_ = true; | 170 enable_benchmarking_ = true; |
184 } | 171 } |
185 | 172 |
186 FieldTrial::~FieldTrial() {} | 173 FieldTrial::~FieldTrial() {} |
187 | 174 |
188 void FieldTrial::SetGroupChoice(const std::string& name, int number) { | 175 void FieldTrial::SetGroupChoice(const std::string& name, int number) { |
189 group_ = number; | 176 group_ = number; |
190 if (name.empty()) | 177 if (name.empty()) |
191 StringAppendF(&group_name_, "%d", group_); | 178 StringAppendF(&group_name_, "%d", group_); |
192 else | 179 else |
193 group_name_ = name; | 180 group_name_ = name; |
194 group_name_hash_ = HashName(group_name_); | |
195 } | 181 } |
196 | 182 |
197 // static | 183 // static |
198 double FieldTrial::HashClientId(const std::string& client_id, | 184 double FieldTrial::HashClientId(const std::string& client_id, |
199 const std::string& trial_name) { | 185 const std::string& trial_name) { |
200 // SHA-1 is designed to produce a uniformly random spread in its output space, | 186 // SHA-1 is designed to produce a uniformly random spread in its output space, |
201 // even for nearly-identical inputs, so it helps massage whatever client_id | 187 // even for nearly-identical inputs, so it helps massage whatever client_id |
202 // and trial_name we get into something with a uniform distribution, which | 188 // and trial_name we get into something with a uniform distribution, which |
203 // is desirable so that we don't skew any part of the 0-100% spectrum. | 189 // is desirable so that we don't skew any part of the 0-100% spectrum. |
204 std::string input(client_id + trial_name); | 190 std::string input(client_id + trial_name); |
205 unsigned char sha1_hash[kSHA1Length]; | 191 unsigned char sha1_hash[kSHA1Length]; |
206 SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), | 192 SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), |
207 input.size(), | 193 input.size(), |
208 sha1_hash); | 194 sha1_hash); |
209 | 195 |
210 COMPILE_ASSERT(sizeof(uint64) < sizeof(sha1_hash), need_more_data); | 196 COMPILE_ASSERT(sizeof(uint64) < sizeof(sha1_hash), need_more_data); |
211 uint64 bits; | 197 uint64 bits; |
212 memcpy(&bits, sha1_hash, sizeof(bits)); | 198 memcpy(&bits, sha1_hash, sizeof(bits)); |
213 bits = base::ByteSwapToLE64(bits); | 199 bits = base::ByteSwapToLE64(bits); |
214 | 200 |
215 return BitsToOpenEndedUnitInterval(bits); | 201 return BitsToOpenEndedUnitInterval(bits); |
216 } | 202 } |
217 | 203 |
218 // static | |
219 uint32 FieldTrial::HashName(const std::string& name) { | |
220 // SHA-1 is designed to produce a uniformly random spread in its output space, | |
221 // even for nearly-identical inputs. | |
222 unsigned char sha1_hash[kSHA1Length]; | |
223 SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()), | |
224 name.size(), | |
225 sha1_hash); | |
226 | |
227 COMPILE_ASSERT(sizeof(uint32) < sizeof(sha1_hash), need_more_data); | |
228 uint32 bits; | |
229 memcpy(&bits, sha1_hash, sizeof(bits)); | |
230 | |
231 // We only DCHECK, since this should not happen because the registration | |
232 // of the experiment on the server should have already warn the developer. | |
233 // If this ever happen, we'll ignore this experiment/group when reporting. | |
234 DCHECK(bits != kReservedHashValue); | |
235 return base::ByteSwapToLE32(bits); | |
236 } | |
237 | |
238 //------------------------------------------------------------------------------ | 204 //------------------------------------------------------------------------------ |
239 // FieldTrialList methods and members. | 205 // FieldTrialList methods and members. |
240 | 206 |
241 // static | 207 // static |
242 FieldTrialList* FieldTrialList::global_ = NULL; | 208 FieldTrialList* FieldTrialList::global_ = NULL; |
243 | 209 |
244 // static | 210 // static |
245 bool FieldTrialList::used_without_global_ = false; | 211 bool FieldTrialList::used_without_global_ = false; |
246 | 212 |
247 FieldTrialList::FieldTrialList(const std::string& client_id) | 213 FieldTrialList::FieldTrialList(const std::string& client_id) |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); | 311 DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); |
346 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); | 312 DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); |
347 output->append(name); | 313 output->append(name); |
348 output->append(1, kPersistentStringSeparator); | 314 output->append(1, kPersistentStringSeparator); |
349 output->append(group_name); | 315 output->append(group_name); |
350 output->append(1, kPersistentStringSeparator); | 316 output->append(1, kPersistentStringSeparator); |
351 } | 317 } |
352 } | 318 } |
353 | 319 |
354 // static | 320 // static |
355 void FieldTrialList::GetFieldTrialNameGroupIds( | 321 void FieldTrialList::GetFieldTrialSelectedGroups( |
356 std::vector<FieldTrial::NameGroupId>* name_group_ids) { | 322 FieldTrial::SelectedGroups* selected_groups) { |
357 DCHECK(name_group_ids->empty()); | 323 DCHECK(selected_groups->empty()); |
358 if (!global_) | 324 if (!global_) |
359 return; | 325 return; |
360 AutoLock auto_lock(global_->lock_); | 326 AutoLock auto_lock(global_->lock_); |
361 | 327 |
362 for (RegistrationList::iterator it = global_->registered_.begin(); | 328 for (RegistrationList::iterator it = global_->registered_.begin(); |
363 it != global_->registered_.end(); ++it) { | 329 it != global_->registered_.end(); ++it) { |
364 FieldTrial::NameGroupId name_group_id; | 330 FieldTrial::SelectedGroup selected_group; |
365 if (it->second->GetNameGroupId(&name_group_id)) | 331 if (it->second->GetSelectedGroup(&selected_group)) |
366 name_group_ids->push_back(name_group_id); | 332 selected_groups->push_back(selected_group); |
367 } | 333 } |
368 } | 334 } |
369 | 335 |
370 // static | 336 // static |
371 bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string) { | 337 bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string) { |
372 DCHECK(global_); | 338 DCHECK(global_); |
373 if (trials_string.empty() || !global_) | 339 if (trials_string.empty() || !global_) |
374 return true; | 340 return true; |
375 | 341 |
376 size_t next_item = 0; | 342 size_t next_item = 0; |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
490 used_without_global_ = true; | 456 used_without_global_ = true; |
491 return; | 457 return; |
492 } | 458 } |
493 AutoLock auto_lock(global_->lock_); | 459 AutoLock auto_lock(global_->lock_); |
494 DCHECK(!global_->PreLockedFind(trial->name())); | 460 DCHECK(!global_->PreLockedFind(trial->name())); |
495 trial->AddRef(); | 461 trial->AddRef(); |
496 global_->registered_[trial->name()] = trial; | 462 global_->registered_[trial->name()] = trial; |
497 } | 463 } |
498 | 464 |
499 } // namespace base | 465 } // namespace base |
OLD | NEW |