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

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

Issue 2463223002: Store field trial parameters in shared memory (Closed)
Patch Set: add locks 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 (!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 AutoLock auto_lock(global_->lock_);
977 if (!global_->field_trial_allocator_)
978 return false;
979
980 // If ref_ isn't set, then the field trial data can't be in shared memory.
981 if (!field_trial->ref_)
982 return false;
983
984 const FieldTrialEntry* entry =
985 global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>(
986 field_trial->ref_, kFieldTrialType);
987
988 size_t allocated_size =
989 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
990 size_t actual_size = sizeof(FieldTrialEntry) + entry->size;
991 if (allocated_size < actual_size)
992 return false;
993
994 return entry->GetParams(params);
995 }
996
997 // static
998 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
999 if (!global_)
1000 return;
1001
1002 AutoLock auto_lock(global_->lock_);
1003 if (!global_->field_trial_allocator_)
1004 return;
1005
1006 // To clear the params, we iterate through every item in the allocator, copy
1007 // just the trial and group name into a newly-allocated segment and then clear
1008 // the existing item.
1009 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1010 FieldTrialAllocator::Iterator mem_iter(allocator);
1011
1012 // List of refs to eventually be made iterable. We can't make it in the loop,
1013 // since it would go on forever.
1014 std::vector<FieldTrial::FieldTrialRef> new_refs;
1015
1016 FieldTrial::FieldTrialRef prev_ref;
1017 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) !=
1018 FieldTrialAllocator::kReferenceNull) {
1019 // Get the existing field trial entry in shared memory.
1020 const FieldTrialEntry* prev_entry =
1021 allocator->GetAsObject<const FieldTrialEntry>(prev_ref,
1022 kFieldTrialType);
1023 StringPiece trial_name;
1024 StringPiece group_name;
1025 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
1026 continue;
1027
1028 // Write a new entry, minus the params.
1029 Pickle pickle;
1030 pickle.WriteString(trial_name);
1031 pickle.WriteString(group_name);
1032 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1033 FieldTrial::FieldTrialRef new_ref =
1034 allocator->Allocate(total_size, kFieldTrialType);
1035 FieldTrialEntry* new_entry =
1036 allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType);
1037 new_entry->activated = prev_entry->activated;
1038 new_entry->size = pickle.size();
1039
1040 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
1041 // in memory, so we can avoid this memcpy.
1042 char* dst = reinterpret_cast<char*>(new_entry) + sizeof(FieldTrialEntry);
1043 memcpy(dst, pickle.data(), pickle.size());
1044
1045 // Update the ref on the field trial and add it to the list to be made
1046 // iterable.
1047 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
1048 trial->ref_ = new_ref;
1049 new_refs.push_back(new_ref);
1050
1051 // Mark the existing entry as unused.
1052 allocator->ChangeType(prev_ref, 0, kFieldTrialType);
1053 }
1054
1055 for (const auto& ref : new_refs) {
1056 allocator->MakeIterable(ref);
1057 }
1058 }
1059
896 #if defined(OS_WIN) 1060 #if defined(OS_WIN)
897 // static 1061 // static
898 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) { 1062 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) {
899 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); 1063 SharedMemoryHandle shm_handle(handle, GetCurrentProcId());
900 1064
901 // shm gets deleted when it gets out of scope, but that's OK because we need 1065 // 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. 1066 // it only for the duration of this method.
903 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); 1067 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
904 if (!shm.get()->Map(kFieldTrialAllocationSize)) 1068 if (!shm.get()->Map(kFieldTrialAllocationSize))
905 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); 1069 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
1029 FieldTrial::State trial_state; 1193 FieldTrial::State trial_state;
1030 if (!field_trial->GetStateWhileLocked(&trial_state)) 1194 if (!field_trial->GetStateWhileLocked(&trial_state))
1031 return; 1195 return;
1032 1196
1033 // Or if we've already added it. We must check after GetState since it can 1197 // Or if we've already added it. We must check after GetState since it can
1034 // also add to the allocator. 1198 // also add to the allocator.
1035 if (field_trial->ref_) 1199 if (field_trial->ref_)
1036 return; 1200 return;
1037 1201
1038 Pickle pickle; 1202 Pickle pickle;
1039 pickle.WriteString(trial_state.trial_name); 1203 if (!PickleFieldTrial(trial_state, &pickle)) {
1040 pickle.WriteString(trial_state.group_name); 1204 NOTREACHED();
1205 return;
1206 }
1041 1207
1042 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); 1208 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
1043 FieldTrial::FieldTrialRef ref = 1209 FieldTrial::FieldTrialRef ref =
1044 allocator->Allocate(total_size, kFieldTrialType); 1210 allocator->Allocate(total_size, kFieldTrialType);
1045 if (ref == FieldTrialAllocator::kReferenceNull) 1211 if (ref == FieldTrialAllocator::kReferenceNull) {
1212 NOTREACHED();
1046 return; 1213 return;
1214 }
1047 1215
1048 FieldTrialEntry* entry = 1216 FieldTrialEntry* entry =
1049 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); 1217 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType);
1050 entry->activated = trial_state.activated; 1218 entry->activated = trial_state.activated;
1051 entry->size = pickle.size(); 1219 entry->size = pickle.size();
1052 1220
1053 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in 1221 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
1054 // memory, so we can avoid this memcpy. 1222 // memory, so we can avoid this memcpy.
1055 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); 1223 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry);
1056 memcpy(dst, pickle.data(), pickle.size()); 1224 memcpy(dst, pickle.data(), pickle.size());
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1108 return; 1276 return;
1109 } 1277 }
1110 AutoLock auto_lock(global_->lock_); 1278 AutoLock auto_lock(global_->lock_);
1111 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1279 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1112 trial->AddRef(); 1280 trial->AddRef();
1113 trial->SetTrialRegistered(); 1281 trial->SetTrialRegistered();
1114 global_->registered_[trial->trial_name()] = trial; 1282 global_->registered_[trial->trial_name()] = trial;
1115 } 1283 }
1116 1284
1117 } // namespace base 1285 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698