| 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 |