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 const std::string& trial_name, | |
191 const 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)) { | |
203 CHECK(false); | |
204 } | |
205 | |
206 std::string existing_trial_name = trial_name_to_check.as_string(); | |
brucedawson
2016/11/21 22:07:00
Why copy these to std::string objects? This requir
lawrencewu
2016/11/22 15:17:54
I think it's more readable this way, but yeah, sin
brucedawson
2016/11/22 17:55:24
Thanks. Given the separate thread going on about t
| |
207 std::string existing_group_name = group_name_to_check.as_string(); | |
208 if (existing_trial_name == trial_name) { | |
209 constexpr size_t buf_size = 100; | |
210 | |
211 char trial_name_c_str[buf_size]; | |
212 char group_name_c_str[buf_size]; | |
213 char existing_group_name_c_str[buf_size]; | |
214 | |
215 strlcpy(trial_name_c_str, trial_name.c_str(), buf_size); | |
216 strlcpy(group_name_c_str, group_name.c_str(), buf_size); | |
217 strlcpy(existing_group_name_c_str, existing_group_name.c_str(), buf_size); | |
218 | |
219 debug::Alias(trial_name_c_str); | |
220 debug::Alias(existing_group_name_c_str); | |
221 debug::Alias(group_name_c_str); | |
brucedawson
2016/11/21 22:07:00
For consistency and to minimize confusion keep the
lawrencewu
2016/11/22 15:17:54
Done.
| |
222 CHECK_EQ(existing_group_name, group_name); | |
223 } | |
224 } | |
225 } | |
226 | |
184 #if defined(OS_WIN) | 227 #if defined(OS_WIN) |
185 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | 228 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
186 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 229 HANDLE src = allocator->shared_memory()->handle().GetHandle(); |
187 ProcessHandle process = GetCurrentProcess(); | 230 ProcessHandle process = GetCurrentProcess(); |
188 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 231 DWORD access = SECTION_MAP_READ | SECTION_QUERY; |
189 HANDLE dst; | 232 HANDLE dst; |
190 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 233 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) |
191 return nullptr; | 234 return nullptr; |
192 return dst; | 235 return dst; |
193 } | 236 } |
(...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
850 return false; | 893 return false; |
851 | 894 |
852 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | 895 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take |
853 // StringPieces. | 896 // StringPieces. |
854 FieldTrial* trial = | 897 FieldTrial* trial = |
855 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); | 898 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); |
856 | 899 |
857 // If we failed to create the field trial, crash with debug info. | 900 // If we failed to create the field trial, crash with debug info. |
858 // TODO(665129): Remove this when the crash is resolved. | 901 // TODO(665129): Remove this when the crash is resolved. |
859 if (!trial) { | 902 if (!trial) { |
903 constexpr size_t buf_size = 100; | |
904 char trial_name_c_str[buf_size] = {0}; | |
905 char group_name_c_str[buf_size] = {0}; | |
906 char existing_group_name_c_str[buf_size] = {0}; | |
907 | |
908 // Copy the names over to the stack. | |
860 std::string trial_name_string = trial_name.as_string(); | 909 std::string trial_name_string = trial_name.as_string(); |
910 strlcpy(trial_name_c_str, trial_name_string.c_str(), buf_size); | |
861 std::string group_name_string = group_name.as_string(); | 911 std::string group_name_string = group_name.as_string(); |
912 strlcpy(group_name_c_str, group_name_string.c_str(), buf_size); | |
913 | |
914 // Alias the trial and group name. | |
915 debug::Alias(trial_name_c_str); | |
916 debug::Alias(group_name_c_str); | |
917 | |
918 // Copy and alias the existing field trial name, if there is one. | |
862 FieldTrial* existing_field_trial = | 919 FieldTrial* existing_field_trial = |
863 FieldTrialList::Find(trial_name_string); | 920 FieldTrialList::Find(trial_name_string); |
864 if (existing_field_trial) | 921 if (existing_field_trial) { |
865 debug::Alias(existing_field_trial->group_name_internal().c_str()); | 922 std::string existing_group_name_string = |
866 debug::Alias(trial_name_string.c_str()); | 923 existing_field_trial->group_name_internal(); |
867 debug::Alias(group_name_string.c_str()); | 924 strlcpy(existing_group_name_c_str, group_name_string.c_str(), buf_size); |
925 debug::Alias(existing_group_name_c_str); | |
926 } | |
868 CHECK(!trial_name_string.empty()); | 927 CHECK(!trial_name_string.empty()); |
869 CHECK(!group_name_string.empty()); | 928 CHECK(!group_name_string.empty()); |
870 CHECK_EQ(existing_field_trial->group_name_internal(), | 929 CHECK_EQ(existing_field_trial->group_name_internal(), |
871 group_name.as_string()); | 930 group_name.as_string()); |
872 return false; | 931 return false; |
873 } | 932 } |
874 | 933 |
875 trial->ref_ = ref; | 934 trial->ref_ = ref; |
876 if (entry->activated) { | 935 if (entry->activated) { |
877 // Call |group()| to mark the trial as "used" and notify observers, if | 936 // Call |group()| to mark the trial as "used" and notify observers, if |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
921 | 980 |
922 // Don't do anything if the allocator hasn't been instantiated yet. | 981 // Don't do anything if the allocator hasn't been instantiated yet. |
923 if (allocator == nullptr) | 982 if (allocator == nullptr) |
924 return; | 983 return; |
925 | 984 |
926 // Or if the allocator is read only, which means we are in a child process and | 985 // Or if the allocator is read only, which means we are in a child process and |
927 // shouldn't be writing to it. | 986 // shouldn't be writing to it. |
928 if (allocator->IsReadonly()) | 987 if (allocator->IsReadonly()) |
929 return; | 988 return; |
930 | 989 |
990 // TODO(665129): Remove this code once the bug has been fixed. | |
991 CheckAllocatorForTrial(allocator, field_trial->trial_name(), | |
992 field_trial->group_name_internal()); | |
993 | |
931 FieldTrial::State trial_state; | 994 FieldTrial::State trial_state; |
932 if (!field_trial->GetStateWhileLocked(&trial_state)) | 995 if (!field_trial->GetStateWhileLocked(&trial_state)) |
933 return; | 996 return; |
934 | 997 |
935 // Or if we've already added it. We must check after GetState since it can | 998 // Or if we've already added it. We must check after GetState since it can |
936 // also add to the allocator. | 999 // also add to the allocator. |
937 if (field_trial->ref_) | 1000 if (field_trial->ref_) |
938 return; | 1001 return; |
939 | 1002 |
940 Pickle pickle; | 1003 Pickle pickle; |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1010 return; | 1073 return; |
1011 } | 1074 } |
1012 AutoLock auto_lock(global_->lock_); | 1075 AutoLock auto_lock(global_->lock_); |
1013 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1076 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
1014 trial->AddRef(); | 1077 trial->AddRef(); |
1015 trial->SetTrialRegistered(); | 1078 trial->SetTrialRegistered(); |
1016 global_->registered_[trial->trial_name()] = trial; | 1079 global_->registered_[trial->trial_name()] = trial; |
1017 } | 1080 } |
1018 | 1081 |
1019 } // namespace base | 1082 } // namespace base |
OLD | NEW |