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" |
| 11 #include "base/build_time.h" | 11 #include "base/build_time.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/debug/alias.h" | 13 #include "base/debug/alias.h" |
| 14 #include "base/feature_list.h" | 14 #include "base/feature_list.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/pickle.h" | 16 #include "base/pickle.h" |
| 17 #include "base/process/memory.h" | 17 #include "base/process/memory.h" |
| 18 #include "base/rand_util.h" | 18 #include "base/rand_util.h" |
| 19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/string_util.h" | 20 #include "base/strings/string_util.h" |
| 21 #include "base/strings/stringprintf.h" | 21 #include "base/strings/stringprintf.h" |
| 22 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
| 23 | 23 |
| 24 #if defined(OS_POSIX) | |
| 25 #include "base/posix/global_descriptors.h" | |
| 26 #endif | |
| 27 | |
| 24 namespace base { | 28 namespace base { |
| 25 | 29 |
| 26 namespace { | 30 namespace { |
| 27 | 31 |
| 28 // Define a separator character to use when creating a persistent form of an | 32 // Define a separator character to use when creating a persistent form of an |
| 29 // instance. This is intended for use as a command line argument, passed to a | 33 // instance. This is intended for use as a command line argument, passed to a |
| 30 // second process to mimic our state (i.e., provide the same group name). | 34 // second process to mimic our state (i.e., provide the same group name). |
| 31 const char kPersistentStringSeparator = '/'; // Currently a slash. | 35 const char kPersistentStringSeparator = '/'; // Currently a slash. |
| 32 | 36 |
| 33 // Define a marker character to be used as a prefix to a trial name on the | 37 // Define a marker character to be used as a prefix to a trial name on the |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 226 #if defined(OS_WIN) | 230 #if defined(OS_WIN) |
| 227 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | 231 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
| 228 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 232 HANDLE src = allocator->shared_memory()->handle().GetHandle(); |
| 229 ProcessHandle process = GetCurrentProcess(); | 233 ProcessHandle process = GetCurrentProcess(); |
| 230 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 234 DWORD access = SECTION_MAP_READ | SECTION_QUERY; |
| 231 HANDLE dst; | 235 HANDLE dst; |
| 232 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 236 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) |
| 233 return nullptr; | 237 return nullptr; |
| 234 return dst; | 238 return dst; |
| 235 } | 239 } |
| 240 #elif defined(OS_POSIX) && !defined(OS_NACL) | |
|
Alexei Svitkine (slow)
2016/11/23 17:49:55
Instead of these #elif chains that are confusing -
lawrencewu
2016/11/23 18:59:44
Done.
| |
| 241 int CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | |
| 242 SharedMemoryHandle new_handle; | |
| 243 allocator->shared_memory()->ShareReadOnlyToProcess(GetCurrentProcessHandle(), | |
| 244 &new_handle); | |
| 245 int fd = SharedMemory::GetFdFromSharedMemoryHandle(new_handle); | |
| 246 return fd; | |
|
Alexei Svitkine (slow)
2016/11/23 17:49:55
Nit: Merge with line above.
lawrencewu
2016/11/23 18:59:44
Done.
| |
| 247 } | |
| 236 #endif | 248 #endif |
| 237 | 249 |
| 238 } // namespace | 250 } // namespace |
| 239 | 251 |
| 240 // statics | 252 // statics |
| 241 const int FieldTrial::kNotFinalized = -1; | 253 const int FieldTrial::kNotFinalized = -1; |
| 242 const int FieldTrial::kDefaultGroupNumber = 0; | 254 const int FieldTrial::kDefaultGroupNumber = 0; |
| 243 bool FieldTrial::enable_benchmarking_ = false; | 255 bool FieldTrial::enable_benchmarking_ = false; |
| 244 | 256 |
| 245 int FieldTrialList::kNoExpirationYear = 0; | 257 int FieldTrialList::kNoExpirationYear = 0; |
| (...skipping 484 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 730 } | 742 } |
| 731 return true; | 743 return true; |
| 732 } | 744 } |
| 733 | 745 |
| 734 // static | 746 // static |
| 735 void FieldTrialList::CreateTrialsFromCommandLine( | 747 void FieldTrialList::CreateTrialsFromCommandLine( |
| 736 const CommandLine& cmd_line, | 748 const CommandLine& cmd_line, |
| 737 const char* field_trial_handle_switch) { | 749 const char* field_trial_handle_switch) { |
| 738 global_->create_trials_from_command_line_called_ = true; | 750 global_->create_trials_from_command_line_called_ = true; |
| 739 | 751 |
| 740 #if defined(OS_WIN) && !defined(OS_NACL) | |
| 741 if (cmd_line.HasSwitch(field_trial_handle_switch)) { | 752 if (cmd_line.HasSwitch(field_trial_handle_switch)) { |
| 742 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); | 753 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); |
| 743 int field_trial_handle = std::stoi(arg); | 754 #if defined(OS_WIN) |
| 744 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | 755 bool result = CreateTrialsFromWindowsSwitch(arg); |
| 745 bool result = CreateTrialsFromWindowsHandle(handle); | |
| 746 DCHECK(result); | 756 DCHECK(result); |
| 757 #elif defined(OS_POSIX) && !defined(OS_NACL) | |
| 758 bool result = CreateTrialsFromPosixSwitch(arg); | |
| 759 DCHECK(result); | |
| 760 #endif | |
| 747 } | 761 } |
| 748 #endif | |
| 749 | 762 |
| 750 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { | 763 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { |
| 751 bool result = FieldTrialList::CreateTrialsFromString( | 764 bool result = FieldTrialList::CreateTrialsFromString( |
| 752 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), | 765 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), |
| 753 std::set<std::string>()); | 766 std::set<std::string>()); |
| 754 DCHECK(result); | 767 DCHECK(result); |
| 755 } | 768 } |
| 756 } | 769 } |
| 757 | 770 |
| 758 #if defined(OS_WIN) | 771 #if defined(OS_WIN) |
| 759 // static | 772 // static |
| 760 void FieldTrialList::AppendFieldTrialHandleIfNeeded( | 773 void FieldTrialList::AppendFieldTrialHandleIfNeeded( |
| 761 HandlesToInheritVector* handles) { | 774 HandlesToInheritVector* handles) { |
| 762 if (!global_) | 775 if (!global_) |
| 763 return; | 776 return; |
| 764 if (kUseSharedMemoryForFieldTrials) { | 777 if (kUseSharedMemoryForFieldTrials) { |
| 765 InstantiateFieldTrialAllocatorIfNeeded(); | 778 InstantiateFieldTrialAllocatorIfNeeded(); |
| 766 if (global_->readonly_allocator_handle_) | 779 if (global_->readonly_allocator_handle_) |
| 767 handles->push_back(global_->readonly_allocator_handle_); | 780 handles->push_back(global_->readonly_allocator_handle_); |
| 768 } | 781 } |
| 769 } | 782 } |
| 783 #elif defined(OS_POSIX) && !defined(OS_NACL) | |
| 784 // static | |
| 785 int FieldTrialList::FieldTrialHandle() { | |
| 786 if (!global_) | |
| 787 return -1; | |
| 788 if (kUseSharedMemoryForFieldTrials) { | |
| 789 InstantiateFieldTrialAllocatorIfNeeded(); | |
| 790 if (global_->readonly_allocator_handle_ != -1) | |
| 791 return global_->readonly_allocator_handle_; | |
| 792 } | |
| 793 return -1; | |
| 794 } | |
| 770 #endif | 795 #endif |
| 771 | 796 |
| 772 // static | 797 // static |
| 773 void FieldTrialList::CopyFieldTrialStateToFlags( | 798 void FieldTrialList::CopyFieldTrialStateToFlags( |
| 774 const char* field_trial_handle_switch, | 799 const char* field_trial_handle_switch, |
| 775 CommandLine* cmd_line) { | 800 CommandLine* cmd_line) { |
| 776 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, | 801 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, |
| 777 // content browser tests currently don't create a FieldTrialList because they | 802 // content browser tests currently don't create a FieldTrialList because they |
| 778 // don't run ChromeBrowserMainParts code where it's done for Chrome. | 803 // don't run ChromeBrowserMainParts code where it's done for Chrome. |
| 779 if (!global_) | 804 if (!global_) |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 795 // int (uintptr_t, to be exact), stringify the int, and pass it as a | 820 // int (uintptr_t, to be exact), stringify the int, and pass it as a |
| 796 // command-line flag. The child process will do the reverse conversions to | 821 // command-line flag. The child process will do the reverse conversions to |
| 797 // retrieve the handle. See http://stackoverflow.com/a/153077 | 822 // retrieve the handle. See http://stackoverflow.com/a/153077 |
| 798 auto uintptr_handle = | 823 auto uintptr_handle = |
| 799 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); | 824 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); |
| 800 std::string field_trial_handle = std::to_string(uintptr_handle); | 825 std::string field_trial_handle = std::to_string(uintptr_handle); |
| 801 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); | 826 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); |
| 802 global_->field_trial_allocator_->UpdateTrackingHistograms(); | 827 global_->field_trial_allocator_->UpdateTrackingHistograms(); |
| 803 return; | 828 return; |
| 804 } | 829 } |
| 830 #elif defined(OS_POSIX) && !defined(OS_NACL) | |
| 831 // Use shared memory to pass the state if the feature is enabled, otherwise | |
| 832 // fallback to passing it via the command line as a string. | |
| 833 if (kUseSharedMemoryForFieldTrials) { | |
| 834 InstantiateFieldTrialAllocatorIfNeeded(); | |
| 835 // If the readonly handle didn't get duplicated properly, then fallback to | |
| 836 // original behavior. | |
| 837 if (global_->readonly_allocator_handle_ == -1) { | |
| 838 AddForceFieldTrialsFlag(cmd_line); | |
| 839 return; | |
| 840 } | |
| 841 | |
| 842 std::string field_trial_handle = | |
| 843 std::to_string(global_->readonly_allocator_handle_); | |
| 844 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); | |
| 845 global_->field_trial_allocator_->UpdateTrackingHistograms(); | |
| 846 return; | |
| 847 } | |
| 805 #endif | 848 #endif |
| 806 | 849 |
| 807 AddForceFieldTrialsFlag(cmd_line); | 850 AddForceFieldTrialsFlag(cmd_line); |
| 808 } | 851 } |
| 809 | 852 |
| 810 // static | 853 // static |
| 811 FieldTrial* FieldTrialList::CreateFieldTrial( | 854 FieldTrial* FieldTrialList::CreateFieldTrial( |
| 812 const std::string& name, | 855 const std::string& name, |
| 813 const std::string& group_name) { | 856 const std::string& group_name) { |
| 814 DCHECK(global_); | 857 DCHECK(global_); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 885 // static | 928 // static |
| 886 size_t FieldTrialList::GetFieldTrialCount() { | 929 size_t FieldTrialList::GetFieldTrialCount() { |
| 887 if (!global_) | 930 if (!global_) |
| 888 return 0; | 931 return 0; |
| 889 AutoLock auto_lock(global_->lock_); | 932 AutoLock auto_lock(global_->lock_); |
| 890 return global_->registered_.size(); | 933 return global_->registered_.size(); |
| 891 } | 934 } |
| 892 | 935 |
| 893 #if defined(OS_WIN) | 936 #if defined(OS_WIN) |
| 894 // static | 937 // static |
| 895 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) { | 938 bool FieldTrialList::CreateTrialsFromWindowsSwitch(std::string cli_switch) { |
| 939 int field_trial_handle = std::stoi(cli_switch); | |
| 940 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | |
| 896 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); | 941 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); |
| 942 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); | |
| 943 } | |
| 944 #elif defined(OS_POSIX) && !defined(OS_NACL) | |
| 945 // static | |
| 946 bool FieldTrialList::CreateTrialsFromPosixSwitch(std::string cli_switch) { | |
| 947 int fd_key = std::stoi(cli_switch); | |
| 948 int fd = GlobalDescriptors::GetInstance()->Get(fd_key); | |
| 949 SharedMemoryHandle shm_handle(fd, true); | |
| 950 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); | |
| 951 } | |
| 952 #endif | |
| 897 | 953 |
| 954 #if !defined(OS_NACL) | |
| 955 // static | |
| 956 bool FieldTrialList::CreateTrialsFromSharedMemoryHandle( | |
| 957 SharedMemoryHandle shm_handle) { | |
| 898 // shm gets deleted when it gets out of scope, but that's OK because we need | 958 // shm gets deleted when it gets out of scope, but that's OK because we need |
| 899 // it only for the duration of this method. | 959 // it only for the duration of this method. |
| 900 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); | 960 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); |
| 901 if (!shm.get()->Map(kFieldTrialAllocationSize)) | 961 if (!shm.get()->Map(kFieldTrialAllocationSize)) |
| 902 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 962 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); |
| 903 | 963 |
| 904 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); | 964 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); |
| 905 } | 965 } |
| 906 #endif | 966 #endif |
| 907 | 967 |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 977 #if !defined(OS_NACL) | 1037 #if !defined(OS_NACL) |
| 978 // static | 1038 // static |
| 979 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { | 1039 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { |
| 980 if (!global_) | 1040 if (!global_) |
| 981 return; | 1041 return; |
| 982 AutoLock auto_lock(global_->lock_); | 1042 AutoLock auto_lock(global_->lock_); |
| 983 // Create the allocator if not already created and add all existing trials. | 1043 // Create the allocator if not already created and add all existing trials. |
| 984 if (global_->field_trial_allocator_ != nullptr) | 1044 if (global_->field_trial_allocator_ != nullptr) |
| 985 return; | 1045 return; |
| 986 | 1046 |
| 1047 SharedMemoryCreateOptions options; | |
| 1048 options.size = kFieldTrialAllocationSize; | |
| 1049 options.share_read_only = true; | |
| 1050 | |
| 987 std::unique_ptr<SharedMemory> shm(new SharedMemory()); | 1051 std::unique_ptr<SharedMemory> shm(new SharedMemory()); |
| 988 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) | 1052 if (!shm->Create(options)) |
| 1053 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | |
| 1054 | |
| 1055 if (!shm->Map(kFieldTrialAllocationSize)) | |
| 989 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 1056 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); |
| 990 | 1057 |
| 991 global_->field_trial_allocator_.reset( | 1058 global_->field_trial_allocator_.reset( |
| 992 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); | 1059 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); |
| 993 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 1060 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); |
| 994 | 1061 |
| 995 // Add all existing field trials. | 1062 // Add all existing field trials. |
| 996 for (const auto& registered : global_->registered_) { | 1063 for (const auto& registered : global_->registered_) { |
| 997 AddToAllocatorWhileLocked(registered.second); | 1064 AddToAllocatorWhileLocked(registered.second); |
| 998 } | 1065 } |
| 999 | 1066 |
| 1000 #if defined(OS_WIN) | 1067 #if defined(OS_WIN) || defined(OS_POSIX) |
| 1001 // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 1068 // Set |readonly_allocator_handle_| so we can pass it to be inherited and |
| 1002 // via the command line. | 1069 // via the command line. |
| 1003 global_->readonly_allocator_handle_ = | 1070 global_->readonly_allocator_handle_ = |
| 1004 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 1071 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
| 1005 #endif | 1072 #endif |
| 1006 } | 1073 } |
| 1007 #endif | 1074 #endif |
| 1008 | 1075 |
| 1009 // static | 1076 // static |
| 1010 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { | 1077 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1105 return; | 1172 return; |
| 1106 } | 1173 } |
| 1107 AutoLock auto_lock(global_->lock_); | 1174 AutoLock auto_lock(global_->lock_); |
| 1108 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1175 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 1109 trial->AddRef(); | 1176 trial->AddRef(); |
| 1110 trial->SetTrialRegistered(); | 1177 trial->SetTrialRegistered(); |
| 1111 global_->registered_[trial->trial_name()] = trial; | 1178 global_->registered_[trial->trial_name()] = trial; |
| 1112 } | 1179 } |
| 1113 | 1180 |
| 1114 } // namespace base | 1181 } // namespace base |
| OLD | NEW |