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 |