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 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 175 void AddForceFieldTrialsFlag(CommandLine* cmd_line) { | 175 void AddForceFieldTrialsFlag(CommandLine* cmd_line) { |
| 176 std::string field_trial_states; | 176 std::string field_trial_states; |
| 177 FieldTrialList::AllStatesToString(&field_trial_states); | 177 FieldTrialList::AllStatesToString(&field_trial_states); |
| 178 if (!field_trial_states.empty()) { | 178 if (!field_trial_states.empty()) { |
| 179 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, | 179 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, |
| 180 field_trial_states); | 180 field_trial_states); |
| 181 } | 181 } |
| 182 } | 182 } |
| 183 | 183 |
| 184 #if defined(OS_WIN) | 184 #if defined(OS_WIN) |
| 185 HANDLE CreateReadOnlyHandle(SharedPersistentMemoryAllocator* allocator) { | 185 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
| 186 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 186 HANDLE src = allocator->shared_memory()->handle().GetHandle(); |
| 187 ProcessHandle process = GetCurrentProcess(); | 187 ProcessHandle process = GetCurrentProcess(); |
| 188 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 188 DWORD access = SECTION_MAP_READ | SECTION_QUERY; |
| 189 HANDLE dst; | 189 HANDLE dst; |
| 190 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 190 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) |
| 191 return nullptr; | 191 return nullptr; |
| 192 return dst; | 192 return dst; |
| 193 } | 193 } |
| 194 #endif | 194 #endif |
| 195 | 195 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 318 divisor_(total_probability), | 318 divisor_(total_probability), |
| 319 default_group_name_(default_group_name), | 319 default_group_name_(default_group_name), |
| 320 random_(GetGroupBoundaryValue(total_probability, entropy_value)), | 320 random_(GetGroupBoundaryValue(total_probability, entropy_value)), |
| 321 accumulated_group_probability_(0), | 321 accumulated_group_probability_(0), |
| 322 next_group_number_(kDefaultGroupNumber + 1), | 322 next_group_number_(kDefaultGroupNumber + 1), |
| 323 group_(kNotFinalized), | 323 group_(kNotFinalized), |
| 324 enable_field_trial_(true), | 324 enable_field_trial_(true), |
| 325 forced_(false), | 325 forced_(false), |
| 326 group_reported_(false), | 326 group_reported_(false), |
| 327 trial_registered_(false), | 327 trial_registered_(false), |
| 328 ref_(SharedPersistentMemoryAllocator::kReferenceNull) { | 328 ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull) { |
| 329 DCHECK_GT(total_probability, 0); | 329 DCHECK_GT(total_probability, 0); |
| 330 DCHECK(!trial_name_.empty()); | 330 DCHECK(!trial_name_.empty()); |
| 331 DCHECK(!default_group_name_.empty()); | 331 DCHECK(!default_group_name_.empty()); |
| 332 } | 332 } |
| 333 | 333 |
| 334 FieldTrial::~FieldTrial() {} | 334 FieldTrial::~FieldTrial() {} |
| 335 | 335 |
| 336 void FieldTrial::SetTrialRegistered() { | 336 void FieldTrial::SetTrialRegistered() { |
| 337 DCHECK_EQ(kNotFinalized, group_); | 337 DCHECK_EQ(kNotFinalized, group_); |
| 338 DCHECK(!trial_registered_); | 338 DCHECK(!trial_registered_); |
| (...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 658 | 658 |
| 659 // static | 659 // static |
| 660 void FieldTrialList::CreateTrialsFromCommandLine( | 660 void FieldTrialList::CreateTrialsFromCommandLine( |
| 661 const CommandLine& cmd_line, | 661 const CommandLine& cmd_line, |
| 662 const char* field_trial_handle_switch) { | 662 const char* field_trial_handle_switch) { |
| 663 DCHECK(global_); | 663 DCHECK(global_); |
| 664 | 664 |
| 665 #if defined(OS_WIN) && !defined(OS_NACL) | 665 #if defined(OS_WIN) && !defined(OS_NACL) |
| 666 if (cmd_line.HasSwitch(field_trial_handle_switch)) { | 666 if (cmd_line.HasSwitch(field_trial_handle_switch)) { |
| 667 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); | 667 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); |
| 668 size_t token = arg.find(","); | 668 int field_trial_handle = std::stoi(arg); |
| 669 int field_trial_handle = std::stoi(arg.substr(0, token)); | |
| 670 size_t field_trial_length = std::stoi(arg.substr(token + 1, arg.length())); | |
| 671 | |
| 672 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | 669 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); |
| 673 SharedMemoryHandle shm_handle = | 670 bool result = CreateTrialsFromWindowsHandle(handle); |
| 674 SharedMemoryHandle(handle, GetCurrentProcId()); | |
| 675 | |
| 676 // Gets deleted when it gets out of scope, but that's OK because we need it | |
| 677 // only for the duration of this method. | |
| 678 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); | |
| 679 if (!shm.get()->Map(field_trial_length)) | |
| 680 TerminateBecauseOutOfMemory(field_trial_length); | |
| 681 | |
| 682 bool result = FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); | |
| 683 DCHECK(result); | 671 DCHECK(result); |
| 684 return; | |
| 685 } | 672 } |
| 686 #endif | 673 #endif |
| 687 | 674 |
| 688 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { | 675 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { |
| 689 bool result = FieldTrialList::CreateTrialsFromString( | 676 bool result = FieldTrialList::CreateTrialsFromString( |
| 690 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), | 677 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), |
| 691 std::set<std::string>()); | 678 std::set<std::string>()); |
| 692 DCHECK(result); | 679 DCHECK(result); |
| 693 } | 680 } |
| 694 } | 681 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 728 AddForceFieldTrialsFlag(cmd_line); | 715 AddForceFieldTrialsFlag(cmd_line); |
| 729 return; | 716 return; |
| 730 } | 717 } |
| 731 | 718 |
| 732 // HANDLE is just typedef'd to void *. We basically cast the handle into an | 719 // HANDLE is just typedef'd to void *. We basically cast the handle into an |
| 733 // int (uintptr_t, to be exact), stringify the int, and pass it as a | 720 // int (uintptr_t, to be exact), stringify the int, and pass it as a |
| 734 // command-line flag. The child process will do the reverse conversions to | 721 // command-line flag. The child process will do the reverse conversions to |
| 735 // retrieve the handle. See http://stackoverflow.com/a/153077 | 722 // retrieve the handle. See http://stackoverflow.com/a/153077 |
| 736 auto uintptr_handle = | 723 auto uintptr_handle = |
| 737 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); | 724 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); |
| 738 size_t field_trial_length = | 725 std::string field_trial_handle = std::to_string(uintptr_handle); |
| 739 global_->field_trial_allocator_->shared_memory()->mapped_size(); | |
| 740 std::string field_trial_handle = std::to_string(uintptr_handle) + "," + | |
| 741 std::to_string(field_trial_length); | |
| 742 | |
| 743 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); | 726 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); |
| 744 global_->field_trial_allocator_->UpdateTrackingHistograms(); | 727 global_->field_trial_allocator_->UpdateTrackingHistograms(); |
| 745 return; | 728 return; |
| 746 } | 729 } |
| 747 #endif | 730 #endif |
| 748 | 731 |
| 749 AddForceFieldTrialsFlag(cmd_line); | 732 AddForceFieldTrialsFlag(cmd_line); |
| 750 } | 733 } |
| 751 | 734 |
| 752 // static | 735 // static |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 825 } | 808 } |
| 826 | 809 |
| 827 // static | 810 // static |
| 828 size_t FieldTrialList::GetFieldTrialCount() { | 811 size_t FieldTrialList::GetFieldTrialCount() { |
| 829 if (!global_) | 812 if (!global_) |
| 830 return 0; | 813 return 0; |
| 831 AutoLock auto_lock(global_->lock_); | 814 AutoLock auto_lock(global_->lock_); |
| 832 return global_->registered_.size(); | 815 return global_->registered_.size(); |
| 833 } | 816 } |
| 834 | 817 |
| 818 #if defined(OS_WIN) | |
| 819 // static | |
| 820 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) { | |
| 821 SharedMemoryHandle shm_handle = | |
| 822 SharedMemoryHandle(handle, GetCurrentProcId()); | |
|
Alexei Svitkine (slow)
2016/11/16 01:55:12
Nit: SharedMemoryHandle shm_handle(andle, GetCurre
| |
| 823 | |
| 824 // shm gets deleted when it gets out of scope, but that's OK because we need | |
| 825 // it only for the duration of this method. | |
| 826 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); | |
| 827 if (!shm.get()->Map(kFieldTrialAllocationSize)) | |
| 828 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | |
| 829 | |
| 830 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); | |
| 831 } | |
| 832 #endif | |
| 833 | |
| 835 // static | 834 // static |
| 836 bool FieldTrialList::CreateTrialsFromSharedMemory( | 835 bool FieldTrialList::CreateTrialsFromSharedMemory( |
| 837 std::unique_ptr<SharedMemory> shm) { | 836 std::unique_ptr<SharedMemory> shm) { |
| 838 global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( | 837 global_->field_trial_allocator_.reset( |
| 839 std::move(shm), 0, kAllocatorName, true)); | 838 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); |
| 840 SharedPersistentMemoryAllocator* shalloc = | 839 FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); |
| 841 global_->field_trial_allocator_.get(); | 840 FieldTrialAllocator::Iterator mem_iter(shalloc); |
| 842 PersistentMemoryAllocator::Iterator mem_iter(shalloc); | |
| 843 | 841 |
| 844 SharedPersistentMemoryAllocator::Reference ref; | 842 FieldTrial::FieldTrialRef ref; |
| 845 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != | 843 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| 846 SharedPersistentMemoryAllocator::kReferenceNull) { | 844 FieldTrialAllocator::kReferenceNull) { |
| 847 const FieldTrialEntry* entry = | 845 const FieldTrialEntry* entry = |
| 848 shalloc->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | 846 shalloc->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); |
| 849 | 847 |
| 850 StringPiece trial_name; | 848 StringPiece trial_name; |
| 851 StringPiece group_name; | 849 StringPiece group_name; |
| 852 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) | 850 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) |
| 853 return false; | 851 return false; |
| 854 | 852 |
| 855 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | 853 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take |
| 856 // StringPieces. | 854 // StringPieces. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 893 return; | 891 return; |
| 894 AutoLock auto_lock(global_->lock_); | 892 AutoLock auto_lock(global_->lock_); |
| 895 // Create the allocator if not already created and add all existing trials. | 893 // Create the allocator if not already created and add all existing trials. |
| 896 if (global_->field_trial_allocator_ != nullptr) | 894 if (global_->field_trial_allocator_ != nullptr) |
| 897 return; | 895 return; |
| 898 | 896 |
| 899 std::unique_ptr<SharedMemory> shm(new SharedMemory()); | 897 std::unique_ptr<SharedMemory> shm(new SharedMemory()); |
| 900 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) | 898 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) |
| 901 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 899 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); |
| 902 | 900 |
| 903 global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( | 901 global_->field_trial_allocator_.reset( |
| 904 std::move(shm), 0, kAllocatorName, false)); | 902 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); |
| 905 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 903 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); |
| 906 | 904 |
| 907 // Add all existing field trials. | 905 // Add all existing field trials. |
| 908 for (const auto& registered : global_->registered_) { | 906 for (const auto& registered : global_->registered_) { |
| 909 AddToAllocatorWhileLocked(registered.second); | 907 AddToAllocatorWhileLocked(registered.second); |
| 910 } | 908 } |
| 911 | 909 |
| 912 #if defined(OS_WIN) | 910 #if defined(OS_WIN) |
| 913 // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 911 // Set |readonly_allocator_handle_| so we can pass it to be inherited and |
| 914 // via the command line. | 912 // via the command line. |
| 915 global_->readonly_allocator_handle_ = | 913 global_->readonly_allocator_handle_ = |
| 916 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 914 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
| 917 #endif | 915 #endif |
| 918 } | 916 } |
| 919 #endif | 917 #endif |
| 920 | 918 |
| 921 // static | 919 // static |
| 922 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { | 920 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
| 923 SharedPersistentMemoryAllocator* allocator = | 921 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); |
| 924 global_->field_trial_allocator_.get(); | |
| 925 | 922 |
| 926 // Don't do anything if the allocator hasn't been instantiated yet. | 923 // Don't do anything if the allocator hasn't been instantiated yet. |
| 927 if (allocator == nullptr) | 924 if (allocator == nullptr) |
| 928 return; | 925 return; |
| 929 | 926 |
| 930 // Or if the allocator is read only, which means we are in a child process and | 927 // Or if the allocator is read only, which means we are in a child process and |
| 931 // shouldn't be writing to it. | 928 // shouldn't be writing to it. |
| 932 if (allocator->IsReadonly()) | 929 if (allocator->IsReadonly()) |
| 933 return; | 930 return; |
| 934 | 931 |
| 935 FieldTrial::State trial_state; | 932 FieldTrial::State trial_state; |
| 936 if (!field_trial->GetStateWhileLocked(&trial_state)) | 933 if (!field_trial->GetStateWhileLocked(&trial_state)) |
| 937 return; | 934 return; |
| 938 | 935 |
| 939 // Or if we've already added it. We must check after GetState since it can | 936 // Or if we've already added it. We must check after GetState since it can |
| 940 // also add to the allocator. | 937 // also add to the allocator. |
| 941 if (field_trial->ref_) | 938 if (field_trial->ref_) |
| 942 return; | 939 return; |
| 943 | 940 |
| 944 Pickle pickle; | 941 Pickle pickle; |
| 945 pickle.WriteString(trial_state.trial_name); | 942 pickle.WriteString(trial_state.trial_name); |
| 946 pickle.WriteString(trial_state.group_name); | 943 pickle.WriteString(trial_state.group_name); |
| 947 | 944 |
| 948 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); | 945 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); |
| 949 SharedPersistentMemoryAllocator::Reference ref = | 946 FieldTrial::FieldTrialRef ref = |
| 950 allocator->Allocate(total_size, kFieldTrialType); | 947 allocator->Allocate(total_size, kFieldTrialType); |
| 951 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) | 948 if (ref == FieldTrialAllocator::kReferenceNull) |
| 952 return; | 949 return; |
| 953 | 950 |
| 954 FieldTrialEntry* entry = | 951 FieldTrialEntry* entry = |
| 955 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 952 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
| 956 entry->activated = trial_state.activated; | 953 entry->activated = trial_state.activated; |
| 957 entry->size = pickle.size(); | 954 entry->size = pickle.size(); |
| 958 | 955 |
| 959 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in | 956 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in |
| 960 // memory, so we can avoid this memcpy. | 957 // memory, so we can avoid this memcpy. |
| 961 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); | 958 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); |
| 962 memcpy(dst, pickle.data(), pickle.size()); | 959 memcpy(dst, pickle.data(), pickle.size()); |
| 963 | 960 |
| 964 allocator->MakeIterable(ref); | 961 allocator->MakeIterable(ref); |
| 965 field_trial->ref_ = ref; | 962 field_trial->ref_ = ref; |
| 966 } | 963 } |
| 967 | 964 |
| 968 // static | 965 // static |
| 969 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( | 966 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( |
| 970 FieldTrial* field_trial) { | 967 FieldTrial* field_trial) { |
| 971 SharedPersistentMemoryAllocator* allocator = | 968 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); |
| 972 global_->field_trial_allocator_.get(); | |
| 973 | 969 |
| 974 // Check if we're in the child process and return early if so. | 970 // Check if we're in the child process and return early if so. |
| 975 if (allocator && allocator->IsReadonly()) | 971 if (allocator && allocator->IsReadonly()) |
| 976 return; | 972 return; |
| 977 | 973 |
| 978 SharedPersistentMemoryAllocator::Reference ref = field_trial->ref_; | 974 FieldTrial::FieldTrialRef ref = field_trial->ref_; |
| 979 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) { | 975 if (ref == FieldTrialAllocator::kReferenceNull) { |
| 980 // It's fine to do this even if the allocator hasn't been instantiated | 976 // It's fine to do this even if the allocator hasn't been instantiated |
| 981 // yet -- it'll just return early. | 977 // yet -- it'll just return early. |
| 982 AddToAllocatorWhileLocked(field_trial); | 978 AddToAllocatorWhileLocked(field_trial); |
| 983 } else { | 979 } else { |
| 984 // It's also okay to do this even though the callee doesn't have a lock -- | 980 // It's also okay to do this even though the callee doesn't have a lock -- |
| 985 // the only thing that happens on a stale read here is a slight performance | 981 // the only thing that happens on a stale read here is a slight performance |
| 986 // hit from the child re-synchronizing activation state. | 982 // hit from the child re-synchronizing activation state. |
| 987 FieldTrialEntry* entry = | 983 FieldTrialEntry* entry = |
| 988 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 984 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
| 989 entry->activated = true; | 985 entry->activated = true; |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 1015 return; | 1011 return; |
| 1016 } | 1012 } |
| 1017 AutoLock auto_lock(global_->lock_); | 1013 AutoLock auto_lock(global_->lock_); |
| 1018 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1014 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 1019 trial->AddRef(); | 1015 trial->AddRef(); |
| 1020 trial->SetTrialRegistered(); | 1016 trial->SetTrialRegistered(); |
| 1021 global_->registered_[trial->trial_name()] = trial; | 1017 global_->registered_[trial->trial_name()] = trial; |
| 1022 } | 1018 } |
| 1023 | 1019 |
| 1024 } // namespace base | 1020 } // namespace base |
| OLD | NEW |