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

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

Issue 2578323002: Improved support for objects inside persistent memory. (Closed)
Patch Set: addressed review comments by asvitkine Created 3 years, 11 months 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/persistent_histogram_allocator.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"
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 // shared memory segment). See https://codereview.chromium.org/2365273004/ and 48 // shared memory segment). See https://codereview.chromium.org/2365273004/ and
49 // crbug.com/653874 49 // crbug.com/653874
50 // The browser is the only process that has write access to the shared memory. 50 // The browser is the only process that has write access to the shared memory.
51 // This is safe from race conditions because MakeIterable is a release operation 51 // This is safe from race conditions because MakeIterable is a release operation
52 // and GetNextOfType is an acquire operation, so memory writes before 52 // and GetNextOfType is an acquire operation, so memory writes before
53 // MakeIterable happen before memory reads after GetNextOfType. 53 // MakeIterable happen before memory reads after GetNextOfType.
54 const bool kUseSharedMemoryForFieldTrials = true; 54 const bool kUseSharedMemoryForFieldTrials = true;
55 55
56 // Constants for the field trial allocator. 56 // Constants for the field trial allocator.
57 const char kAllocatorName[] = "FieldTrialAllocator"; 57 const char kAllocatorName[] = "FieldTrialAllocator";
58 const uint32_t kFieldTrialType = 0xABA17E13 + 2; // SHA1(FieldTrialEntry) v2
59 58
60 // We allocate 128 KiB to hold all the field trial data. This should be enough, 59 // We allocate 128 KiB to hold all the field trial data. This should be enough,
61 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016). 60 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016).
62 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped 61 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped
63 // to physical memory when they are touched. If the size of the allocated field 62 // to physical memory when they are touched. If the size of the allocated field
64 // trials does get larger than 128 KiB, then we will drop some field trials in 63 // trials does get larger than 128 KiB, then we will drop some field trials in
65 // child processes, leading to an inconsistent view between browser and child 64 // child processes, leading to an inconsistent view between browser and child
66 // processes and possibly causing crashes (see crbug.com/661617). 65 // processes and possibly causing crashes (see crbug.com/661617).
67 #if !defined(OS_NACL) 66 #if !defined(OS_NACL)
68 const size_t kFieldTrialAllocationSize = 128 << 10; // 128 KiB 67 const size_t kFieldTrialAllocationSize = 128 << 10; // 128 KiB
(...skipping 641 matching lines...) Expand 10 before | Expand all | Expand 10 after
710 709
711 if (!global_->field_trial_allocator_) { 710 if (!global_->field_trial_allocator_) {
712 GetActiveFieldTrialGroupsFromString( 711 GetActiveFieldTrialGroupsFromString(
713 command_line.GetSwitchValueASCII(switches::kForceFieldTrials), 712 command_line.GetSwitchValueASCII(switches::kForceFieldTrials),
714 active_groups); 713 active_groups);
715 return; 714 return;
716 } 715 }
717 716
718 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); 717 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
719 FieldTrialAllocator::Iterator mem_iter(allocator); 718 FieldTrialAllocator::Iterator mem_iter(allocator);
720 FieldTrial::FieldTrialRef ref; 719 const FieldTrial::FieldTrialEntry* entry;
721 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != 720 while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
722 SharedPersistentMemoryAllocator::kReferenceNull) { 721 nullptr) {
723 const FieldTrial::FieldTrialEntry* entry =
724 allocator->GetAsObject<const FieldTrial::FieldTrialEntry>(
725 ref, kFieldTrialType);
726
727 StringPiece trial_name; 722 StringPiece trial_name;
728 StringPiece group_name; 723 StringPiece group_name;
729 if (subtle::NoBarrier_Load(&entry->activated) && 724 if (subtle::NoBarrier_Load(&entry->activated) &&
730 entry->GetTrialAndGroupName(&trial_name, &group_name)) { 725 entry->GetTrialAndGroupName(&trial_name, &group_name)) {
731 FieldTrial::ActiveGroup group; 726 FieldTrial::ActiveGroup group;
732 group.trial_name = trial_name.as_string(); 727 group.trial_name = trial_name.as_string();
733 group.group_name = group_name.as_string(); 728 group.group_name = group_name.as_string();
734 active_groups->push_back(group); 729 active_groups->push_back(group);
735 } 730 }
736 } 731 }
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after
1035 // you can call it after it's been set up. 1030 // you can call it after it's been set up.
1036 AutoLock auto_lock(global_->lock_); 1031 AutoLock auto_lock(global_->lock_);
1037 if (!global_->field_trial_allocator_) 1032 if (!global_->field_trial_allocator_)
1038 return false; 1033 return false;
1039 1034
1040 // If ref_ isn't set, then the field trial data can't be in shared memory. 1035 // If ref_ isn't set, then the field trial data can't be in shared memory.
1041 if (!field_trial->ref_) 1036 if (!field_trial->ref_)
1042 return false; 1037 return false;
1043 1038
1044 const FieldTrial::FieldTrialEntry* entry = 1039 const FieldTrial::FieldTrialEntry* entry =
1045 global_->field_trial_allocator_ 1040 global_->field_trial_allocator_->GetAsObject<FieldTrial::FieldTrialEntry>(
1046 ->GetAsObject<const FieldTrial::FieldTrialEntry>(field_trial->ref_, 1041 field_trial->ref_);
1047 kFieldTrialType);
1048 1042
1049 size_t allocated_size = 1043 size_t allocated_size =
1050 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); 1044 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
1051 size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size; 1045 size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size;
1052 if (allocated_size < actual_size) 1046 if (allocated_size < actual_size)
1053 return false; 1047 return false;
1054 1048
1055 return entry->GetParams(params); 1049 return entry->GetParams(params);
1056 } 1050 }
1057 1051
(...skipping 10 matching lines...) Expand all
1068 // just the trial and group name into a newly-allocated segment and then clear 1062 // just the trial and group name into a newly-allocated segment and then clear
1069 // the existing item. 1063 // the existing item.
1070 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); 1064 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
1071 FieldTrialAllocator::Iterator mem_iter(allocator); 1065 FieldTrialAllocator::Iterator mem_iter(allocator);
1072 1066
1073 // List of refs to eventually be made iterable. We can't make it in the loop, 1067 // List of refs to eventually be made iterable. We can't make it in the loop,
1074 // since it would go on forever. 1068 // since it would go on forever.
1075 std::vector<FieldTrial::FieldTrialRef> new_refs; 1069 std::vector<FieldTrial::FieldTrialRef> new_refs;
1076 1070
1077 FieldTrial::FieldTrialRef prev_ref; 1071 FieldTrial::FieldTrialRef prev_ref;
1078 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) != 1072 while ((prev_ref = mem_iter.GetNextOfType<FieldTrial::FieldTrialEntry>()) !=
1079 FieldTrialAllocator::kReferenceNull) { 1073 FieldTrialAllocator::kReferenceNull) {
1080 // Get the existing field trial entry in shared memory. 1074 // Get the existing field trial entry in shared memory.
1081 const FieldTrial::FieldTrialEntry* prev_entry = 1075 const FieldTrial::FieldTrialEntry* prev_entry =
1082 allocator->GetAsObject<const FieldTrial::FieldTrialEntry>( 1076 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(prev_ref);
1083 prev_ref, kFieldTrialType);
1084 StringPiece trial_name; 1077 StringPiece trial_name;
1085 StringPiece group_name; 1078 StringPiece group_name;
1086 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) 1079 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
1087 continue; 1080 continue;
1088 1081
1089 // Write a new entry, minus the params. 1082 // Write a new entry, minus the params.
1090 Pickle pickle; 1083 Pickle pickle;
1091 pickle.WriteString(trial_name); 1084 pickle.WriteString(trial_name);
1092 pickle.WriteString(group_name); 1085 pickle.WriteString(group_name);
1093 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); 1086 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
1094 FieldTrial::FieldTrialRef new_ref =
1095 allocator->Allocate(total_size, kFieldTrialType);
1096 FieldTrial::FieldTrialEntry* new_entry = 1087 FieldTrial::FieldTrialEntry* new_entry =
1097 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(new_ref, 1088 allocator->AllocateObject<FieldTrial::FieldTrialEntry>(total_size);
1098 kFieldTrialType);
1099 subtle::NoBarrier_Store(&new_entry->activated, 1089 subtle::NoBarrier_Store(&new_entry->activated,
1100 subtle::NoBarrier_Load(&prev_entry->activated)); 1090 subtle::NoBarrier_Load(&prev_entry->activated));
1101 new_entry->pickle_size = pickle.size(); 1091 new_entry->pickle_size = pickle.size();
1102 1092
1103 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section 1093 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
1104 // in memory, so we can avoid this memcpy. 1094 // in memory, so we can avoid this memcpy.
1105 char* dst = reinterpret_cast<char*>(new_entry) + 1095 char* dst = reinterpret_cast<char*>(new_entry) +
1106 sizeof(FieldTrial::FieldTrialEntry); 1096 sizeof(FieldTrial::FieldTrialEntry);
1107 memcpy(dst, pickle.data(), pickle.size()); 1097 memcpy(dst, pickle.data(), pickle.size());
1108 1098
1109 // Update the ref on the field trial and add it to the list to be made 1099 // Update the ref on the field trial and add it to the list to be made
1110 // iterable. 1100 // iterable.
1101 FieldTrial::FieldTrialRef new_ref = allocator->GetAsReference(new_entry);
1111 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string()); 1102 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
1112 trial->ref_ = new_ref; 1103 trial->ref_ = new_ref;
1113 new_refs.push_back(new_ref); 1104 new_refs.push_back(new_ref);
1114 1105
1115 // Mark the existing entry as unused. 1106 // Mark the existing entry as unused.
1116 allocator->ChangeType(prev_ref, 0, kFieldTrialType); 1107 allocator->ChangeType(prev_ref, 0,
1108 FieldTrial::FieldTrialEntry::kPersistentTypeId);
1117 } 1109 }
1118 1110
1119 for (const auto& ref : new_refs) { 1111 for (const auto& ref : new_refs) {
1120 allocator->MakeIterable(ref); 1112 allocator->MakeIterable(ref);
1121 } 1113 }
1122 } 1114 }
1123 1115
1124 // static 1116 // static
1125 void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator( 1117 void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(
1126 PersistentMemoryAllocator* allocator) { 1118 PersistentMemoryAllocator* allocator) {
1127 if (!global_) 1119 if (!global_)
1128 return; 1120 return;
1129 AutoLock auto_lock(global_->lock_); 1121 AutoLock auto_lock(global_->lock_);
1130 for (const auto& registered : global_->registered_) { 1122 for (const auto& registered : global_->registered_) {
1131 AddToAllocatorWhileLocked(allocator, registered.second); 1123 AddToAllocatorWhileLocked(allocator, registered.second);
1132 } 1124 }
1133 } 1125 }
1134 1126
1135 // static 1127 // static
1136 std::vector<const FieldTrial::FieldTrialEntry*> 1128 std::vector<const FieldTrial::FieldTrialEntry*>
1137 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator( 1129 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(
1138 PersistentMemoryAllocator const& allocator) { 1130 PersistentMemoryAllocator const& allocator) {
1139 std::vector<const FieldTrial::FieldTrialEntry*> entries; 1131 std::vector<const FieldTrial::FieldTrialEntry*> entries;
1140 FieldTrial::FieldTrialRef ref;
1141 FieldTrialAllocator::Iterator iter(&allocator); 1132 FieldTrialAllocator::Iterator iter(&allocator);
1142 while ((ref = iter.GetNextOfType(kFieldTrialType)) != 1133 const FieldTrial::FieldTrialEntry* entry;
1143 FieldTrialAllocator::kReferenceNull) { 1134 while ((entry = iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
1144 const FieldTrial::FieldTrialEntry* entry = 1135 nullptr) {
1145 allocator.GetAsObject<const FieldTrial::FieldTrialEntry>(
1146 ref, kFieldTrialType);
1147 entries.push_back(entry); 1136 entries.push_back(entry);
1148 } 1137 }
1149 return entries; 1138 return entries;
1150 } 1139 }
1151 1140
1152 #if defined(OS_WIN) 1141 #if defined(OS_WIN)
1153 // static 1142 // static
1154 bool FieldTrialList::CreateTrialsFromHandleSwitch( 1143 bool FieldTrialList::CreateTrialsFromHandleSwitch(
1155 const std::string& handle_switch) { 1144 const std::string& handle_switch) {
1156 int field_trial_handle = std::stoi(handle_switch); 1145 int field_trial_handle = std::stoi(handle_switch);
(...skipping 18 matching lines...) Expand all
1175 #endif 1164 #endif
1176 1165
1177 // static 1166 // static
1178 bool FieldTrialList::CreateTrialsFromSharedMemory( 1167 bool FieldTrialList::CreateTrialsFromSharedMemory(
1179 std::unique_ptr<SharedMemory> shm) { 1168 std::unique_ptr<SharedMemory> shm) {
1180 global_->field_trial_allocator_.reset( 1169 global_->field_trial_allocator_.reset(
1181 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); 1170 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true));
1182 FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); 1171 FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get();
1183 FieldTrialAllocator::Iterator mem_iter(shalloc); 1172 FieldTrialAllocator::Iterator mem_iter(shalloc);
1184 1173
1185 FieldTrial::FieldTrialRef ref; 1174 const FieldTrial::FieldTrialEntry* entry;
1186 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != 1175 while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
1187 FieldTrialAllocator::kReferenceNull) { 1176 nullptr) {
1188 const FieldTrial::FieldTrialEntry* entry =
1189 shalloc->GetAsObject<const FieldTrial::FieldTrialEntry>(
1190 ref, kFieldTrialType);
1191
1192 StringPiece trial_name; 1177 StringPiece trial_name;
1193 StringPiece group_name; 1178 StringPiece group_name;
1194 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) 1179 if (!entry->GetTrialAndGroupName(&trial_name, &group_name))
1195 return false; 1180 return false;
1196 1181
1197 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take 1182 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take
1198 // StringPieces. 1183 // StringPieces.
1199 FieldTrial* trial = 1184 FieldTrial* trial =
1200 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); 1185 CreateFieldTrial(trial_name.as_string(), group_name.as_string());
1201 1186
1202 trial->ref_ = ref; 1187 trial->ref_ = mem_iter.GetAsReference(entry);
1203 if (subtle::NoBarrier_Load(&entry->activated)) { 1188 if (subtle::NoBarrier_Load(&entry->activated)) {
1204 // Call |group()| to mark the trial as "used" and notify observers, if 1189 // Call |group()| to mark the trial as "used" and notify observers, if
1205 // any. This is useful to ensure that field trials created in child 1190 // any. This is useful to ensure that field trials created in child
1206 // processes are properly reported in crash reports. 1191 // processes are properly reported in crash reports.
1207 trial->group(); 1192 trial->group();
1208 } 1193 }
1209 } 1194 }
1210 return true; 1195 return true;
1211 } 1196 }
1212 1197
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
1279 if (field_trial->ref_) 1264 if (field_trial->ref_)
1280 return; 1265 return;
1281 1266
1282 Pickle pickle; 1267 Pickle pickle;
1283 if (!PickleFieldTrial(trial_state, &pickle)) { 1268 if (!PickleFieldTrial(trial_state, &pickle)) {
1284 NOTREACHED(); 1269 NOTREACHED();
1285 return; 1270 return;
1286 } 1271 }
1287 1272
1288 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); 1273 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
1289 FieldTrial::FieldTrialRef ref = 1274 FieldTrial::FieldTrialRef ref = allocator->Allocate(
1290 allocator->Allocate(total_size, kFieldTrialType); 1275 total_size, FieldTrial::FieldTrialEntry::kPersistentTypeId);
1291 if (ref == FieldTrialAllocator::kReferenceNull) { 1276 if (ref == FieldTrialAllocator::kReferenceNull) {
1292 NOTREACHED(); 1277 NOTREACHED();
1293 return; 1278 return;
1294 } 1279 }
1295 1280
1296 FieldTrial::FieldTrialEntry* entry = 1281 FieldTrial::FieldTrialEntry* entry =
1297 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, kFieldTrialType); 1282 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
1298 subtle::NoBarrier_Store(&entry->activated, trial_state.activated); 1283 subtle::NoBarrier_Store(&entry->activated, trial_state.activated);
1299 entry->pickle_size = pickle.size(); 1284 entry->pickle_size = pickle.size();
1300 1285
1301 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in 1286 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
1302 // memory, so we can avoid this memcpy. 1287 // memory, so we can avoid this memcpy.
1303 char* dst = 1288 char* dst =
1304 reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry); 1289 reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry);
1305 memcpy(dst, pickle.data(), pickle.size()); 1290 memcpy(dst, pickle.data(), pickle.size());
1306 1291
1307 allocator->MakeIterable(ref); 1292 allocator->MakeIterable(ref);
(...skipping 13 matching lines...) Expand all
1321 if (ref == FieldTrialAllocator::kReferenceNull) { 1306 if (ref == FieldTrialAllocator::kReferenceNull) {
1322 // It's fine to do this even if the allocator hasn't been instantiated 1307 // It's fine to do this even if the allocator hasn't been instantiated
1323 // yet -- it'll just return early. 1308 // yet -- it'll just return early.
1324 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), 1309 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
1325 field_trial); 1310 field_trial);
1326 } else { 1311 } else {
1327 // It's also okay to do this even though the callee doesn't have a lock -- 1312 // It's also okay to do this even though the callee doesn't have a lock --
1328 // the only thing that happens on a stale read here is a slight performance 1313 // the only thing that happens on a stale read here is a slight performance
1329 // hit from the child re-synchronizing activation state. 1314 // hit from the child re-synchronizing activation state.
1330 FieldTrial::FieldTrialEntry* entry = 1315 FieldTrial::FieldTrialEntry* entry =
1331 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, 1316 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
1332 kFieldTrialType);
1333 subtle::NoBarrier_Store(&entry->activated, 1); 1317 subtle::NoBarrier_Store(&entry->activated, 1);
1334 } 1318 }
1335 } 1319 }
1336 1320
1337 // static 1321 // static
1338 const FieldTrial::EntropyProvider* 1322 const FieldTrial::EntropyProvider*
1339 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { 1323 FieldTrialList::GetEntropyProviderForOneTimeRandomization() {
1340 if (!global_) { 1324 if (!global_) {
1341 used_without_global_ = true; 1325 used_without_global_ = true;
1342 return NULL; 1326 return NULL;
(...skipping 16 matching lines...) Expand all
1359 return; 1343 return;
1360 } 1344 }
1361 AutoLock auto_lock(global_->lock_); 1345 AutoLock auto_lock(global_->lock_);
1362 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1346 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1363 trial->AddRef(); 1347 trial->AddRef();
1364 trial->SetTrialRegistered(); 1348 trial->SetTrialRegistered();
1365 global_->registered_[trial->trial_name()] = trial; 1349 global_->registered_[trial->trial_name()] = trial;
1366 } 1350 }
1367 1351
1368 } // namespace base 1352 } // namespace base
OLDNEW
« no previous file with comments | « base/metrics/field_trial.h ('k') | base/metrics/persistent_histogram_allocator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698