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

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

Issue 2463223002: Store field trial parameters in shared memory (Closed)
Patch Set: check that cache has been cleared in test 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
« no previous file with comments | « base/metrics/field_trial.h ('k') | base/metrics/field_trial_param_associator.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/feature_list.h" 13 #include "base/feature_list.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/metrics/field_trial_param_associator.h"
15 #include "base/pickle.h" 16 #include "base/pickle.h"
16 #include "base/process/memory.h" 17 #include "base/process/memory.h"
17 #include "base/rand_util.h" 18 #include "base/rand_util.h"
18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h" 20 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h" 21 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h" 22 #include "base/strings/utf_string_conversions.h"
22 23
23 // On systems that use the zygote process to spawn child processes, we must 24 // On systems that use the zygote process to spawn child processes, we must
24 // retrieve the correct fd using the mapping in GlobalDescriptors. 25 // retrieve the correct fd using the mapping in GlobalDescriptors.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 // Expected size for 32/64-bit check. 74 // Expected size for 32/64-bit check.
74 static constexpr size_t kExpectedInstanceSize = 8; 75 static constexpr size_t kExpectedInstanceSize = 8;
75 76
76 // Whether or not this field trial is activated. This is really just a boolean 77 // Whether or not this field trial is activated. This is really just a boolean
77 // but marked as a uint32_t for portability reasons. 78 // but marked as a uint32_t for portability reasons.
78 uint32_t activated; 79 uint32_t activated;
79 80
80 // Size of the pickled structure, NOT the total size of this entry. 81 // Size of the pickled structure, NOT the total size of this entry.
81 uint32_t size; 82 uint32_t size;
82 83
84 // Returns an iterator over the data containing names and params.
85 PickleIterator GetPickleIterator() const {
86 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) +
87 sizeof(FieldTrialEntry);
88
89 Pickle pickle(src, size);
90 return PickleIterator(pickle);
91 }
92
93 // Takes the iterator and writes out the first two items into |trial_name| and
94 // |group_name|.
95 bool ReadStringPair(PickleIterator* iter,
96 StringPiece* trial_name,
97 StringPiece* group_name) const {
98 if (!iter->ReadStringPiece(trial_name))
99 return false;
100 if (!iter->ReadStringPiece(group_name))
101 return false;
102 return true;
103 }
104
83 // Calling this is only valid when the entry is initialized. That is, it 105 // Calling this is only valid when the entry is initialized. That is, it
84 // resides in shared memory and has a pickle containing the trial name and 106 // resides in shared memory and has a pickle containing the trial name and
85 // group name following it. 107 // group name following it.
86 bool GetTrialAndGroupName(StringPiece* trial_name, 108 bool GetTrialAndGroupName(StringPiece* trial_name,
87 StringPiece* group_name) const { 109 StringPiece* group_name) const {
88 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) + 110 PickleIterator iter = GetPickleIterator();
89 sizeof(FieldTrialEntry); 111 return ReadStringPair(&iter, trial_name, group_name);
112 }
90 113
91 Pickle pickle(src, size); 114 // Calling this is only valid when the entry is initialized as well. Reads the
92 PickleIterator pickle_iter(pickle); 115 // parameters following the trial and group name and stores them as key-value
116 // mappings in |params|.
117 bool GetParams(std::map<std::string, std::string>* params) const {
118 PickleIterator iter = GetPickleIterator();
119 StringPiece tmp;
120 if (!ReadStringPair(&iter, &tmp, &tmp))
121 return false;
93 122
94 if (!pickle_iter.ReadStringPiece(trial_name)) 123 while (true) {
95 return false; 124 StringPiece key;
96 if (!pickle_iter.ReadStringPiece(group_name)) 125 StringPiece value;
97 return false; 126 if (!ReadStringPair(&iter, &key, &value))
98 return true; 127 return key.empty(); // Non-empty is bad: got one of a pair.
128 (*params)[key.as_string()] = value.as_string();
129 }
99 } 130 }
100 }; 131 };
101 132
133 // Writes out string1 and then string2 to pickle.
134 bool WriteStringPair(Pickle* pickle,
135 const StringPiece& string1,
136 const StringPiece& string2) {
137 if (!pickle->WriteString(string1))
138 return false;
139 if (!pickle->WriteString(string2))
140 return false;
141 return true;
142 }
143
144 // Writes out the field trial's contents (via trial_state) to the pickle. The
145 // format of the pickle looks like:
146 // TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ...
147 // If there are no parameters, then it just ends at GroupName.
148 bool PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) {
149 if (!WriteStringPair(pickle, trial_state.trial_name, trial_state.group_name))
150 return false;
151
152 // Get field trial params.
153 std::map<std::string, std::string> params;
154 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
155 trial_state.trial_name.as_string(), trial_state.group_name.as_string(),
156 &params);
157
158 // Write params to pickle.
159 for (const auto& param : params) {
160 if (!WriteStringPair(pickle, param.first, param.second))
161 return false;
162 }
163 return true;
164 }
165
102 // Created a time value based on |year|, |month| and |day_of_month| parameters. 166 // Created a time value based on |year|, |month| and |day_of_month| parameters.
103 Time CreateTimeFromParams(int year, int month, int day_of_month) { 167 Time CreateTimeFromParams(int year, int month, int day_of_month) {
104 DCHECK_GT(year, 1970); 168 DCHECK_GT(year, 1970);
105 DCHECK_GT(month, 0); 169 DCHECK_GT(month, 0);
106 DCHECK_LT(month, 13); 170 DCHECK_LT(month, 13);
107 DCHECK_GT(day_of_month, 0); 171 DCHECK_GT(day_of_month, 0);
108 DCHECK_LT(day_of_month, 32); 172 DCHECK_LT(day_of_month, 32);
109 173
110 Time::Exploded exploded; 174 Time::Exploded exploded;
111 exploded.year = year; 175 exploded.year = year;
(...skipping 798 matching lines...) Expand 10 before | Expand all | Expand 10 after
910 } 974 }
911 975
912 // static 976 // static
913 size_t FieldTrialList::GetFieldTrialCount() { 977 size_t FieldTrialList::GetFieldTrialCount() {
914 if (!global_) 978 if (!global_)
915 return 0; 979 return 0;
916 AutoLock auto_lock(global_->lock_); 980 AutoLock auto_lock(global_->lock_);
917 return global_->registered_.size(); 981 return global_->registered_.size();
918 } 982 }
919 983
984 // static
985 bool FieldTrialList::GetParamsFromSharedMemory(
986 FieldTrial* field_trial,
987 std::map<std::string, std::string>* params) {
988 DCHECK(global_);
989 // If the field trial allocator is not set up yet, then there are several
990 // cases:
991 // - We are in the browser process and the allocator has not been set up
992 // yet. If we got here, then we couldn't find the params in
993 // FieldTrialParamAssociator, so it's definitely not here. Return false.
994 // - Using shared memory for field trials is not enabled. If we got here,
995 // then there's nothing in shared memory. Return false.
996 // - We are in the child process and the allocator has not been set up yet.
997 // If this is the case, then you are calling this too early. The field trial
998 // allocator should get set up very early in the lifecycle. Try to see if
999 // you can call it after it's been set up.
1000 AutoLock auto_lock(global_->lock_);
1001 if (!global_->field_trial_allocator_)
1002 return false;
1003
1004 // If ref_ isn't set, then the field trial data can't be in shared memory.
1005 if (!field_trial->ref_)
1006 return false;
1007
1008 const FieldTrialEntry* entry =
1009 global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>(
1010 field_trial->ref_, kFieldTrialType);
1011
1012 size_t allocated_size =
1013 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
1014 size_t actual_size = sizeof(FieldTrialEntry) + entry->size;
1015 if (allocated_size < actual_size)
1016 return false;
1017
1018 return entry->GetParams(params);
1019 }
1020
1021 // static
1022 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
1023 if (!global_)
1024 return;
1025
1026 AutoLock auto_lock(global_->lock_);
1027 if (!global_->field_trial_allocator_)
1028 return;
1029
1030 // To clear the params, we iterate through every item in the allocator, copy
1031 // just the trial and group name into a newly-allocated segment and then clear
1032 // the existing item.
1033 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1034 FieldTrialAllocator::Iterator mem_iter(allocator);
1035
1036 // List of refs to eventually be made iterable. We can't make it in the loop,
1037 // since it would go on forever.
1038 std::vector<FieldTrial::FieldTrialRef> new_refs;
1039
1040 FieldTrial::FieldTrialRef prev_ref;
1041 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) !=
1042 FieldTrialAllocator::kReferenceNull) {
1043 // Get the existing field trial entry in shared memory.
1044 const FieldTrialEntry* prev_entry =
1045 allocator->GetAsObject<const FieldTrialEntry>(prev_ref,
1046 kFieldTrialType);
1047 StringPiece trial_name;
1048 StringPiece group_name;
1049 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
1050 continue;
1051
1052 // Write a new entry, minus the params.
1053 Pickle pickle;
1054 pickle.WriteString(trial_name);
1055 pickle.WriteString(group_name);
1056 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1057 FieldTrial::FieldTrialRef new_ref =
1058 allocator->Allocate(total_size, kFieldTrialType);
1059 FieldTrialEntry* new_entry =
1060 allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType);
1061 new_entry->activated = prev_entry->activated;
1062 new_entry->size = pickle.size();
1063
1064 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
1065 // in memory, so we can avoid this memcpy.
1066 char* dst = reinterpret_cast<char*>(new_entry) + sizeof(FieldTrialEntry);
1067 memcpy(dst, pickle.data(), pickle.size());
1068
1069 // Update the ref on the field trial and add it to the list to be made
1070 // iterable.
1071 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
1072 trial->ref_ = new_ref;
1073 new_refs.push_back(new_ref);
1074
1075 // Mark the existing entry as unused.
1076 allocator->ChangeType(prev_ref, 0, kFieldTrialType);
1077 }
1078
1079 for (const auto& ref : new_refs) {
1080 allocator->MakeIterable(ref);
1081 }
1082 }
1083
920 #if defined(OS_WIN) 1084 #if defined(OS_WIN)
921 // static 1085 // static
922 bool FieldTrialList::CreateTrialsFromHandleSwitch( 1086 bool FieldTrialList::CreateTrialsFromHandleSwitch(
923 const std::string& handle_switch) { 1087 const std::string& handle_switch) {
924 int field_trial_handle = std::stoi(handle_switch); 1088 int field_trial_handle = std::stoi(handle_switch);
925 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); 1089 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
926 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); 1090 SharedMemoryHandle shm_handle(handle, GetCurrentProcId());
927 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); 1091 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle);
928 } 1092 }
929 #endif 1093 #endif
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
1032 FieldTrial::State trial_state; 1196 FieldTrial::State trial_state;
1033 if (!field_trial->GetStateWhileLocked(&trial_state)) 1197 if (!field_trial->GetStateWhileLocked(&trial_state))
1034 return; 1198 return;
1035 1199
1036 // Or if we've already added it. We must check after GetState since it can 1200 // Or if we've already added it. We must check after GetState since it can
1037 // also add to the allocator. 1201 // also add to the allocator.
1038 if (field_trial->ref_) 1202 if (field_trial->ref_)
1039 return; 1203 return;
1040 1204
1041 Pickle pickle; 1205 Pickle pickle;
1042 pickle.WriteString(trial_state.trial_name); 1206 if (!PickleFieldTrial(trial_state, &pickle)) {
1043 pickle.WriteString(trial_state.group_name); 1207 NOTREACHED();
1208 return;
1209 }
1044 1210
1045 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); 1211 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1046 FieldTrial::FieldTrialRef ref = 1212 FieldTrial::FieldTrialRef ref =
1047 allocator->Allocate(total_size, kFieldTrialType); 1213 allocator->Allocate(total_size, kFieldTrialType);
1048 if (ref == FieldTrialAllocator::kReferenceNull) 1214 if (ref == FieldTrialAllocator::kReferenceNull) {
1215 NOTREACHED();
1049 return; 1216 return;
1217 }
1050 1218
1051 FieldTrialEntry* entry = 1219 FieldTrialEntry* entry =
1052 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); 1220 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType);
1053 entry->activated = trial_state.activated; 1221 entry->activated = trial_state.activated;
1054 entry->size = pickle.size(); 1222 entry->size = pickle.size();
1055 1223
1056 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in 1224 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
1057 // memory, so we can avoid this memcpy. 1225 // memory, so we can avoid this memcpy.
1058 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); 1226 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry);
1059 memcpy(dst, pickle.data(), pickle.size()); 1227 memcpy(dst, pickle.data(), pickle.size());
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1111 return; 1279 return;
1112 } 1280 }
1113 AutoLock auto_lock(global_->lock_); 1281 AutoLock auto_lock(global_->lock_);
1114 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1282 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1115 trial->AddRef(); 1283 trial->AddRef();
1116 trial->SetTrialRegistered(); 1284 trial->SetTrialRegistered();
1117 global_->registered_[trial->trial_name()] = trial; 1285 global_->registered_[trial->trial_name()] = trial;
1118 } 1286 }
1119 1287
1120 } // namespace base 1288 } // namespace base
OLDNEW
« no previous file with comments | « base/metrics/field_trial.h ('k') | base/metrics/field_trial_param_associator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698