| 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 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 : trial_name_(trial_name), | 302 : trial_name_(trial_name), |
| 303 divisor_(total_probability), | 303 divisor_(total_probability), |
| 304 default_group_name_(default_group_name), | 304 default_group_name_(default_group_name), |
| 305 random_(GetGroupBoundaryValue(total_probability, entropy_value)), | 305 random_(GetGroupBoundaryValue(total_probability, entropy_value)), |
| 306 accumulated_group_probability_(0), | 306 accumulated_group_probability_(0), |
| 307 next_group_number_(kDefaultGroupNumber + 1), | 307 next_group_number_(kDefaultGroupNumber + 1), |
| 308 group_(kNotFinalized), | 308 group_(kNotFinalized), |
| 309 enable_field_trial_(true), | 309 enable_field_trial_(true), |
| 310 forced_(false), | 310 forced_(false), |
| 311 group_reported_(false), | 311 group_reported_(false), |
| 312 trial_registered_(false) { | 312 trial_registered_(false), |
| 313 ref_(SharedPersistentMemoryAllocator::kReferenceNull) { |
| 313 DCHECK_GT(total_probability, 0); | 314 DCHECK_GT(total_probability, 0); |
| 314 DCHECK(!trial_name_.empty()); | 315 DCHECK(!trial_name_.empty()); |
| 315 DCHECK(!default_group_name_.empty()); | 316 DCHECK(!default_group_name_.empty()); |
| 316 } | 317 } |
| 317 | 318 |
| 318 FieldTrial::~FieldTrial() {} | 319 FieldTrial::~FieldTrial() {} |
| 319 | 320 |
| 320 void FieldTrial::SetTrialRegistered() { | 321 void FieldTrial::SetTrialRegistered() { |
| 321 DCHECK_EQ(kNotFinalized, group_); | 322 DCHECK_EQ(kNotFinalized, group_); |
| 322 DCHECK(!trial_registered_); | 323 DCHECK(!trial_registered_); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 333 } | 334 } |
| 334 | 335 |
| 335 void FieldTrial::FinalizeGroupChoice() { | 336 void FieldTrial::FinalizeGroupChoice() { |
| 336 if (group_ != kNotFinalized) | 337 if (group_ != kNotFinalized) |
| 337 return; | 338 return; |
| 338 accumulated_group_probability_ = divisor_; | 339 accumulated_group_probability_ = divisor_; |
| 339 // 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 |
| 340 // finalized. | 341 // finalized. |
| 341 DCHECK(!forced_); | 342 DCHECK(!forced_); |
| 342 SetGroupChoice(default_group_name_, kDefaultGroupNumber); | 343 SetGroupChoice(default_group_name_, kDefaultGroupNumber); |
| 344 |
| 345 // Add the field trial to shared memory. |
| 346 if (kUseSharedMemoryForFieldTrials) |
| 347 FieldTrialList::OnGroupFinalized(this); |
| 343 } | 348 } |
| 344 | 349 |
| 345 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { | 350 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { |
| 346 if (!group_reported_ || !enable_field_trial_) | 351 if (!group_reported_ || !enable_field_trial_) |
| 347 return false; | 352 return false; |
| 348 DCHECK_NE(group_, kNotFinalized); | 353 DCHECK_NE(group_, kNotFinalized); |
| 349 active_group->trial_name = trial_name_; | 354 active_group->trial_name = trial_name_; |
| 350 active_group->group_name = group_name_; | 355 active_group->group_name = group_name_; |
| 351 return true; | 356 return true; |
| 352 } | 357 } |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 666 return; | 671 return; |
| 667 if (kUseSharedMemoryForFieldTrials) { | 672 if (kUseSharedMemoryForFieldTrials) { |
| 668 InstantiateFieldTrialAllocatorIfNeeded(); | 673 InstantiateFieldTrialAllocatorIfNeeded(); |
| 669 if (global_->readonly_allocator_handle_) | 674 if (global_->readonly_allocator_handle_) |
| 670 handles->push_back(global_->readonly_allocator_handle_); | 675 handles->push_back(global_->readonly_allocator_handle_); |
| 671 } | 676 } |
| 672 } | 677 } |
| 673 #endif | 678 #endif |
| 674 | 679 |
| 675 // static | 680 // static |
| 676 void FieldTrialList::CreateTrialsFromSharedMemory( | |
| 677 std::unique_ptr<SharedMemory> shm) { | |
| 678 const SharedPersistentMemoryAllocator shalloc(std::move(shm), 0, | |
| 679 kAllocatorName, true); | |
| 680 PersistentMemoryAllocator::Iterator iter(&shalloc); | |
| 681 | |
| 682 SharedPersistentMemoryAllocator::Reference ref; | |
| 683 while ((ref = iter.GetNextOfType(kFieldTrialType)) != | |
| 684 SharedPersistentMemoryAllocator::kReferenceNull) { | |
| 685 const FieldTrialEntry* entry = | |
| 686 shalloc.GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | |
| 687 FieldTrial* trial = | |
| 688 CreateFieldTrial(entry->GetTrialName(), entry->GetGroupName()); | |
| 689 | |
| 690 if (entry->activated) { | |
| 691 // Call |group()| to mark the trial as "used" and notify observers, if | |
| 692 // any. This is useful to ensure that field trials created in child | |
| 693 // processes are properly reported in crash reports. | |
| 694 trial->group(); | |
| 695 } | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 // static | |
| 700 void FieldTrialList::CopyFieldTrialStateToFlags( | 681 void FieldTrialList::CopyFieldTrialStateToFlags( |
| 701 const char* field_trial_handle_switch, | 682 const char* field_trial_handle_switch, |
| 702 CommandLine* cmd_line) { | 683 CommandLine* cmd_line) { |
| 703 #if defined(OS_WIN) | 684 #if defined(OS_WIN) |
| 704 // Use shared memory to pass the state if the feature is enabled, otherwise | 685 // Use shared memory to pass the state if the feature is enabled, otherwise |
| 705 // fallback to passing it via the command line as a string. | 686 // fallback to passing it via the command line as a string. |
| 706 if (kUseSharedMemoryForFieldTrials) { | 687 if (kUseSharedMemoryForFieldTrials) { |
| 707 InstantiateFieldTrialAllocatorIfNeeded(); | 688 InstantiateFieldTrialAllocatorIfNeeded(); |
| 708 // If the readonly handle didn't get duplicated properly, then fallback to | 689 // If the readonly handle didn't get duplicated properly, then fallback to |
| 709 // original behavior. | 690 // original behavior. |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 765 } | 746 } |
| 766 | 747 |
| 767 // static | 748 // static |
| 768 void FieldTrialList::RemoveObserver(Observer* observer) { | 749 void FieldTrialList::RemoveObserver(Observer* observer) { |
| 769 if (!global_) | 750 if (!global_) |
| 770 return; | 751 return; |
| 771 global_->observer_list_->RemoveObserver(observer); | 752 global_->observer_list_->RemoveObserver(observer); |
| 772 } | 753 } |
| 773 | 754 |
| 774 // static | 755 // static |
| 756 void FieldTrialList::OnGroupFinalized(FieldTrial* field_trial) { |
| 757 if (!global_) |
| 758 return; |
| 759 AutoLock auto_lock(global_->lock_); |
| 760 AddToAllocatorWhileLocked(field_trial); |
| 761 } |
| 762 |
| 763 // static |
| 775 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { | 764 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { |
| 776 if (!global_) | 765 if (!global_) |
| 777 return; | 766 return; |
| 778 | 767 |
| 779 { | 768 { |
| 780 AutoLock auto_lock(global_->lock_); | 769 AutoLock auto_lock(global_->lock_); |
| 781 if (field_trial->group_reported_) | 770 if (field_trial->group_reported_) |
| 782 return; | 771 return; |
| 783 field_trial->group_reported_ = true; | 772 field_trial->group_reported_ = true; |
| 784 | 773 |
| 785 if (!field_trial->enable_field_trial_) | 774 if (!field_trial->enable_field_trial_) |
| 786 return; | 775 return; |
| 787 | 776 |
| 788 if (kUseSharedMemoryForFieldTrials) { | 777 if (kUseSharedMemoryForFieldTrials) |
| 789 field_trial->AddToAllocatorWhileLocked( | 778 ActivateFieldTrialEntryWhileLocked(field_trial); |
| 790 global_->field_trial_allocator_.get()); | |
| 791 } | |
| 792 } | 779 } |
| 793 | 780 |
| 794 global_->observer_list_->Notify( | 781 global_->observer_list_->Notify( |
| 795 FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, | 782 FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, |
| 796 field_trial->trial_name(), field_trial->group_name_internal()); | 783 field_trial->trial_name(), field_trial->group_name_internal()); |
| 797 } | 784 } |
| 798 | 785 |
| 786 // static |
| 787 size_t FieldTrialList::GetFieldTrialCount() { |
| 788 if (!global_) |
| 789 return 0; |
| 790 AutoLock auto_lock(global_->lock_); |
| 791 return global_->registered_.size(); |
| 792 } |
| 793 |
| 794 // static |
| 795 void FieldTrialList::CreateTrialsFromSharedMemory( |
| 796 std::unique_ptr<SharedMemory> shm) { |
| 797 const SharedPersistentMemoryAllocator shalloc(std::move(shm), 0, |
| 798 kAllocatorName, true); |
| 799 PersistentMemoryAllocator::Iterator iter(&shalloc); |
| 800 |
| 801 SharedPersistentMemoryAllocator::Reference ref; |
| 802 while ((ref = iter.GetNextOfType(kFieldTrialType)) != |
| 803 SharedPersistentMemoryAllocator::kReferenceNull) { |
| 804 const FieldTrialEntry* entry = |
| 805 shalloc.GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); |
| 806 FieldTrial* trial = |
| 807 CreateFieldTrial(entry->GetTrialName(), entry->GetGroupName()); |
| 808 |
| 809 if (entry->activated) { |
| 810 // Call |group()| to mark the trial as "used" and notify observers, if |
| 811 // any. This is useful to ensure that field trials created in child |
| 812 // processes are properly reported in crash reports. |
| 813 trial->group(); |
| 814 } |
| 815 } |
| 816 } |
| 817 |
| 799 #if !defined(OS_NACL) | 818 #if !defined(OS_NACL) |
| 800 // static | 819 // static |
| 801 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { | 820 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { |
| 821 if (!global_) |
| 822 return; |
| 802 AutoLock auto_lock(global_->lock_); | 823 AutoLock auto_lock(global_->lock_); |
| 803 // Create the allocator if not already created and add all existing trials. | 824 // Create the allocator if not already created and add all existing trials. |
| 804 if (global_->field_trial_allocator_ != nullptr) | 825 if (global_->field_trial_allocator_ != nullptr) |
| 805 return; | 826 return; |
| 806 | 827 |
| 807 std::unique_ptr<SharedMemory> shm(new SharedMemory()); | 828 std::unique_ptr<SharedMemory> shm(new SharedMemory()); |
| 808 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) | 829 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) |
| 809 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 830 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); |
| 810 | 831 |
| 811 // TODO(lawrencewu): call UpdateTrackingHistograms() when all field trials | 832 // TODO(lawrencewu): call UpdateTrackingHistograms() when all field trials |
| 812 // have been registered (perhaps in the destructor?) | 833 // have been registered (perhaps in the destructor?) |
| 813 global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( | 834 global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( |
| 814 std::move(shm), 0, kAllocatorName, false)); | 835 std::move(shm), 0, kAllocatorName, false)); |
| 815 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 836 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); |
| 816 | 837 |
| 817 // Add all existing field trials. | 838 // Add all existing field trials. |
| 818 for (const auto& registered : global_->registered_) { | 839 for (const auto& registered : global_->registered_) { |
| 819 registered.second->AddToAllocatorWhileLocked( | 840 AddToAllocatorWhileLocked(registered.second); |
| 820 global_->field_trial_allocator_.get()); | |
| 821 } | 841 } |
| 822 | 842 |
| 823 #if defined(OS_WIN) | 843 #if defined(OS_WIN) |
| 824 // Set |readonly_allocator_handle_| so we can pass it to be inherited and via | 844 // Set |readonly_allocator_handle_| so we can pass it to be inherited and |
| 825 // the command line. | 845 // via the command line. |
| 826 global_->readonly_allocator_handle_ = | 846 global_->readonly_allocator_handle_ = |
| 827 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 847 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
| 828 #endif | 848 #endif |
| 829 } | 849 } |
| 830 #endif | 850 #endif |
| 831 | 851 |
| 832 void FieldTrial::AddToAllocatorWhileLocked( | 852 // static |
| 833 SharedPersistentMemoryAllocator* allocator) { | 853 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
| 854 SharedPersistentMemoryAllocator* allocator = |
| 855 global_->field_trial_allocator_.get(); |
| 856 |
| 834 // Don't do anything if the allocator hasn't been instantiated yet. | 857 // Don't do anything if the allocator hasn't been instantiated yet. |
| 835 if (allocator == nullptr) | 858 if (allocator == nullptr) |
| 836 return; | 859 return; |
| 837 | 860 |
| 838 State trial_state; | 861 // Or if we've already added it. |
| 839 if (!GetState(&trial_state)) | 862 if (field_trial->ref_ != SharedPersistentMemoryAllocator::kReferenceNull) |
| 863 return; |
| 864 |
| 865 FieldTrial::State trial_state; |
| 866 if (!field_trial->GetState(&trial_state)) |
| 840 return; | 867 return; |
| 841 | 868 |
| 842 size_t trial_name_size = trial_state.trial_name.size() + 1; | 869 size_t trial_name_size = trial_state.trial_name.size() + 1; |
| 843 size_t group_name_size = trial_state.group_name.size() + 1; | 870 size_t group_name_size = trial_state.group_name.size() + 1; |
| 844 size_t size = sizeof(FieldTrialEntry) + trial_name_size + group_name_size; | 871 size_t size = sizeof(FieldTrialEntry) + trial_name_size + group_name_size; |
| 845 | 872 |
| 846 // Allocate just enough memory to fit the FieldTrialEntry struct, trial name, | 873 // Allocate just enough memory to fit the FieldTrialEntry struct, trial name, |
| 847 // and group name. | 874 // and group name. |
| 848 SharedPersistentMemoryAllocator::Reference ref = | 875 SharedPersistentMemoryAllocator::Reference ref = |
| 849 allocator->Allocate(size, kFieldTrialType); | 876 allocator->Allocate(size, kFieldTrialType); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 862 char* trial_name = reinterpret_cast<char*>(entry) + trial_name_offset; | 889 char* trial_name = reinterpret_cast<char*>(entry) + trial_name_offset; |
| 863 char* group_name = reinterpret_cast<char*>(entry) + group_name_offset; | 890 char* group_name = reinterpret_cast<char*>(entry) + group_name_offset; |
| 864 memcpy(trial_name, trial_state.trial_name.data(), trial_name_size); | 891 memcpy(trial_name, trial_state.trial_name.data(), trial_name_size); |
| 865 memcpy(group_name, trial_state.group_name.data(), group_name_size); | 892 memcpy(group_name, trial_state.group_name.data(), group_name_size); |
| 866 | 893 |
| 867 // Null terminate the strings. | 894 // Null terminate the strings. |
| 868 trial_name[trial_state.trial_name.size()] = '\0'; | 895 trial_name[trial_state.trial_name.size()] = '\0'; |
| 869 group_name[trial_state.group_name.size()] = '\0'; | 896 group_name[trial_state.group_name.size()] = '\0'; |
| 870 | 897 |
| 871 allocator->MakeIterable(ref); | 898 allocator->MakeIterable(ref); |
| 899 field_trial->ref_ = ref; |
| 872 } | 900 } |
| 873 | 901 |
| 874 // static | 902 // static |
| 875 size_t FieldTrialList::GetFieldTrialCount() { | 903 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( |
| 876 if (!global_) | 904 FieldTrial* field_trial) { |
| 877 return 0; | 905 SharedPersistentMemoryAllocator* allocator = |
| 878 AutoLock auto_lock(global_->lock_); | 906 global_->field_trial_allocator_.get(); |
| 879 return global_->registered_.size(); | 907 SharedPersistentMemoryAllocator::Reference ref = field_trial->ref_; |
| 908 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) { |
| 909 // It's fine to do this even if the allocator hasn't been instantiated |
| 910 // yet -- it'll just return early. |
| 911 AddToAllocatorWhileLocked(field_trial); |
| 912 } else { |
| 913 // It's also okay to do this even though the callee doesn't have a lock -- |
| 914 // the only thing that happens on a stale read here is a slight performance |
| 915 // hit from the child re-synchronizing activation state. |
| 916 FieldTrialEntry* entry = |
| 917 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
| 918 entry->activated = true; |
| 919 } |
| 880 } | 920 } |
| 881 | 921 |
| 882 // static | 922 // static |
| 883 const FieldTrial::EntropyProvider* | 923 const FieldTrial::EntropyProvider* |
| 884 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { | 924 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { |
| 885 if (!global_) { | 925 if (!global_) { |
| 886 used_without_global_ = true; | 926 used_without_global_ = true; |
| 887 return NULL; | 927 return NULL; |
| 888 } | 928 } |
| 889 | 929 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 904 return; | 944 return; |
| 905 } | 945 } |
| 906 AutoLock auto_lock(global_->lock_); | 946 AutoLock auto_lock(global_->lock_); |
| 907 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 947 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 908 trial->AddRef(); | 948 trial->AddRef(); |
| 909 trial->SetTrialRegistered(); | 949 trial->SetTrialRegistered(); |
| 910 global_->registered_[trial->trial_name()] = trial; | 950 global_->registered_[trial->trial_name()] = trial; |
| 911 } | 951 } |
| 912 | 952 |
| 913 } // namespace base | 953 } // namespace base |
| OLD | NEW |