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 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 174 | 174 |
| 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 // In order to debug the crash where two field trials with the same name but | |
| 185 // different groups get added to the allocator, let's try to get the stack | |
| 186 // frame where the second duplicate gets added here as well as the trial/group | |
| 187 // names by going through the allocator and checking if a trial with the same | |
| 188 // name already exists. | |
| 189 void CheckAllocatorForTrial(FieldTrialList::FieldTrialAllocator* allocator, | |
| 190 std::string trial_name, | |
|
Alexei Svitkine (slow)
2016/11/21 20:11:33
Nit: const&
Same for the next param.
lawrencewu
2016/11/21 20:41:23
Done.
| |
| 191 std::string group_name) { | |
| 192 FieldTrialList::FieldTrialAllocator::Iterator iter(allocator); | |
| 193 FieldTrial::FieldTrialRef ref; | |
| 194 while ((ref = iter.GetNextOfType(kFieldTrialType)) != | |
| 195 FieldTrialList::FieldTrialAllocator::kReferenceNull) { | |
| 196 const FieldTrialEntry* entry = | |
| 197 allocator->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | |
| 198 | |
| 199 StringPiece trial_name_to_check; | |
| 200 StringPiece group_name_to_check; | |
| 201 if (!entry->GetTrialAndGroupName(&trial_name_to_check, | |
| 202 &group_name_to_check)) | |
|
Alexei Svitkine (slow)
2016/11/21 20:11:33
Nit: {}'s
lawrencewu
2016/11/21 20:41:23
Done.
| |
| 203 CHECK(false); | |
| 204 | |
| 205 std::string existing_trial_name = trial_name_to_check.as_string(); | |
| 206 std::string existing_group_name = group_name_to_check.as_string(); | |
| 207 if (existing_trial_name == trial_name) { | |
| 208 constexpr size_t buf_size = 100; | |
| 209 | |
| 210 char trial_name_c_str[buf_size]; | |
| 211 char group_name_c_str[buf_size]; | |
| 212 char existing_group_name_c_str[buf_size]; | |
| 213 | |
| 214 strlcpy(trial_name_c_str, trial_name.c_str(), buf_size); | |
| 215 strlcpy(group_name_c_str, group_name.c_str(), buf_size); | |
| 216 strlcpy(existing_group_name_c_str, existing_group_name.c_str(), buf_size); | |
| 217 | |
| 218 debug::Alias(trial_name_c_str); | |
| 219 debug::Alias(existing_group_name_c_str); | |
| 220 debug::Alias(group_name_c_str); | |
| 221 CHECK_EQ(existing_group_name, group_name); | |
| 222 } | |
| 223 } | |
| 224 } | |
| 225 | |
| 184 #if defined(OS_WIN) | 226 #if defined(OS_WIN) |
| 185 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | 227 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
| 186 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 228 HANDLE src = allocator->shared_memory()->handle().GetHandle(); |
| 187 ProcessHandle process = GetCurrentProcess(); | 229 ProcessHandle process = GetCurrentProcess(); |
| 188 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 230 DWORD access = SECTION_MAP_READ | SECTION_QUERY; |
| 189 HANDLE dst; | 231 HANDLE dst; |
| 190 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 232 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) |
| 191 return nullptr; | 233 return nullptr; |
| 192 return dst; | 234 return dst; |
| 193 } | 235 } |
| (...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 850 return false; | 892 return false; |
| 851 | 893 |
| 852 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | 894 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take |
| 853 // StringPieces. | 895 // StringPieces. |
| 854 FieldTrial* trial = | 896 FieldTrial* trial = |
| 855 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); | 897 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); |
| 856 | 898 |
| 857 // If we failed to create the field trial, crash with debug info. | 899 // If we failed to create the field trial, crash with debug info. |
| 858 // TODO(665129): Remove this when the crash is resolved. | 900 // TODO(665129): Remove this when the crash is resolved. |
| 859 if (!trial) { | 901 if (!trial) { |
| 902 constexpr size_t buf_size = 100; | |
| 903 char trial_name_c_str[buf_size] = {0}; | |
| 904 char group_name_c_str[buf_size] = {0}; | |
| 905 char existing_group_name_c_str[buf_size] = {0}; | |
| 906 | |
| 907 // Copy the names over to the stack. | |
| 860 std::string trial_name_string = trial_name.as_string(); | 908 std::string trial_name_string = trial_name.as_string(); |
| 909 strlcpy(trial_name_c_str, trial_name_string.c_str(), buf_size); | |
| 861 std::string group_name_string = group_name.as_string(); | 910 std::string group_name_string = group_name.as_string(); |
| 911 strlcpy(group_name_c_str, group_name_string.c_str(), buf_size); | |
| 912 | |
| 913 // Alias the trial and group name. | |
| 914 debug::Alias(trial_name_c_str); | |
| 915 debug::Alias(group_name_c_str); | |
| 916 | |
| 917 // Copy and alias the existing field trial name, if there is one. | |
| 862 FieldTrial* existing_field_trial = | 918 FieldTrial* existing_field_trial = |
| 863 FieldTrialList::Find(trial_name_string); | 919 FieldTrialList::Find(trial_name_string); |
| 864 if (existing_field_trial) | 920 if (existing_field_trial) { |
| 865 debug::Alias(existing_field_trial->group_name_internal().c_str()); | 921 std::string existing_group_name_string = |
| 866 debug::Alias(trial_name_string.c_str()); | 922 existing_field_trial->group_name_internal(); |
| 867 debug::Alias(group_name_string.c_str()); | 923 strlcpy(existing_group_name_c_str, group_name_string.c_str(), buf_size); |
| 924 debug::Alias(existing_group_name_c_str); | |
| 925 } | |
| 868 CHECK(!trial_name_string.empty()); | 926 CHECK(!trial_name_string.empty()); |
| 869 CHECK(!group_name_string.empty()); | 927 CHECK(!group_name_string.empty()); |
| 870 CHECK_EQ(existing_field_trial->group_name_internal(), | 928 CHECK_EQ(existing_field_trial->group_name_internal(), |
| 871 group_name.as_string()); | 929 group_name.as_string()); |
| 872 return false; | 930 return false; |
| 873 } | 931 } |
| 874 | 932 |
| 875 trial->ref_ = ref; | 933 trial->ref_ = ref; |
| 876 if (entry->activated) { | 934 if (entry->activated) { |
| 877 // Call |group()| to mark the trial as "used" and notify observers, if | 935 // Call |group()| to mark the trial as "used" and notify observers, if |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 912 global_->readonly_allocator_handle_ = | 970 global_->readonly_allocator_handle_ = |
| 913 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 971 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
| 914 #endif | 972 #endif |
| 915 } | 973 } |
| 916 #endif | 974 #endif |
| 917 | 975 |
| 918 // static | 976 // static |
| 919 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { | 977 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
| 920 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); | 978 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); |
| 921 | 979 |
| 980 // TODO(665129): Remove this code once the bug has been fixed. | |
| 981 CheckAllocatorForTrial(allocator, field_trial->trial_name(), | |
| 982 field_trial->group_name_internal()); | |
| 983 | |
| 922 // Don't do anything if the allocator hasn't been instantiated yet. | 984 // Don't do anything if the allocator hasn't been instantiated yet. |
| 923 if (allocator == nullptr) | 985 if (allocator == nullptr) |
| 924 return; | 986 return; |
| 925 | 987 |
| 926 // Or if the allocator is read only, which means we are in a child process and | 988 // Or if the allocator is read only, which means we are in a child process and |
| 927 // shouldn't be writing to it. | 989 // shouldn't be writing to it. |
| 928 if (allocator->IsReadonly()) | 990 if (allocator->IsReadonly()) |
| 929 return; | 991 return; |
| 930 | 992 |
| 931 FieldTrial::State trial_state; | 993 FieldTrial::State trial_state; |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1010 return; | 1072 return; |
| 1011 } | 1073 } |
| 1012 AutoLock auto_lock(global_->lock_); | 1074 AutoLock auto_lock(global_->lock_); |
| 1013 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1075 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 1014 trial->AddRef(); | 1076 trial->AddRef(); |
| 1015 trial->SetTrialRegistered(); | 1077 trial->SetTrialRegistered(); |
| 1016 global_->registered_[trial->trial_name()] = trial; | 1078 global_->registered_[trial->trial_name()] = trial; |
| 1017 } | 1079 } |
| 1018 | 1080 |
| 1019 } // namespace base | 1081 } // namespace base |
| OLD | NEW |