Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(694)

Side by Side Diff: base/metrics/field_trial.cc

Issue 2463223002: Store field trial parameters in shared memory (Closed)
Patch Set: fix failing tests due to lock Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/metrics/field_trial_param_associator.h"
16 #include "base/pickle.h" 17 #include "base/pickle.h"
17 #include "base/process/memory.h" 18 #include "base/process/memory.h"
18 #include "base/rand_util.h" 19 #include "base/rand_util.h"
19 #include "base/strings/string_number_conversions.h" 20 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h" 21 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h" 22 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h" 23 #include "base/strings/utf_string_conversions.h"
23 24
24 namespace base { 25 namespace base {
25 26
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 // Expected size for 32/64-bit check. 65 // Expected size for 32/64-bit check.
65 static constexpr size_t kExpectedInstanceSize = 8; 66 static constexpr size_t kExpectedInstanceSize = 8;
66 67
67 // Whether or not this field trial is activated. This is really just a boolean 68 // Whether or not this field trial is activated. This is really just a boolean
68 // but marked as a uint32_t for portability reasons. 69 // but marked as a uint32_t for portability reasons.
69 uint32_t activated; 70 uint32_t activated;
70 71
71 // Size of the pickled structure, NOT the total size of this entry. 72 // Size of the pickled structure, NOT the total size of this entry.
72 uint32_t size; 73 uint32_t size;
73 74
75 // Returns an iterator over the data containing names and params.
76 PickleIterator GetPickleIterator() const {
77 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) +
78 sizeof(FieldTrialEntry);
79
80 Pickle pickle(src, size);
81 return PickleIterator(pickle);
82 }
83
84 // Takes the iterator and writes out the first two items into |trial_name| and
85 // |group_name|.
86 bool ReadStringPair(PickleIterator* iter,
87 StringPiece* trial_name,
88 StringPiece* group_name) const {
89 if (!iter->ReadStringPiece(trial_name))
90 return false;
91 if (!iter->ReadStringPiece(group_name))
92 return false;
93 return true;
94 }
95
74 // Calling this is only valid when the entry is initialized. That is, it 96 // Calling this is only valid when the entry is initialized. That is, it
75 // resides in shared memory and has a pickle containing the trial name and 97 // resides in shared memory and has a pickle containing the trial name and
76 // group name following it. 98 // group name following it.
77 bool GetTrialAndGroupName(StringPiece* trial_name, 99 bool GetTrialAndGroupName(StringPiece* trial_name,
78 StringPiece* group_name) const { 100 StringPiece* group_name) const {
79 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) + 101 PickleIterator iter = GetPickleIterator();
80 sizeof(FieldTrialEntry); 102 return ReadStringPair(&iter, trial_name, group_name);
103 }
81 104
82 Pickle pickle(src, size); 105 // Calling this is only valid when the entry is initialized as well. Reads the
83 PickleIterator pickle_iter(pickle); 106 // parameters following the trial and group name and stores them as key-value
107 // mappings in |params|.
108 bool GetParams(std::map<std::string, std::string>* params) const {
109 PickleIterator iter = GetPickleIterator();
110 StringPiece tmp;
111 if (!ReadStringPair(&iter, &tmp, &tmp))
112 return false;
84 113
85 if (!pickle_iter.ReadStringPiece(trial_name)) 114 while (true) {
86 return false; 115 StringPiece key;
87 if (!pickle_iter.ReadStringPiece(group_name)) 116 StringPiece value;
88 return false; 117 if (!iter.ReadStringPiece(&key))
89 return true; 118 return true; // No more params to read.
119 if (!iter.ReadStringPiece(&value))
120 return false;
121 (*params)[key.as_string()] = value.as_string();
122 }
90 } 123 }
91 }; 124 };
92 125
126 // Writes out string1 and then string2 to pickle.
127 bool WriteStringPair(Pickle* pickle,
128 const StringPiece& string1,
129 const StringPiece& string2) {
130 if (!pickle->WriteString(string1))
131 return false;
132 if (!pickle->WriteString(string2))
133 return false;
134 return true;
135 }
136
137 // Writes out the field trial's contents (via trial_state) to the pickle. The
138 // format of the pickle looks like:
139 // TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ...
140 // If there are no parameters, then it just ends at GroupName.
141 bool PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) {
142 if (!WriteStringPair(pickle, trial_state.trial_name, trial_state.group_name))
143 return false;
144
145 // Get field trial params.
146 std::map<std::string, std::string> params;
147 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
148 trial_state.trial_name.as_string(), trial_state.group_name.as_string(),
149 &params);
150
151 // Write params to pickle.
152 for (const auto& param : params) {
153 if (!WriteStringPair(pickle, StringPiece(param.first),
bcwhite 2016/11/24 18:29:47 Do you have to manually create the StringPiece obj
lawrencewu 2016/11/24 21:50:35 Removed.
154 StringPiece(param.second)))
Alexei Svitkine (slow) 2016/11/24 17:56:32 Nit: {}'s
lawrencewu 2016/11/24 21:50:35 Don't need anymore as removing the StringPiece cto
155 return false;
156 }
157 return true;
158 }
159
93 // Created a time value based on |year|, |month| and |day_of_month| parameters. 160 // Created a time value based on |year|, |month| and |day_of_month| parameters.
94 Time CreateTimeFromParams(int year, int month, int day_of_month) { 161 Time CreateTimeFromParams(int year, int month, int day_of_month) {
95 DCHECK_GT(year, 1970); 162 DCHECK_GT(year, 1970);
96 DCHECK_GT(month, 0); 163 DCHECK_GT(month, 0);
97 DCHECK_LT(month, 13); 164 DCHECK_LT(month, 13);
98 DCHECK_GT(day_of_month, 0); 165 DCHECK_GT(day_of_month, 0);
99 DCHECK_LT(day_of_month, 32); 166 DCHECK_LT(day_of_month, 32);
100 167
101 Time::Exploded exploded; 168 Time::Exploded exploded;
102 exploded.year = year; 169 exploded.year = year;
(...skipping 783 matching lines...) Expand 10 before | Expand all | Expand 10 after
886 } 953 }
887 954
888 // static 955 // static
889 size_t FieldTrialList::GetFieldTrialCount() { 956 size_t FieldTrialList::GetFieldTrialCount() {
890 if (!global_) 957 if (!global_)
891 return 0; 958 return 0;
892 AutoLock auto_lock(global_->lock_); 959 AutoLock auto_lock(global_->lock_);
893 return global_->registered_.size(); 960 return global_->registered_.size();
894 } 961 }
895 962
963 // static
964 bool FieldTrialList::GetParamsFromSharedMemory(
965 FieldTrial* field_trial,
966 std::map<std::string, std::string>* params) {
967 DCHECK(global_);
968 // If the field trial allocator is not set up yet, then there are several
969 // cases:
970 // - We are in the browser process and the allocator has not been set up
971 // yet. If we got here, then we couldn't find the params in
972 // FieldTrialParamAssociator, so it's definitely not here. Return false.
973 // - Using shared memory for field trials is not enabled. If we got here,
974 // then there's nothing in shared memory. Return false.
975 // - We are in the child process and the allocator has not been set up yet.
976 // If this is the case, then you are calling this too early. The field trial
977 // allocator should get set up very early in the lifecycle. Try to see if
978 // you can call it after it's been set up.
979 if (!global_->field_trial_allocator_)
980 return false;
981
982 // If ref_ isn't set, then the field trial data can't be in shared memory.
983 if (!field_trial->ref_)
984 return false;
985
986 const FieldTrialEntry* entry =
987 global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>(
988 field_trial->ref_, kFieldTrialType);
989 CHECK_GE(global_->field_trial_allocator_->GetAllocSize(field_trial->ref_),
bcwhite 2016/11/24 18:29:47 You should just "return false" like every other fa
lawrencewu 2016/11/24 21:50:35 Done.
990 sizeof(FieldTrialEntry) + entry->size);
991 return entry->GetParams(params);
992 }
993
994 // static
995 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
996 if (!global_ || !global_->field_trial_allocator_)
997 return;
998 AutoLock auto_lock(global_->lock_);
999
1000 // To clear the params, we iterate through every item in the allocator, copy
1001 // just the trial and group name into a newly-allocated segment and then clear
1002 // the existing item.
1003 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1004 FieldTrialAllocator::Iterator mem_iter(allocator);
1005
1006 FieldTrial::FieldTrialRef prev_ref;
1007 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) !=
1008 FieldTrialAllocator::kReferenceNull) {
1009 // Get the existing field trial entry in shared memory.
1010 const FieldTrialEntry* prev_entry =
1011 allocator->GetAsObject<const FieldTrialEntry>(prev_ref,
1012 kFieldTrialType);
1013 StringPiece trial_name;
1014 StringPiece group_name;
1015 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
1016 continue;
1017
1018 // Write a new entry, minus the params.
1019 Pickle pickle;
1020 pickle.WriteString(trial_name);
1021 pickle.WriteString(group_name);
1022 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1023 FieldTrial::FieldTrialRef new_ref =
1024 allocator->Allocate(total_size, kFieldTrialType);
1025 FieldTrialEntry* new_entry =
1026 allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType);
1027 new_entry->activated = prev_entry->activated;
1028 new_entry->size = pickle.size();
1029
1030 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
1031 // in memory, so we can avoid this memcpy.
1032 char* dst = reinterpret_cast<char*>(new_entry) + sizeof(FieldTrialEntry);
1033 memcpy(dst, pickle.data(), pickle.size());
1034
1035 // Update the ref on the field trial.
1036 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
1037 trial->ref_ = new_ref;
1038
1039 // Mark the existing entry as unused.
1040 allocator->ChangeType(prev_ref, 0, kFieldTrialType);
1041 }
1042 }
1043
896 #if defined(OS_WIN) 1044 #if defined(OS_WIN)
897 // static 1045 // static
898 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) { 1046 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) {
899 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); 1047 SharedMemoryHandle shm_handle(handle, GetCurrentProcId());
900 1048
901 // shm gets deleted when it gets out of scope, but that's OK because we need 1049 // shm gets deleted when it gets out of scope, but that's OK because we need
902 // it only for the duration of this method. 1050 // it only for the duration of this method.
903 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); 1051 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
904 if (!shm.get()->Map(kFieldTrialAllocationSize)) 1052 if (!shm.get()->Map(kFieldTrialAllocationSize))
905 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); 1053 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
1029 FieldTrial::State trial_state; 1177 FieldTrial::State trial_state;
1030 if (!field_trial->GetStateWhileLocked(&trial_state)) 1178 if (!field_trial->GetStateWhileLocked(&trial_state))
1031 return; 1179 return;
1032 1180
1033 // Or if we've already added it. We must check after GetState since it can 1181 // Or if we've already added it. We must check after GetState since it can
1034 // also add to the allocator. 1182 // also add to the allocator.
1035 if (field_trial->ref_) 1183 if (field_trial->ref_)
1036 return; 1184 return;
1037 1185
1038 Pickle pickle; 1186 Pickle pickle;
1039 pickle.WriteString(trial_state.trial_name); 1187 if (!PickleFieldTrial(trial_state, &pickle))
1040 pickle.WriteString(trial_state.group_name); 1188 NOTREACHED();
bcwhite 2016/11/24 18:29:47 This will do nothing in production code meaning th
lawrencewu 2016/11/24 21:50:35 Done.
1041 1189
1042 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); 1190 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1043 FieldTrial::FieldTrialRef ref = 1191 FieldTrial::FieldTrialRef ref =
1044 allocator->Allocate(total_size, kFieldTrialType); 1192 allocator->Allocate(total_size, kFieldTrialType);
1045 if (ref == FieldTrialAllocator::kReferenceNull) 1193 if (ref == FieldTrialAllocator::kReferenceNull)
1046 return; 1194 NOTREACHED();
1047 1195
1048 FieldTrialEntry* entry = 1196 FieldTrialEntry* entry =
1049 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); 1197 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType);
1050 entry->activated = trial_state.activated; 1198 entry->activated = trial_state.activated;
1051 entry->size = pickle.size(); 1199 entry->size = pickle.size();
1052 1200
1053 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in 1201 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
1054 // memory, so we can avoid this memcpy. 1202 // memory, so we can avoid this memcpy.
1055 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); 1203 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry);
1056 memcpy(dst, pickle.data(), pickle.size()); 1204 memcpy(dst, pickle.data(), pickle.size());
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1108 return; 1256 return;
1109 } 1257 }
1110 AutoLock auto_lock(global_->lock_); 1258 AutoLock auto_lock(global_->lock_);
1111 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1259 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1112 trial->AddRef(); 1260 trial->AddRef();
1113 trial->SetTrialRegistered(); 1261 trial->SetTrialRegistered();
1114 global_->registered_[trial->trial_name()] = trial; 1262 global_->registered_[trial->trial_name()] = trial;
1115 } 1263 }
1116 1264
1117 } // namespace base 1265 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698