| 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" | |
| 14 #include "base/feature_list.h" | 13 #include "base/feature_list.h" |
| 15 #include "base/logging.h" | 14 #include "base/logging.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 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 | 176 |
| 178 void AddForceFieldTrialsFlag(CommandLine* cmd_line) { | 177 void AddForceFieldTrialsFlag(CommandLine* cmd_line) { |
| 179 std::string field_trial_states; | 178 std::string field_trial_states; |
| 180 FieldTrialList::AllStatesToString(&field_trial_states); | 179 FieldTrialList::AllStatesToString(&field_trial_states); |
| 181 if (!field_trial_states.empty()) { | 180 if (!field_trial_states.empty()) { |
| 182 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, | 181 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, |
| 183 field_trial_states); | 182 field_trial_states); |
| 184 } | 183 } |
| 185 } | 184 } |
| 186 | 185 |
| 187 // In order to debug the crash where two field trials with the same name but | |
| 188 // different groups get added to the allocator, let's try to get the stack | |
| 189 // frame where the second duplicate gets added here as well as the trial/group | |
| 190 // names by going through the allocator and checking if a trial with the same | |
| 191 // name already exists. | |
| 192 void CheckAllocatorForTrial(FieldTrialList::FieldTrialAllocator* allocator, | |
| 193 const std::string& trial_name, | |
| 194 const std::string& group_name) { | |
| 195 FieldTrialList::FieldTrialAllocator::Iterator iter(allocator); | |
| 196 FieldTrial::FieldTrialRef ref; | |
| 197 while ((ref = iter.GetNextOfType(kFieldTrialType)) != | |
| 198 FieldTrialList::FieldTrialAllocator::kReferenceNull) { | |
| 199 const FieldTrialEntry* entry = | |
| 200 allocator->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | |
| 201 | |
| 202 StringPiece trial_name_to_check; | |
| 203 StringPiece group_name_to_check; | |
| 204 if (!entry->GetTrialAndGroupName(&trial_name_to_check, | |
| 205 &group_name_to_check)) { | |
| 206 CHECK(false); | |
| 207 } | |
| 208 | |
| 209 if (trial_name_to_check.as_string() == trial_name) { | |
| 210 constexpr size_t buf_size = 100; | |
| 211 | |
| 212 char trial_name_c_str[buf_size]; | |
| 213 char group_name_c_str[buf_size]; | |
| 214 char existing_group_name_c_str[buf_size]; | |
| 215 | |
| 216 strlcpy(trial_name_c_str, trial_name.c_str(), buf_size); | |
| 217 strlcpy(group_name_c_str, group_name.c_str(), buf_size); | |
| 218 strlcpy(existing_group_name_c_str, | |
| 219 group_name_to_check.as_string().c_str(), buf_size); | |
| 220 | |
| 221 debug::Alias(trial_name_c_str); | |
| 222 debug::Alias(group_name_c_str); | |
| 223 debug::Alias(existing_group_name_c_str); | |
| 224 CHECK_EQ(group_name_to_check.as_string(), group_name); | |
| 225 } | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 #if defined(OS_WIN) | 186 #if defined(OS_WIN) |
| 230 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | 187 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
| 231 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 188 HANDLE src = allocator->shared_memory()->handle().GetHandle(); |
| 232 ProcessHandle process = GetCurrentProcess(); | 189 ProcessHandle process = GetCurrentProcess(); |
| 233 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 190 DWORD access = SECTION_MAP_READ | SECTION_QUERY; |
| 234 HANDLE dst; | 191 HANDLE dst; |
| 235 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 192 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) |
| 236 return nullptr; | 193 return nullptr; |
| 237 return dst; | 194 return dst; |
| 238 } | 195 } |
| (...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 925 StringPiece trial_name; | 882 StringPiece trial_name; |
| 926 StringPiece group_name; | 883 StringPiece group_name; |
| 927 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) | 884 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) |
| 928 return false; | 885 return false; |
| 929 | 886 |
| 930 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | 887 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take |
| 931 // StringPieces. | 888 // StringPieces. |
| 932 FieldTrial* trial = | 889 FieldTrial* trial = |
| 933 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); | 890 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); |
| 934 | 891 |
| 935 // If we failed to create the field trial, crash with debug info. | |
| 936 // TODO(665129): Remove this when the crash is resolved. | |
| 937 if (!trial) { | |
| 938 constexpr size_t buf_size = 100; | |
| 939 char trial_name_c_str[buf_size] = {0}; | |
| 940 char group_name_c_str[buf_size] = {0}; | |
| 941 char existing_group_name_c_str[buf_size] = {0}; | |
| 942 | |
| 943 // Copy the names over to the stack. | |
| 944 std::string trial_name_string = trial_name.as_string(); | |
| 945 strlcpy(trial_name_c_str, trial_name_string.c_str(), buf_size); | |
| 946 std::string group_name_string = group_name.as_string(); | |
| 947 strlcpy(group_name_c_str, group_name_string.c_str(), buf_size); | |
| 948 | |
| 949 // Alias the trial and group name. | |
| 950 debug::Alias(trial_name_c_str); | |
| 951 debug::Alias(group_name_c_str); | |
| 952 | |
| 953 // Copy and alias the existing field trial name, if there is one. | |
| 954 FieldTrial* existing_field_trial = | |
| 955 FieldTrialList::Find(trial_name_string); | |
| 956 if (existing_field_trial) { | |
| 957 std::string existing_group_name_string = | |
| 958 existing_field_trial->group_name_internal(); | |
| 959 strlcpy(existing_group_name_c_str, group_name_string.c_str(), buf_size); | |
| 960 debug::Alias(existing_group_name_c_str); | |
| 961 } | |
| 962 CHECK(!trial_name_string.empty()); | |
| 963 CHECK(!group_name_string.empty()); | |
| 964 CHECK_EQ(existing_field_trial->group_name_internal(), | |
| 965 group_name.as_string()); | |
| 966 return false; | |
| 967 } | |
| 968 | |
| 969 trial->ref_ = ref; | 892 trial->ref_ = ref; |
| 970 if (entry->activated) { | 893 if (entry->activated) { |
| 971 // Call |group()| to mark the trial as "used" and notify observers, if | 894 // Call |group()| to mark the trial as "used" and notify observers, if |
| 972 // any. This is useful to ensure that field trials created in child | 895 // any. This is useful to ensure that field trials created in child |
| 973 // processes are properly reported in crash reports. | 896 // processes are properly reported in crash reports. |
| 974 trial->group(); | 897 trial->group(); |
| 975 } | 898 } |
| 976 } | 899 } |
| 977 return true; | 900 return true; |
| 978 } | 901 } |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1015 | 938 |
| 1016 // Don't do anything if the allocator hasn't been instantiated yet. | 939 // Don't do anything if the allocator hasn't been instantiated yet. |
| 1017 if (allocator == nullptr) | 940 if (allocator == nullptr) |
| 1018 return; | 941 return; |
| 1019 | 942 |
| 1020 // Or if the allocator is read only, which means we are in a child process and | 943 // Or if the allocator is read only, which means we are in a child process and |
| 1021 // shouldn't be writing to it. | 944 // shouldn't be writing to it. |
| 1022 if (allocator->IsReadonly()) | 945 if (allocator->IsReadonly()) |
| 1023 return; | 946 return; |
| 1024 | 947 |
| 1025 // TODO(665129): Remove this code once the bug has been fixed. | |
| 1026 CheckAllocatorForTrial(allocator, field_trial->trial_name(), | |
| 1027 field_trial->group_name_internal()); | |
| 1028 | |
| 1029 FieldTrial::State trial_state; | 948 FieldTrial::State trial_state; |
| 1030 if (!field_trial->GetStateWhileLocked(&trial_state)) | 949 if (!field_trial->GetStateWhileLocked(&trial_state)) |
| 1031 return; | 950 return; |
| 1032 | 951 |
| 1033 // Or if we've already added it. We must check after GetState since it can | 952 // Or if we've already added it. We must check after GetState since it can |
| 1034 // also add to the allocator. | 953 // also add to the allocator. |
| 1035 if (field_trial->ref_) | 954 if (field_trial->ref_) |
| 1036 return; | 955 return; |
| 1037 | 956 |
| 1038 Pickle pickle; | 957 Pickle pickle; |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1108 return; | 1027 return; |
| 1109 } | 1028 } |
| 1110 AutoLock auto_lock(global_->lock_); | 1029 AutoLock auto_lock(global_->lock_); |
| 1111 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1030 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 1112 trial->AddRef(); | 1031 trial->AddRef(); |
| 1113 trial->SetTrialRegistered(); | 1032 trial->SetTrialRegistered(); |
| 1114 global_->registered_[trial->trial_name()] = trial; | 1033 global_->registered_[trial->trial_name()] = trial; |
| 1115 } | 1034 } |
| 1116 | 1035 |
| 1117 } // namespace base | 1036 } // namespace base |
| OLD | NEW |