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/feature_list.h" | |
14 #include "base/logging.h" | 13 #include "base/logging.h" |
15 #include "base/metrics/field_trial_param_associator.h" | 14 #include "base/metrics/field_trial_param_associator.h" |
16 #include "base/pickle.h" | 15 #include "base/pickle.h" |
17 #include "base/process/memory.h" | 16 #include "base/process/memory.h" |
18 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
19 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
20 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
21 #include "base/strings/stringprintf.h" | 20 #include "base/strings/stringprintf.h" |
22 #include "base/strings/utf_string_conversions.h" | 21 #include "base/strings/utf_string_conversions.h" |
23 | 22 |
(...skipping 18 matching lines...) Expand all Loading... |
42 const char kPersistentStringSeparator = '/'; // Currently a slash. | 41 const char kPersistentStringSeparator = '/'; // Currently a slash. |
43 | 42 |
44 // Define a marker character to be used as a prefix to a trial name on the | 43 // Define a marker character to be used as a prefix to a trial name on the |
45 // command line which forces its activation. | 44 // command line which forces its activation. |
46 const char kActivationMarker = '*'; | 45 const char kActivationMarker = '*'; |
47 | 46 |
48 // Use shared memory to communicate field trial (experiment) state. Set to false | 47 // Use shared memory to communicate field trial (experiment) state. Set to false |
49 // for now while the implementation is fleshed out (e.g. data format, single | 48 // for now while the implementation is fleshed out (e.g. data format, single |
50 // shared memory segment). See https://codereview.chromium.org/2365273004/ and | 49 // shared memory segment). See https://codereview.chromium.org/2365273004/ and |
51 // crbug.com/653874 | 50 // crbug.com/653874 |
| 51 // The browser is the only process that has write access to the shared memory. |
| 52 // This is safe from race conditions because MakeIterable is a release operation |
| 53 // and GetNextOfType is an acquire operation, so memory writes before |
| 54 // MakeIterable happen before memory reads after GetNextOfType. |
52 const bool kUseSharedMemoryForFieldTrials = true; | 55 const bool kUseSharedMemoryForFieldTrials = true; |
53 | 56 |
54 // Constants for the field trial allocator. | 57 // Constants for the field trial allocator. |
55 const char kAllocatorName[] = "FieldTrialAllocator"; | 58 const char kAllocatorName[] = "FieldTrialAllocator"; |
56 const uint32_t kFieldTrialType = 0xABA17E13 + 2; // SHA1(FieldTrialEntry) v2 | 59 const uint32_t kFieldTrialType = 0xABA17E13 + 2; // SHA1(FieldTrialEntry) v2 |
57 | 60 |
58 // We allocate 128 KiB to hold all the field trial data. This should be enough, | 61 // We allocate 128 KiB to hold all the field trial data. This should be enough, |
59 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016). | 62 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016). |
60 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped | 63 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped |
61 // to physical memory when they are touched. If the size of the allocated field | 64 // to physical memory when they are touched. If the size of the allocated field |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 trials_string_piece.substr(next_item, name_end - next_item); | 244 trials_string_piece.substr(next_item, name_end - next_item); |
242 entry.group_name = | 245 entry.group_name = |
243 trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1); | 246 trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1); |
244 next_item = group_name_end + 1; | 247 next_item = group_name_end + 1; |
245 | 248 |
246 entries->push_back(std::move(entry)); | 249 entries->push_back(std::move(entry)); |
247 } | 250 } |
248 return true; | 251 return true; |
249 } | 252 } |
250 | 253 |
251 void AddForceFieldTrialsFlag(CommandLine* cmd_line) { | 254 void AddFeatureAndFieldTrialFlags(const char* enable_features_switch, |
| 255 const char* disable_features_switch, |
| 256 CommandLine* cmd_line) { |
| 257 std::string enabled_features; |
| 258 std::string disabled_features; |
| 259 FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features, |
| 260 &disabled_features); |
| 261 |
| 262 if (!enabled_features.empty()) |
| 263 cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features); |
| 264 if (!disabled_features.empty()) |
| 265 cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features); |
| 266 |
252 std::string field_trial_states; | 267 std::string field_trial_states; |
253 FieldTrialList::AllStatesToString(&field_trial_states); | 268 FieldTrialList::AllStatesToString(&field_trial_states); |
254 if (!field_trial_states.empty()) { | 269 if (!field_trial_states.empty()) { |
255 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, | 270 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, |
256 field_trial_states); | 271 field_trial_states); |
257 } | 272 } |
258 } | 273 } |
259 | 274 |
260 #if defined(OS_WIN) | 275 #if defined(OS_WIN) |
261 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | 276 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
798 #endif | 813 #endif |
799 | 814 |
800 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { | 815 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { |
801 bool result = FieldTrialList::CreateTrialsFromString( | 816 bool result = FieldTrialList::CreateTrialsFromString( |
802 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), | 817 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), |
803 std::set<std::string>()); | 818 std::set<std::string>()); |
804 DCHECK(result); | 819 DCHECK(result); |
805 } | 820 } |
806 } | 821 } |
807 | 822 |
| 823 // static |
| 824 void FieldTrialList::CreateFeaturesFromCommandLine( |
| 825 const base::CommandLine& command_line, |
| 826 const char* enable_features_switch, |
| 827 const char* disable_features_switch, |
| 828 FeatureList* feature_list) { |
| 829 // Fallback to command line if not using shared memory. |
| 830 if (!kUseSharedMemoryForFieldTrials || |
| 831 !global_->field_trial_allocator_.get()) { |
| 832 return feature_list->InitializeFromCommandLine( |
| 833 command_line.GetSwitchValueASCII(enable_features_switch), |
| 834 command_line.GetSwitchValueASCII(disable_features_switch)); |
| 835 } |
| 836 |
| 837 feature_list->InitializeFromSharedMemory( |
| 838 global_->field_trial_allocator_.get()); |
| 839 } |
| 840 |
808 #if defined(POSIX_WITH_ZYGOTE) | 841 #if defined(POSIX_WITH_ZYGOTE) |
809 // static | 842 // static |
810 bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) { | 843 bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) { |
811 if (!kUseSharedMemoryForFieldTrials) | 844 if (!kUseSharedMemoryForFieldTrials) |
812 return false; | 845 return false; |
813 | 846 |
814 if (fd_key == -1) | 847 if (fd_key == -1) |
815 return false; | 848 return false; |
816 | 849 |
817 int fd = GlobalDescriptors::GetInstance()->MaybeGet(fd_key); | 850 int fd = GlobalDescriptors::GetInstance()->MaybeGet(fd_key); |
(...skipping 29 matching lines...) Expand all Loading... |
847 // We check for an invalid handle where this gets called. | 880 // We check for an invalid handle where this gets called. |
848 return global_->readonly_allocator_handle_; | 881 return global_->readonly_allocator_handle_; |
849 } | 882 } |
850 return kInvalidPlatformFile; | 883 return kInvalidPlatformFile; |
851 } | 884 } |
852 #endif | 885 #endif |
853 | 886 |
854 // static | 887 // static |
855 void FieldTrialList::CopyFieldTrialStateToFlags( | 888 void FieldTrialList::CopyFieldTrialStateToFlags( |
856 const char* field_trial_handle_switch, | 889 const char* field_trial_handle_switch, |
| 890 const char* enable_features_switch, |
| 891 const char* disable_features_switch, |
857 CommandLine* cmd_line) { | 892 CommandLine* cmd_line) { |
858 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, | 893 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, |
859 // content browser tests currently don't create a FieldTrialList because they | 894 // content browser tests currently don't create a FieldTrialList because they |
860 // don't run ChromeBrowserMainParts code where it's done for Chrome. | 895 // don't run ChromeBrowserMainParts code where it's done for Chrome. |
861 if (!global_) | 896 // Some tests depend on the enable and disable features flag switch, though, |
| 897 // so we can still add those even though AllStatesToString() will be a no-op. |
| 898 if (!global_) { |
| 899 AddFeatureAndFieldTrialFlags(enable_features_switch, |
| 900 disable_features_switch, cmd_line); |
862 return; | 901 return; |
| 902 } |
863 | 903 |
864 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) | 904 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) |
865 // Use shared memory to pass the state if the feature is enabled, otherwise | 905 // Use shared memory to pass the state if the feature is enabled, otherwise |
866 // fallback to passing it via the command line as a string. | 906 // fallback to passing it via the command line as a string. |
867 if (kUseSharedMemoryForFieldTrials) { | 907 if (kUseSharedMemoryForFieldTrials) { |
868 InstantiateFieldTrialAllocatorIfNeeded(); | 908 InstantiateFieldTrialAllocatorIfNeeded(); |
869 // If the readonly handle didn't get duplicated properly, then fallback to | 909 // If the readonly handle didn't get duplicated properly, then fallback to |
870 // original behavior. | 910 // original behavior. |
871 if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) { | 911 if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) { |
872 AddForceFieldTrialsFlag(cmd_line); | 912 AddFeatureAndFieldTrialFlags(enable_features_switch, |
| 913 disable_features_switch, cmd_line); |
873 return; | 914 return; |
874 } | 915 } |
875 | 916 |
876 global_->field_trial_allocator_->UpdateTrackingHistograms(); | 917 global_->field_trial_allocator_->UpdateTrackingHistograms(); |
877 | 918 |
878 #if defined(OS_WIN) | 919 #if defined(OS_WIN) |
879 // We need to pass a named anonymous handle to shared memory over the | 920 // We need to pass a named anonymous handle to shared memory over the |
880 // command line on Windows, since the child doesn't know which of the | 921 // command line on Windows, since the child doesn't know which of the |
881 // handles it inherited it should open. On POSIX, we don't need to do this | 922 // handles it inherited it should open. On POSIX, we don't need to do this |
882 // -- we dup the fd into a fixed fd kFieldTrialDescriptor, so we can just | 923 // -- we dup the fd into a fixed fd kFieldTrialDescriptor, so we can just |
883 // look it up there. | 924 // look it up there. |
884 // PlatformFile is typedef'd to HANDLE which is typedef'd to void *. We | 925 // PlatformFile is typedef'd to HANDLE which is typedef'd to void *. We |
885 // basically cast the handle into an int (uintptr_t, to be exact), stringify | 926 // basically cast the handle into an int (uintptr_t, to be exact), stringify |
886 // the int, and pass it as a command-line flag. The child process will do | 927 // the int, and pass it as a command-line flag. The child process will do |
887 // the reverse conversions to retrieve the handle. See | 928 // the reverse conversions to retrieve the handle. See |
888 // http://stackoverflow.com/a/153077 | 929 // http://stackoverflow.com/a/153077 |
889 auto uintptr_handle = | 930 auto uintptr_handle = |
890 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); | 931 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); |
891 std::string field_trial_handle = std::to_string(uintptr_handle); | 932 std::string field_trial_handle = std::to_string(uintptr_handle); |
892 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); | 933 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); |
893 #endif | 934 #endif |
894 return; | 935 return; |
895 } | 936 } |
896 #endif | 937 #endif |
897 | 938 |
898 AddForceFieldTrialsFlag(cmd_line); | 939 AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch, |
| 940 cmd_line); |
899 } | 941 } |
900 | 942 |
901 // static | 943 // static |
902 FieldTrial* FieldTrialList::CreateFieldTrial( | 944 FieldTrial* FieldTrialList::CreateFieldTrial( |
903 const std::string& name, | 945 const std::string& name, |
904 const std::string& group_name) { | 946 const std::string& group_name) { |
905 DCHECK(global_); | 947 DCHECK(global_); |
906 DCHECK_GE(name.size(), 0u); | 948 DCHECK_GE(name.size(), 0u); |
907 DCHECK_GE(group_name.size(), 0u); | 949 DCHECK_GE(group_name.size(), 0u); |
908 if (name.empty() || group_name.empty() || !global_) | 950 if (name.empty() || group_name.empty() || !global_) |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1164 | 1206 |
1165 global_->field_trial_allocator_.reset( | 1207 global_->field_trial_allocator_.reset( |
1166 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); | 1208 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); |
1167 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 1209 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); |
1168 | 1210 |
1169 // Add all existing field trials. | 1211 // Add all existing field trials. |
1170 for (const auto& registered : global_->registered_) { | 1212 for (const auto& registered : global_->registered_) { |
1171 AddToAllocatorWhileLocked(registered.second); | 1213 AddToAllocatorWhileLocked(registered.second); |
1172 } | 1214 } |
1173 | 1215 |
| 1216 // Add all existing features. |
| 1217 FeatureList::GetInstance()->AddFeaturesToAllocator( |
| 1218 global_->field_trial_allocator_.get()); |
| 1219 |
1174 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) | 1220 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) |
1175 // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 1221 // Set |readonly_allocator_handle_| so we can pass it to be inherited and |
1176 // via the command line. | 1222 // via the command line. |
1177 global_->readonly_allocator_handle_ = | 1223 global_->readonly_allocator_handle_ = |
1178 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 1224 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
1179 #endif | 1225 #endif |
1180 } | 1226 } |
1181 #endif | 1227 #endif |
1182 | 1228 |
1183 // static | 1229 // static |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1279 return; | 1325 return; |
1280 } | 1326 } |
1281 AutoLock auto_lock(global_->lock_); | 1327 AutoLock auto_lock(global_->lock_); |
1282 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1328 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
1283 trial->AddRef(); | 1329 trial->AddRef(); |
1284 trial->SetTrialRegistered(); | 1330 trial->SetTrialRegistered(); |
1285 global_->registered_[trial->trial_name()] = trial; | 1331 global_->registered_[trial->trial_name()] = trial; |
1286 } | 1332 } |
1287 | 1333 |
1288 } // namespace base | 1334 } // namespace base |
OLD | NEW |