Chromium Code Reviews| 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 <algorithm> | 7 #include <algorithm> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/base_switches.h" | 10 #include "base/base_switches.h" |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 const char kPersistentStringSeparator = '/'; // Currently a slash. | 29 const char kPersistentStringSeparator = '/'; // Currently a slash. |
| 30 | 30 |
| 31 // Define a marker character to be used as a prefix to a trial name on the | 31 // Define a marker character to be used as a prefix to a trial name on the |
| 32 // command line which forces its activation. | 32 // command line which forces its activation. |
| 33 const char kActivationMarker = '*'; | 33 const char kActivationMarker = '*'; |
| 34 | 34 |
| 35 // Use shared memory to communicate field trial (experiment) state. Set to false | 35 // Use shared memory to communicate field trial (experiment) state. Set to false |
| 36 // for now while the implementation is fleshed out (e.g. data format, single | 36 // for now while the implementation is fleshed out (e.g. data format, single |
| 37 // shared memory segment). See https://codereview.chromium.org/2365273004/ and | 37 // shared memory segment). See https://codereview.chromium.org/2365273004/ and |
| 38 // crbug.com/653874 | 38 // crbug.com/653874 |
| 39 const bool kUseSharedMemoryForFieldTrials = false; | 39 const bool kUseSharedMemoryForFieldTrials = true; |
| 40 | 40 |
| 41 // Constants for the field trial allocator. | 41 // Constants for the field trial allocator. |
| 42 const char kAllocatorName[] = "FieldTrialAllocator"; | 42 const char kAllocatorName[] = "FieldTrialAllocator"; |
| 43 const uint32_t kFieldTrialType = 0xABA17E13 + 1; // SHA1(FieldTrialEntry) v1 | 43 const uint32_t kFieldTrialType = 0xABA17E13 + 1; // SHA1(FieldTrialEntry) v1 |
| 44 #if !defined(OS_NACL) | 44 #if !defined(OS_NACL) |
| 45 const size_t kFieldTrialAllocationSize = 4 << 10; // 4 KiB = one page | 45 const size_t kFieldTrialAllocationSize = 4 << 10; // 4 KiB = one page |
| 46 #endif | 46 #endif |
| 47 | 47 |
| 48 // We create one FieldTrialEntry struct per field trial in shared memory (via | 48 // We create one FieldTrialEntry struct per field trial in shared memory (via |
| 49 // field_trial_allocator_). It contains whether or not it's activated, and the | 49 // field_trial_allocator_). It contains whether or not it's activated, and the |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 | 243 |
| 244 DCHECK_LE(accumulated_group_probability_, divisor_); | 244 DCHECK_LE(accumulated_group_probability_, divisor_); |
| 245 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { | 245 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { |
| 246 // This is the group that crossed the random line, so we do the assignment. | 246 // This is the group that crossed the random line, so we do the assignment. |
| 247 SetGroupChoice(name, next_group_number_); | 247 SetGroupChoice(name, next_group_number_); |
| 248 } | 248 } |
| 249 return next_group_number_++; | 249 return next_group_number_++; |
| 250 } | 250 } |
| 251 | 251 |
| 252 int FieldTrial::group() { | 252 int FieldTrial::group() { |
| 253 FinalizeGroupChoice(); | 253 FinalizeGroupChoice(false); |
| 254 if (trial_registered_) | 254 if (trial_registered_) |
| 255 FieldTrialList::NotifyFieldTrialGroupSelection(this); | 255 FieldTrialList::NotifyFieldTrialGroupSelection(this); |
| 256 return group_; | 256 return group_; |
| 257 } | 257 } |
| 258 | 258 |
| 259 const std::string& FieldTrial::group_name() { | 259 const std::string& FieldTrial::group_name() { |
| 260 // Call |group()| to ensure group gets assigned and observers are notified. | 260 // Call |group()| to ensure group gets assigned and observers are notified. |
| 261 group(); | 261 group(); |
| 262 DCHECK(!group_name_.empty()); | 262 DCHECK(!group_name_.empty()); |
| 263 return group_name_; | 263 return group_name_; |
| 264 } | 264 } |
| 265 | 265 |
| 266 const std::string& FieldTrial::GetGroupNameWithoutActivation() { | 266 const std::string& FieldTrial::GetGroupNameWithoutActivation() { |
| 267 FinalizeGroupChoice(); | 267 FinalizeGroupChoice(false); |
| 268 return group_name_; | 268 return group_name_; |
| 269 } | 269 } |
| 270 | 270 |
| 271 void FieldTrial::SetForced() { | 271 void FieldTrial::SetForced() { |
| 272 // We might have been forced before (e.g., by CreateFieldTrial) and it's | 272 // We might have been forced before (e.g., by CreateFieldTrial) and it's |
| 273 // first come first served, e.g., command line switch has precedence. | 273 // first come first served, e.g., command line switch has precedence. |
| 274 if (forced_) | 274 if (forced_) |
| 275 return; | 275 return; |
| 276 | 276 |
| 277 // And we must finalize the group choice before we mark ourselves as forced. | 277 // And we must finalize the group choice before we mark ourselves as forced. |
| 278 FinalizeGroupChoice(); | 278 FinalizeGroupChoice(false); |
| 279 forced_ = true; | 279 forced_ = true; |
| 280 } | 280 } |
| 281 | 281 |
| 282 // static | 282 // static |
| 283 void FieldTrial::EnableBenchmarking() { | 283 void FieldTrial::EnableBenchmarking() { |
| 284 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); | 284 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); |
| 285 enable_benchmarking_ = true; | 285 enable_benchmarking_ = true; |
| 286 } | 286 } |
| 287 | 287 |
| 288 // static | 288 // static |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 326 | 326 |
| 327 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) { | 327 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) { |
| 328 group_ = number; | 328 group_ = number; |
| 329 if (group_name.empty()) | 329 if (group_name.empty()) |
| 330 StringAppendF(&group_name_, "%d", group_); | 330 StringAppendF(&group_name_, "%d", group_); |
| 331 else | 331 else |
| 332 group_name_ = group_name; | 332 group_name_ = group_name; |
| 333 DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_; | 333 DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_; |
| 334 } | 334 } |
| 335 | 335 |
| 336 void FieldTrial::FinalizeGroupChoice() { | 336 void FieldTrial::FinalizeGroupChoice(bool is_locked) { |
| 337 if (group_ != kNotFinalized) | 337 if (group_ != kNotFinalized) |
| 338 return; | 338 return; |
| 339 accumulated_group_probability_ = divisor_; | 339 accumulated_group_probability_ = divisor_; |
| 340 // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not | 340 // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not |
| 341 // finalized. | 341 // finalized. |
| 342 DCHECK(!forced_); | 342 DCHECK(!forced_); |
| 343 SetGroupChoice(default_group_name_, kDefaultGroupNumber); | 343 SetGroupChoice(default_group_name_, kDefaultGroupNumber); |
| 344 | 344 |
| 345 // Add the field trial to shared memory. | 345 // Add the field trial to shared memory. |
| 346 if (kUseSharedMemoryForFieldTrials) | 346 if (kUseSharedMemoryForFieldTrials) |
| 347 FieldTrialList::OnGroupFinalized(this); | 347 FieldTrialList::OnGroupFinalized(is_locked, this); |
| 348 } | 348 } |
| 349 | 349 |
| 350 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { | 350 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { |
| 351 if (!group_reported_ || !enable_field_trial_) | 351 if (!group_reported_ || !enable_field_trial_) |
| 352 return false; | 352 return false; |
| 353 DCHECK_NE(group_, kNotFinalized); | 353 DCHECK_NE(group_, kNotFinalized); |
| 354 active_group->trial_name = trial_name_; | 354 active_group->trial_name = trial_name_; |
| 355 active_group->group_name = group_name_; | 355 active_group->group_name = group_name_; |
| 356 return true; | 356 return true; |
| 357 } | 357 } |
| 358 | 358 |
| 359 bool FieldTrial::GetState(State* field_trial_state) { | 359 bool FieldTrial::GetState(State* field_trial_state) { |
| 360 if (!enable_field_trial_) | 360 if (!enable_field_trial_) |
| 361 return false; | 361 return false; |
| 362 FinalizeGroupChoice(); | 362 FinalizeGroupChoice(false); |
| 363 field_trial_state->trial_name = trial_name_; | 363 field_trial_state->trial_name = trial_name_; |
| 364 field_trial_state->group_name = group_name_; | 364 field_trial_state->group_name = group_name_; |
| 365 field_trial_state->activated = group_reported_; | 365 field_trial_state->activated = group_reported_; |
| 366 return true; | |
| 367 } | |
| 368 | |
| 369 bool FieldTrial::GetStateWhileLocked(State* field_trial_state) { | |
| 370 if (!enable_field_trial_) | |
| 371 return false; | |
| 372 FinalizeGroupChoice(true); | |
|
Alexei Svitkine (slow)
2016/10/27 21:40:43
How about making the call that takes the param Fin
lawrencewu
2016/10/28 17:20:21
Done.
| |
| 373 field_trial_state->trial_name = trial_name_; | |
| 374 field_trial_state->group_name = group_name_; | |
| 375 field_trial_state->activated = group_reported_; | |
| 366 return true; | 376 return true; |
| 367 } | 377 } |
| 368 | 378 |
| 369 //------------------------------------------------------------------------------ | 379 //------------------------------------------------------------------------------ |
| 370 // FieldTrialList methods and members. | 380 // FieldTrialList methods and members. |
| 371 | 381 |
| 372 // static | 382 // static |
| 373 FieldTrialList* FieldTrialList::global_ = NULL; | 383 FieldTrialList* FieldTrialList::global_ = NULL; |
| 374 | 384 |
| 375 // static | 385 // static |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 539 } | 549 } |
| 540 | 550 |
| 541 // static | 551 // static |
| 542 void FieldTrialList::AllStatesToString(std::string* output) { | 552 void FieldTrialList::AllStatesToString(std::string* output) { |
| 543 if (!global_) | 553 if (!global_) |
| 544 return; | 554 return; |
| 545 AutoLock auto_lock(global_->lock_); | 555 AutoLock auto_lock(global_->lock_); |
| 546 | 556 |
| 547 for (const auto& registered : global_->registered_) { | 557 for (const auto& registered : global_->registered_) { |
| 548 FieldTrial::State trial; | 558 FieldTrial::State trial; |
| 549 if (!registered.second->GetState(&trial)) | 559 if (!registered.second->GetStateWhileLocked(&trial)) |
| 550 continue; | 560 continue; |
| 551 DCHECK_EQ(std::string::npos, | 561 DCHECK_EQ(std::string::npos, |
| 552 trial.trial_name.find(kPersistentStringSeparator)); | 562 trial.trial_name.find(kPersistentStringSeparator)); |
| 553 DCHECK_EQ(std::string::npos, | 563 DCHECK_EQ(std::string::npos, |
| 554 trial.group_name.find(kPersistentStringSeparator)); | 564 trial.group_name.find(kPersistentStringSeparator)); |
| 555 if (trial.activated) | 565 if (trial.activated) |
| 556 output->append(1, kActivationMarker); | 566 output->append(1, kActivationMarker); |
| 557 trial.trial_name.AppendToString(output); | 567 trial.trial_name.AppendToString(output); |
| 558 output->append(1, kPersistentStringSeparator); | 568 output->append(1, kPersistentStringSeparator); |
| 559 trial.group_name.AppendToString(output); | 569 trial.group_name.AppendToString(output); |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 746 } | 756 } |
| 747 | 757 |
| 748 // static | 758 // static |
| 749 void FieldTrialList::RemoveObserver(Observer* observer) { | 759 void FieldTrialList::RemoveObserver(Observer* observer) { |
| 750 if (!global_) | 760 if (!global_) |
| 751 return; | 761 return; |
| 752 global_->observer_list_->RemoveObserver(observer); | 762 global_->observer_list_->RemoveObserver(observer); |
| 753 } | 763 } |
| 754 | 764 |
| 755 // static | 765 // static |
| 756 void FieldTrialList::OnGroupFinalized(FieldTrial* field_trial) { | 766 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { |
| 757 if (!global_) | 767 if (!global_) |
| 758 return; | 768 return; |
| 759 AutoLock auto_lock(global_->lock_); | 769 if (is_locked) { |
| 760 AddToAllocatorWhileLocked(field_trial); | 770 AddToAllocatorWhileLocked(field_trial); |
| 771 } else { | |
| 772 AutoLock auto_lock(global_->lock_); | |
| 773 AddToAllocatorWhileLocked(field_trial); | |
| 774 } | |
| 761 } | 775 } |
| 762 | 776 |
| 763 // static | 777 // static |
| 764 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { | 778 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { |
| 765 if (!global_) | 779 if (!global_) |
| 766 return; | 780 return; |
| 767 | 781 |
| 768 { | 782 { |
| 769 AutoLock auto_lock(global_->lock_); | 783 AutoLock auto_lock(global_->lock_); |
| 770 if (field_trial->group_reported_) | 784 if (field_trial->group_reported_) |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 944 return; | 958 return; |
| 945 } | 959 } |
| 946 AutoLock auto_lock(global_->lock_); | 960 AutoLock auto_lock(global_->lock_); |
| 947 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 961 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 948 trial->AddRef(); | 962 trial->AddRef(); |
| 949 trial->SetTrialRegistered(); | 963 trial->SetTrialRegistered(); |
| 950 global_->registered_[trial->trial_name()] = trial; | 964 global_->registered_[trial->trial_name()] = trial; |
| 951 } | 965 } |
| 952 | 966 |
| 953 } // namespace base | 967 } // namespace base |
| OLD | NEW |