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

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

Issue 2463223002: Store field trial parameters in shared memory (Closed)
Patch Set: address comments 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/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 (!ReadStringPair(&iter, &key, &value))
89 return true; 118 return key.empty(); // Non-empty is bad: got one of a pair.
119 (*params)[key.as_string()] = value.as_string();
120 }
90 } 121 }
91 }; 122 };
92 123
124 // Writes out string1 and then string2 to pickle.
125 bool WriteStringPair(Pickle* pickle,
126 const StringPiece& string1,
127 const StringPiece& string2) {
128 if (!pickle->WriteString(string1))
129 return false;
130 if (!pickle->WriteString(string2))
131 return false;
132 return true;
133 }
134
135 // Writes out the field trial's contents (via trial_state) to the pickle. The
136 // format of the pickle looks like:
137 // TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ...
138 // If there are no parameters, then it just ends at GroupName.
139 bool PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) {
140 if (!WriteStringPair(pickle, trial_state.trial_name, trial_state.group_name))
141 return false;
142
143 // Get field trial params.
144 std::map<std::string, std::string> params;
145 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
146 trial_state.trial_name.as_string(), trial_state.group_name.as_string(),
147 &params);
148
149 // Write params to pickle.
150 for (const auto& param : params) {
151 if (!WriteStringPair(pickle, param.first, param.second))
152 return false;
153 }
154 return true;
155 }
156
93 // Created a time value based on |year|, |month| and |day_of_month| parameters. 157 // Created a time value based on |year|, |month| and |day_of_month| parameters.
94 Time CreateTimeFromParams(int year, int month, int day_of_month) { 158 Time CreateTimeFromParams(int year, int month, int day_of_month) {
95 DCHECK_GT(year, 1970); 159 DCHECK_GT(year, 1970);
96 DCHECK_GT(month, 0); 160 DCHECK_GT(month, 0);
97 DCHECK_LT(month, 13); 161 DCHECK_LT(month, 13);
98 DCHECK_GT(day_of_month, 0); 162 DCHECK_GT(day_of_month, 0);
99 DCHECK_LT(day_of_month, 32); 163 DCHECK_LT(day_of_month, 32);
100 164
101 Time::Exploded exploded; 165 Time::Exploded exploded;
102 exploded.year = year; 166 exploded.year = year;
(...skipping 783 matching lines...) Expand 10 before | Expand all | Expand 10 after
886 } 950 }
887 951
888 // static 952 // static
889 size_t FieldTrialList::GetFieldTrialCount() { 953 size_t FieldTrialList::GetFieldTrialCount() {
890 if (!global_) 954 if (!global_)
891 return 0; 955 return 0;
892 AutoLock auto_lock(global_->lock_); 956 AutoLock auto_lock(global_->lock_);
893 return global_->registered_.size(); 957 return global_->registered_.size();
894 } 958 }
895 959
960 // static
961 bool FieldTrialList::GetParamsFromSharedMemory(
962 FieldTrial* field_trial,
963 std::map<std::string, std::string>* params) {
964 DCHECK(global_);
965 // If the field trial allocator is not set up yet, then there are several
966 // cases:
967 // - We are in the browser process and the allocator has not been set up
968 // yet. If we got here, then we couldn't find the params in
969 // FieldTrialParamAssociator, so it's definitely not here. Return false.
970 // - Using shared memory for field trials is not enabled. If we got here,
971 // then there's nothing in shared memory. Return false.
972 // - We are in the child process and the allocator has not been set up yet.
973 // If this is the case, then you are calling this too early. The field trial
974 // allocator should get set up very early in the lifecycle. Try to see if
975 // you can call it after it's been set up.
976 if (!global_->field_trial_allocator_)
Alexei Svitkine (slow) 2016/11/24 21:58:48 field_trial_allocator_ shouldn't be used outside t
lawrencewu 2016/11/24 22:07:03 Done.
977 return false;
978
979 // If ref_ isn't set, then the field trial data can't be in shared memory.
980 if (!field_trial->ref_)
981 return false;
982
983 const FieldTrialEntry* entry =
984 global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>(
985 field_trial->ref_, kFieldTrialType);
986
987 size_t allocated_size =
988 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
989 size_t actual_size = sizeof(FieldTrialEntry) + entry->size;
990 if (allocated_size < actual_size)
991 return false;
992
993 return entry->GetParams(params);
994 }
995
996 // static
997 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
998 if (!global_ || !global_->field_trial_allocator_)
Alexei Svitkine (slow) 2016/11/24 21:58:48 field_trial_allocator_ shouldn't be checked outsid
lawrencewu 2016/11/24 22:07:03 Done.
999 return;
1000 AutoLock auto_lock(global_->lock_);
1001
1002 // To clear the params, we iterate through every item in the allocator, copy
1003 // just the trial and group name into a newly-allocated segment and then clear
1004 // the existing item.
1005 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1006 FieldTrialAllocator::Iterator mem_iter(allocator);
1007
1008 // List of refs to eventually be made iterable. We can't make it in the loop,
bcwhite 2016/11/28 21:57:41 It shouldn't. You're iterating for kFieldTrialTyp
lawrencewu 2016/11/28 22:51:26 I'm trying to make iterable the newly-allocated en
bcwhite 2016/11/30 15:05:33 Ah, of course. Good, then.
1009 // since it would go on forever.
1010 std::vector<FieldTrial::FieldTrialRef> new_refs;
1011
1012 FieldTrial::FieldTrialRef prev_ref;
1013 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) !=
1014 FieldTrialAllocator::kReferenceNull) {
1015 // Get the existing field trial entry in shared memory.
1016 const FieldTrialEntry* prev_entry =
1017 allocator->GetAsObject<const FieldTrialEntry>(prev_ref,
1018 kFieldTrialType);
1019 StringPiece trial_name;
1020 StringPiece group_name;
1021 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
1022 continue;
1023
1024 // Write a new entry, minus the params.
1025 Pickle pickle;
1026 pickle.WriteString(trial_name);
1027 pickle.WriteString(group_name);
1028 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1029 FieldTrial::FieldTrialRef new_ref =
1030 allocator->Allocate(total_size, kFieldTrialType);
1031 FieldTrialEntry* new_entry =
1032 allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType);
1033 new_entry->activated = prev_entry->activated;
1034 new_entry->size = pickle.size();
1035
1036 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
1037 // in memory, so we can avoid this memcpy.
1038 char* dst = reinterpret_cast<char*>(new_entry) + sizeof(FieldTrialEntry);
1039 memcpy(dst, pickle.data(), pickle.size());
1040
1041 // Update the ref on the field trial and add it to the list to be made
1042 // iterable.
1043 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
1044 trial->ref_ = new_ref;
1045 new_refs.push_back(new_ref);
1046
1047 // Mark the existing entry as unused.
1048 allocator->ChangeType(prev_ref, 0, kFieldTrialType);
1049 }
1050
1051 for (const auto& ref : new_refs) {
1052 allocator->MakeIterable(ref);
1053 }
1054 }
1055
896 #if defined(OS_WIN) 1056 #if defined(OS_WIN)
897 // static 1057 // static
898 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) { 1058 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) {
899 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); 1059 SharedMemoryHandle shm_handle(handle, GetCurrentProcId());
900 1060
901 // shm gets deleted when it gets out of scope, but that's OK because we need 1061 // 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. 1062 // it only for the duration of this method.
903 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); 1063 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
904 if (!shm.get()->Map(kFieldTrialAllocationSize)) 1064 if (!shm.get()->Map(kFieldTrialAllocationSize))
905 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); 1065 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
1029 FieldTrial::State trial_state; 1189 FieldTrial::State trial_state;
1030 if (!field_trial->GetStateWhileLocked(&trial_state)) 1190 if (!field_trial->GetStateWhileLocked(&trial_state))
1031 return; 1191 return;
1032 1192
1033 // Or if we've already added it. We must check after GetState since it can 1193 // Or if we've already added it. We must check after GetState since it can
1034 // also add to the allocator. 1194 // also add to the allocator.
1035 if (field_trial->ref_) 1195 if (field_trial->ref_)
1036 return; 1196 return;
1037 1197
1038 Pickle pickle; 1198 Pickle pickle;
1039 pickle.WriteString(trial_state.trial_name); 1199 if (!PickleFieldTrial(trial_state, &pickle)) {
1040 pickle.WriteString(trial_state.group_name); 1200 NOTREACHED();
1201 return;
1202 }
1041 1203
1042 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); 1204 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1043 FieldTrial::FieldTrialRef ref = 1205 FieldTrial::FieldTrialRef ref =
1044 allocator->Allocate(total_size, kFieldTrialType); 1206 allocator->Allocate(total_size, kFieldTrialType);
1045 if (ref == FieldTrialAllocator::kReferenceNull) 1207 if (ref == FieldTrialAllocator::kReferenceNull) {
1208 NOTREACHED();
1046 return; 1209 return;
1210 }
1047 1211
1048 FieldTrialEntry* entry = 1212 FieldTrialEntry* entry =
1049 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); 1213 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType);
1050 entry->activated = trial_state.activated; 1214 entry->activated = trial_state.activated;
1051 entry->size = pickle.size(); 1215 entry->size = pickle.size();
1052 1216
1053 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in 1217 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
1054 // memory, so we can avoid this memcpy. 1218 // memory, so we can avoid this memcpy.
1055 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); 1219 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry);
1056 memcpy(dst, pickle.data(), pickle.size()); 1220 memcpy(dst, pickle.data(), pickle.size());
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1108 return; 1272 return;
1109 } 1273 }
1110 AutoLock auto_lock(global_->lock_); 1274 AutoLock auto_lock(global_->lock_);
1111 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1275 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1112 trial->AddRef(); 1276 trial->AddRef();
1113 trial->SetTrialRegistered(); 1277 trial->SetTrialRegistered();
1114 global_->registered_[trial->trial_name()] = trial; 1278 global_->registered_[trial->trial_name()] = trial;
1115 } 1279 }
1116 1280
1117 } // namespace base 1281 } // 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