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 3 // found in the LICENSE file. 4 5 #include "base/metrics/field_trial.h" 6 7 #include <algorithm> 8 #include <utility> 9 10 #include "base/base_switches.h" 11 #include "base/build_time.h" 12 #include "base/command_line.h" 13 #include "base/debug/activity_tracker.h" 14 #include "base/logging.h" 15 #include "base/metrics/field_trial_param_associator.h" 16 #include "base/process/memory.h" 17 #include "base/rand_util.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/strings/utf_string_conversions.h" 22 23 // On POSIX, the fd is shared using the mapping in GlobalDescriptors. 24 #if defined(OS_POSIX) && !defined(OS_NACL) 25 #include "base/posix/global_descriptors.h" 26 #endif 27 28 namespace base { 29 30 namespace { 31 32 // Define a separator character to use when creating a persistent form of an 33 // instance. This is intended for use as a command line argument, passed to a 34 // second process to mimic our state (i.e., provide the same group name). 35 const char kPersistentStringSeparator = '/'; // Currently a slash. 36 37 // Define a marker character to be used as a prefix to a trial name on the 38 // command line which forces its activation. 39 const char kActivationMarker = '*'; 40 41 // Use shared memory to communicate field trial (experiment) state. Set to false 42 // for now while the implementation is fleshed out (e.g. data format, single 43 // shared memory segment). See https://codereview.chromium.org/2365273004/ and 44 // crbug.com/653874 45 // The browser is the only process that has write access to the shared memory. 46 // This is safe from race conditions because MakeIterable is a release operation 47 // and GetNextOfType is an acquire operation, so memory writes before 48 // MakeIterable happen before memory reads after GetNextOfType. 49 const bool kUseSharedMemoryForFieldTrials = true; 50 51 // Constants for the field trial allocator. 52 const char kAllocatorName[] = "FieldTrialAllocator"; 53 54 // We allocate 128 KiB to hold all the field trial data. This should be enough, 55 // as most people use 3 - 25 KiB for field trials (as of 11/25/2016). 56 // This also doesn't allocate all 128 KiB at once -- the pages only get mapped 57 // to physical memory when they are touched. If the size of the allocated field 58 // trials does get larger than 128 KiB, then we will drop some field trials in 59 // child processes, leading to an inconsistent view between browser and child 60 // processes and possibly causing crashes (see crbug.com/661617). 61 const size_t kFieldTrialAllocationSize = 128 << 10; // 128 KiB 62 63 // Writes out string1 and then string2 to pickle. 64 bool WriteStringPair(Pickle* pickle, 65 const StringPiece& string1, 66 const StringPiece& string2) { 67 if (!pickle->WriteString(string1)) 68 return false; 69 if (!pickle->WriteString(string2)) 70 return false; 71 return true; 72 } 73 74 // Writes out the field trial's contents (via trial_state) to the pickle. The 75 // format of the pickle looks like: 76 // TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ... 77 // If there are no parameters, then it just ends at GroupName. 78 bool PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) { 79 if (!WriteStringPair(pickle, *trial_state.trial_name, 80 *trial_state.group_name)) { 81 return false; 82 } 83 84 // Get field trial params. 85 std::map<std::string, std::string> params; 86 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback( 87 *trial_state.trial_name, *trial_state.group_name, ¶ms); 88 89 // Write params to pickle. 90 for (const auto& param : params) { 91 if (!WriteStringPair(pickle, param.first, param.second)) 92 return false; 93 } 94 return true; 95 } 96 97 // Created a time value based on |year|, |month| and |day_of_month| parameters. 98 Time CreateTimeFromParams(int year, int month, int day_of_month) { 99 DCHECK_GT(year, 1970); 100 DCHECK_GT(month, 0); 101 DCHECK_LT(month, 13); 102 DCHECK_GT(day_of_month, 0); 103 DCHECK_LT(day_of_month, 32); 104 105 Time::Exploded exploded; 106 exploded.year = year; 107 exploded.month = month; 108 exploded.day_of_week = 0; // Should be unused. 109 exploded.day_of_month = day_of_month; 110 exploded.hour = 0; 111 exploded.minute = 0; 112 exploded.second = 0; 113 exploded.millisecond = 0; 114 Time out_time; 115 if (!Time::FromLocalExploded(exploded, &out_time)) { 116 // TODO(maksims): implement failure handling. 117 // We might just return |out_time|, which is Time(0). 118 NOTIMPLEMENTED(); 119 } 120 121 return out_time; 122 } 123 124 // Returns the boundary value for comparing against the FieldTrial's added 125 // groups for a given |divisor| (total probability) and |entropy_value|. 126 FieldTrial::Probability GetGroupBoundaryValue( 127 FieldTrial::Probability divisor, 128 double entropy_value) { 129 // Add a tiny epsilon value to get consistent results when converting floating 130 // points to int. Without it, boundary values have inconsistent results, e.g.: 131 // 132 // static_cast<FieldTrial::Probability>(100 * 0.56) == 56 133 // static_cast<FieldTrial::Probability>(100 * 0.57) == 56 134 // static_cast<FieldTrial::Probability>(100 * 0.58) == 57 135 // static_cast<FieldTrial::Probability>(100 * 0.59) == 59 136 const double kEpsilon = 1e-8; 137 const FieldTrial::Probability result = 138 static_cast<FieldTrial::Probability>(divisor * entropy_value + kEpsilon); 139 // Ensure that adding the epsilon still results in a value < |divisor|. 140 return std::min(result, divisor - 1); 141 } 142 143 // Separate type from FieldTrial::State so that it can use StringPieces. 144 struct FieldTrialStringEntry { 145 StringPiece trial_name; 146 StringPiece group_name; 147 bool activated = false; 148 }; 149 150 // Parses the --force-fieldtrials string |trials_string| into |entries|. 151 // Returns true if the string was parsed correctly. On failure, the |entries| 152 // array may end up being partially filled. 153 bool ParseFieldTrialsString(const std::string& trials_string, 154 std::vector<FieldTrialStringEntry>* entries) { 155 const StringPiece trials_string_piece(trials_string); 156 157 size_t next_item = 0; 158 while (next_item < trials_string.length()) { 159 size_t name_end = trials_string.find(kPersistentStringSeparator, next_item); 160 if (name_end == trials_string.npos || next_item == name_end) 161 return false; 162 size_t group_name_end = 163 trials_string.find(kPersistentStringSeparator, name_end + 1); 164 if (name_end + 1 == group_name_end) 165 return false; 166 if (group_name_end == trials_string.npos) 167 group_name_end = trials_string.length(); 168 169 FieldTrialStringEntry entry; 170 // Verify if the trial should be activated or not. 171 if (trials_string[next_item] == kActivationMarker) { 172 // Name cannot be only the indicator. 173 if (name_end - next_item == 1) 174 return false; 175 next_item++; 176 entry.activated = true; 177 } 178 entry.trial_name = 179 trials_string_piece.substr(next_item, name_end - next_item); 180 entry.group_name = 181 trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1); 182 next_item = group_name_end + 1; 183 184 entries->push_back(std::move(entry)); 185 } 186 return true; 187 } 188 189 void AddFeatureAndFieldTrialFlags(const char* enable_features_switch, 190 const char* disable_features_switch, 191 CommandLine* cmd_line) { 192 std::string enabled_features; 193 std::string disabled_features; 194 FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features, 195 &disabled_features); 196 197 if (!enabled_features.empty()) 198 cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features); 199 if (!disabled_features.empty()) 200 cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features); 201 202 std::string field_trial_states; 203 FieldTrialList::AllStatesToString(&field_trial_states); 204 if (!field_trial_states.empty()) { 205 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, 206 field_trial_states); 207 } 208 } 209 210 #if defined(OS_WIN) 211 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { 212 HANDLE src = allocator->shared_memory()->handle().GetHandle(); 213 ProcessHandle process = GetCurrentProcess(); 214 DWORD access = SECTION_MAP_READ | SECTION_QUERY; 215 HANDLE dst; 216 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) 217 return kInvalidPlatformFile; 218 return dst; 219 } 220 #endif 221 222 #if defined(OS_POSIX) && !defined(OS_NACL) 223 int CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { 224 SharedMemoryHandle new_handle; 225 allocator->shared_memory()->ShareReadOnlyToProcess(GetCurrentProcessHandle(), 226 &new_handle); 227 return SharedMemory::GetFdFromSharedMemoryHandle(new_handle); 228 } 229 #endif 230 231 void OnOutOfMemory(size_t size) { 232 #if defined(OS_NACL) 233 NOTREACHED(); 234 #else 235 TerminateBecauseOutOfMemory(size); 236 #endif 237 } 238 239 } // namespace 240 241 // statics 242 const int FieldTrial::kNotFinalized = -1; 243 const int FieldTrial::kDefaultGroupNumber = 0; 244 bool FieldTrial::enable_benchmarking_ = false; 245 246 int FieldTrialList::kNoExpirationYear = 0; 247 248 //------------------------------------------------------------------------------ 249 // FieldTrial methods and members. 250 251 FieldTrial::EntropyProvider::~EntropyProvider() { 252 } 253 254 FieldTrial::State::State() {} 255 256 FieldTrial::State::State(const State& other) = default; 257 258 FieldTrial::State::~State() {} 259 260 bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName( 261 StringPiece* trial_name, 262 StringPiece* group_name) const { 263 PickleIterator iter = GetPickleIterator(); 264 return ReadStringPair(&iter, trial_name, group_name); 265 } 266 267 bool FieldTrial::FieldTrialEntry::GetParams( 268 std::map<std::string, std::string>* params) const { 269 PickleIterator iter = GetPickleIterator(); 270 StringPiece tmp; 271 // Skip reading trial and group name. 272 if (!ReadStringPair(&iter, &tmp, &tmp)) 273 return false; 274 275 while (true) { 276 StringPiece key; 277 StringPiece value; 278 if (!ReadStringPair(&iter, &key, &value)) 279 return key.empty(); // Non-empty is bad: got one of a pair. 280 (*params)[key.as_string()] = value.as_string(); 281 } 282 } 283 284 PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const { 285 const char* src = 286 reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry); 287 288 Pickle pickle(src, pickle_size); 289 return PickleIterator(pickle); 290 } 291 292 bool FieldTrial::FieldTrialEntry::ReadStringPair( 293 PickleIterator* iter, 294 StringPiece* trial_name, 295 StringPiece* group_name) const { 296 if (!iter->ReadStringPiece(trial_name)) 297 return false; 298 if (!iter->ReadStringPiece(group_name)) 299 return false; 300 return true; 301 } 302 303 void FieldTrial::Disable() { 304 DCHECK(!group_reported_); 305 enable_field_trial_ = false; 306 307 // In case we are disabled after initialization, we need to switch 308 // the trial to the default group. 309 if (group_ != kNotFinalized) { 310 // Only reset when not already the default group, because in case we were 311 // forced to the default group, the group number may not be 312 // kDefaultGroupNumber, so we should keep it as is. 313 if (group_name_ != default_group_name_) 314 SetGroupChoice(default_group_name_, kDefaultGroupNumber); 315 } 316 } 317 318 int FieldTrial::AppendGroup(const std::string& name, 319 Probability group_probability) { 320 // When the group choice was previously forced, we only need to return the 321 // the id of the chosen group, and anything can be returned for the others. 322 if (forced_) { 323 DCHECK(!group_name_.empty()); 324 if (name == group_name_) { 325 // Note that while |group_| may be equal to |kDefaultGroupNumber| on the 326 // forced trial, it will not have the same value as the default group 327 // number returned from the non-forced |FactoryGetFieldTrial()| call, 328 // which takes care to ensure that this does not happen. 329 return group_; 330 } 331 DCHECK_NE(next_group_number_, group_); 332 // We still return different numbers each time, in case some caller need 333 // them to be different. 334 return next_group_number_++; 335 } 336 337 DCHECK_LE(group_probability, divisor_); 338 DCHECK_GE(group_probability, 0); 339 340 if (enable_benchmarking_ || !enable_field_trial_) 341 group_probability = 0; 342 343 accumulated_group_probability_ += group_probability; 344 345 DCHECK_LE(accumulated_group_probability_, divisor_); 346 if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { 347 // This is the group that crossed the random line, so we do the assignment. 348 SetGroupChoice(name, next_group_number_); 349 } 350 return next_group_number_++; 351 } 352 353 int FieldTrial::group() { 354 FinalizeGroupChoice(); 355 if (trial_registered_) 356 FieldTrialList::NotifyFieldTrialGroupSelection(this); 357 return group_; 358 } 359 360 const std::string& FieldTrial::group_name() { 361 // Call |group()| to ensure group gets assigned and observers are notified. 362 group(); 363 DCHECK(!group_name_.empty()); 364 return group_name_; 365 } 366 367 const std::string& FieldTrial::GetGroupNameWithoutActivation() { 368 FinalizeGroupChoice(); 369 return group_name_; 370 } 371 372 void FieldTrial::SetForced() { 373 // We might have been forced before (e.g., by CreateFieldTrial) and it's 374 // first come first served, e.g., command line switch has precedence. 375 if (forced_) 376 return; 377 378 // And we must finalize the group choice before we mark ourselves as forced. 379 FinalizeGroupChoice(); 380 forced_ = true; 381 } 382 383 // static 384 void FieldTrial::EnableBenchmarking() { 385 DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); 386 enable_benchmarking_ = true; 387 } 388 389 // static 390 FieldTrial* FieldTrial::CreateSimulatedFieldTrial( 391 const std::string& trial_name, 392 Probability total_probability, 393 const std::string& default_group_name, 394 double entropy_value) { 395 return new FieldTrial(trial_name, total_probability, default_group_name, 396 entropy_value); 397 } 398 399 FieldTrial::FieldTrial(const std::string& trial_name, 400 const Probability total_probability, 401 const std::string& default_group_name, 402 double entropy_value) 403 : trial_name_(trial_name), 404 divisor_(total_probability), 405 default_group_name_(default_group_name), 406 random_(GetGroupBoundaryValue(total_probability, entropy_value)), 407 accumulated_group_probability_(0), 408 next_group_number_(kDefaultGroupNumber + 1), 409 group_(kNotFinalized), 410 enable_field_trial_(true), 411 forced_(false), 412 group_reported_(false), 413 trial_registered_(false), 414 ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull) { 415 DCHECK_GT(total_probability, 0); 416 DCHECK(!trial_name_.empty()); 417 DCHECK(!default_group_name_.empty()); 418 } 419 420 FieldTrial::~FieldTrial() {} 421 422 void FieldTrial::SetTrialRegistered() { 423 DCHECK_EQ(kNotFinalized, group_); 424 DCHECK(!trial_registered_); 425 trial_registered_ = true; 426 } 427 428 void FieldTrial::SetGroupChoice(const std::string& group_name, int number) { 429 group_ = number; 430 if (group_name.empty()) 431 StringAppendF(&group_name_, "%d", group_); 432 else 433 group_name_ = group_name; 434 DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_; 435 } 436 437 void FieldTrial::FinalizeGroupChoice() { 438 FinalizeGroupChoiceImpl(false); 439 } 440 441 void FieldTrial::FinalizeGroupChoiceImpl(bool is_locked) { 442 if (group_ != kNotFinalized) 443 return; 444 accumulated_group_probability_ = divisor_; 445 // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not 446 // finalized. 447 DCHECK(!forced_); 448 SetGroupChoice(default_group_name_, kDefaultGroupNumber); 449 450 // Add the field trial to shared memory. 451 if (kUseSharedMemoryForFieldTrials && trial_registered_) 452 FieldTrialList::OnGroupFinalized(is_locked, this); 453 } 454 455 bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { 456 if (!group_reported_ || !enable_field_trial_) 457 return false; 458 DCHECK_NE(group_, kNotFinalized); 459 active_group->trial_name = trial_name_; 460 active_group->group_name = group_name_; 461 return true; 462 } 463 464 bool FieldTrial::GetState(State* field_trial_state) { 465 if (!enable_field_trial_) 466 return false; 467 FinalizeGroupChoice(); 468 field_trial_state->trial_name = &trial_name_; 469 field_trial_state->group_name = &group_name_; 470 field_trial_state->activated = group_reported_; 471 return true; 472 } 473 474 bool FieldTrial::GetStateWhileLocked(State* field_trial_state) { 475 if (!enable_field_trial_) 476 return false; 477 FinalizeGroupChoiceImpl(true); 478 field_trial_state->trial_name = &trial_name_; 479 field_trial_state->group_name = &group_name_; 480 field_trial_state->activated = group_reported_; 481 return true; 482 } 483 484 //------------------------------------------------------------------------------ 485 // FieldTrialList methods and members. 486 487 // static 488 FieldTrialList* FieldTrialList::global_ = NULL; 489 490 // static 491 bool FieldTrialList::used_without_global_ = false; 492 493 FieldTrialList::Observer::~Observer() { 494 } 495 496 FieldTrialList::FieldTrialList( 497 std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider) 498 : entropy_provider_(std::move(entropy_provider)), 499 observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>( 500 ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) { 501 DCHECK(!global_); 502 DCHECK(!used_without_global_); 503 global_ = this; 504 505 Time two_years_from_build_time = GetBuildTime() + TimeDelta::FromDays(730); 506 Time::Exploded exploded; 507 two_years_from_build_time.LocalExplode(&exploded); 508 kNoExpirationYear = exploded.year; 509 } 510 511 FieldTrialList::~FieldTrialList() { 512 AutoLock auto_lock(lock_); 513 while (!registered_.empty()) { 514 RegistrationMap::iterator it = registered_.begin(); 515 it->second->Release(); 516 registered_.erase(it->first); 517 } 518 DCHECK_EQ(this, global_); 519 global_ = NULL; 520 } 521 522 // static 523 FieldTrial* FieldTrialList::FactoryGetFieldTrial( 524 const std::string& trial_name, 525 FieldTrial::Probability total_probability, 526 const std::string& default_group_name, 527 const int year, 528 const int month, 529 const int day_of_month, 530 FieldTrial::RandomizationType randomization_type, 531 int* default_group_number) { 532 return FactoryGetFieldTrialWithRandomizationSeed( 533 trial_name, total_probability, default_group_name, year, month, 534 day_of_month, randomization_type, 0, default_group_number, NULL); 535 } 536 537 // static 538 FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed( 539 const std::string& trial_name, 540 FieldTrial::Probability total_probability, 541 const std::string& default_group_name, 542 const int year, 543 const int month, 544 const int day_of_month, 545 FieldTrial::RandomizationType randomization_type, 546 uint32_t randomization_seed, 547 int* default_group_number, 548 const FieldTrial::EntropyProvider* override_entropy_provider) { 549 if (default_group_number) 550 *default_group_number = FieldTrial::kDefaultGroupNumber; 551 // Check if the field trial has already been created in some other way. 552 FieldTrial* existing_trial = Find(trial_name); 553 if (existing_trial) { 554 CHECK(existing_trial->forced_); 555 // If the default group name differs between the existing forced trial 556 // and this trial, then use a different value for the default group number. 557 if (default_group_number && 558 default_group_name != existing_trial->default_group_name()) { 559 // If the new default group number corresponds to the group that was 560 // chosen for the forced trial (which has been finalized when it was 561 // forced), then set the default group number to that. 562 if (default_group_name == existing_trial->group_name_internal()) { 563 *default_group_number = existing_trial->group_; 564 } else { 565 // Otherwise, use |kNonConflictingGroupNumber| (-2) for the default 566 // group number, so that it does not conflict with the |AppendGroup()| 567 // result for the chosen group. 568 const int kNonConflictingGroupNumber = -2; 569 static_assert( 570 kNonConflictingGroupNumber != FieldTrial::kDefaultGroupNumber, 571 "The 'non-conflicting' group number conflicts"); 572 static_assert(kNonConflictingGroupNumber != FieldTrial::kNotFinalized, 573 "The 'non-conflicting' group number conflicts"); 574 *default_group_number = kNonConflictingGroupNumber; 575 } 576 } 577 return existing_trial; 578 } 579 580 double entropy_value; 581 if (randomization_type == FieldTrial::ONE_TIME_RANDOMIZED) { 582 // If an override entropy provider is given, use it. 583 const FieldTrial::EntropyProvider* entropy_provider = 584 override_entropy_provider ? override_entropy_provider 585 : GetEntropyProviderForOneTimeRandomization(); 586 CHECK(entropy_provider); 587 entropy_value = entropy_provider->GetEntropyForTrial(trial_name, 588 randomization_seed); 589 } else { 590 DCHECK_EQ(FieldTrial::SESSION_RANDOMIZED, randomization_type); 591 DCHECK_EQ(0U, randomization_seed); 592 entropy_value = RandDouble(); 593 } 594 595 FieldTrial* field_trial = new FieldTrial(trial_name, total_probability, 596 default_group_name, entropy_value); 597 if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month)) 598 field_trial->Disable(); 599 FieldTrialList::Register(field_trial); 600 return field_trial; 601 } 602 603 // static 604 FieldTrial* FieldTrialList::Find(const std::string& trial_name) { 605 if (!global_) 606 return NULL; 607 AutoLock auto_lock(global_->lock_); 608 return global_->PreLockedFind(trial_name); 609 } 610 611 // static 612 int FieldTrialList::FindValue(const std::string& trial_name) { 613 FieldTrial* field_trial = Find(trial_name); 614 if (field_trial) 615 return field_trial->group(); 616 return FieldTrial::kNotFinalized; 617 } 618 619 // static 620 std::string FieldTrialList::FindFullName(const std::string& trial_name) { 621 FieldTrial* field_trial = Find(trial_name); 622 if (field_trial) 623 return field_trial->group_name(); 624 return std::string(); 625 } 626 627 // static 628 bool FieldTrialList::TrialExists(const std::string& trial_name) { 629 return Find(trial_name) != NULL; 630 } 631 632 // static 633 bool FieldTrialList::IsTrialActive(const std::string& trial_name) { 634 FieldTrial* field_trial = Find(trial_name); 635 FieldTrial::ActiveGroup active_group; 636 return field_trial && field_trial->GetActiveGroup(&active_group); 637 } 638 639 // static 640 void FieldTrialList::StatesToString(std::string* output) { 641 FieldTrial::ActiveGroups active_groups; 642 GetActiveFieldTrialGroups(&active_groups); 643 for (FieldTrial::ActiveGroups::const_iterator it = active_groups.begin(); 644 it != active_groups.end(); ++it) { 645 DCHECK_EQ(std::string::npos, 646 it->trial_name.find(kPersistentStringSeparator)); 647 DCHECK_EQ(std::string::npos, 648 it->group_name.find(kPersistentStringSeparator)); 649 output->append(it->trial_name); 650 output->append(1, kPersistentStringSeparator); 651 output->append(it->group_name); 652 output->append(1, kPersistentStringSeparator); 653 } 654 } 655 656 // static 657 void FieldTrialList::AllStatesToString(std::string* output) { 658 if (!global_) 659 return; 660 AutoLock auto_lock(global_->lock_); 661 662 for (const auto& registered : global_->registered_) { 663 FieldTrial::State trial; 664 if (!registered.second->GetStateWhileLocked(&trial)) 665 continue; 666 DCHECK_EQ(std::string::npos, 667 trial.trial_name->find(kPersistentStringSeparator)); 668 DCHECK_EQ(std::string::npos, 669 trial.group_name->find(kPersistentStringSeparator)); 670 if (trial.activated) 671 output->append(1, kActivationMarker); 672 output->append(*trial.trial_name); 673 output->append(1, kPersistentStringSeparator); 674 output->append(*trial.group_name); 675 output->append(1, kPersistentStringSeparator); 676 } 677 } 678 679 // static 680 void FieldTrialList::GetActiveFieldTrialGroups( 681 FieldTrial::ActiveGroups* active_groups) { 682 DCHECK(active_groups->empty()); 683 if (!global_) 684 return; 685 AutoLock auto_lock(global_->lock_); 686 687 for (RegistrationMap::iterator it = global_->registered_.begin(); 688 it != global_->registered_.end(); ++it) { 689 FieldTrial::ActiveGroup active_group; 690 if (it->second->GetActiveGroup(&active_group)) 691 active_groups->push_back(active_group); 692 } 693 } 694 695 // static 696 void FieldTrialList::GetActiveFieldTrialGroupsFromString( 697 const std::string& trials_string, 698 FieldTrial::ActiveGroups* active_groups) { 699 std::vector<FieldTrialStringEntry> entries; 700 if (!ParseFieldTrialsString(trials_string, &entries)) 701 return; 702 703 for (const auto& entry : entries) { 704 if (entry.activated) { 705 FieldTrial::ActiveGroup group; 706 group.trial_name = entry.trial_name.as_string(); 707 group.group_name = entry.group_name.as_string(); 708 active_groups->push_back(group); 709 } 710 } 711 } 712 713 // static 714 void FieldTrialList::GetInitiallyActiveFieldTrials( 715 const base::CommandLine& command_line, 716 FieldTrial::ActiveGroups* active_groups) { 717 DCHECK(global_->create_trials_from_command_line_called_); 718 719 if (!global_->field_trial_allocator_) { 720 GetActiveFieldTrialGroupsFromString( 721 command_line.GetSwitchValueASCII(switches::kForceFieldTrials), 722 active_groups); 723 return; 724 } 725 726 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); 727 FieldTrialAllocator::Iterator mem_iter(allocator); 728 const FieldTrial::FieldTrialEntry* entry; 729 while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) != 730 nullptr) { 731 StringPiece trial_name; 732 StringPiece group_name; 733 if (subtle::NoBarrier_Load(&entry->activated) && 734 entry->GetTrialAndGroupName(&trial_name, &group_name)) { 735 FieldTrial::ActiveGroup group; 736 group.trial_name = trial_name.as_string(); 737 group.group_name = group_name.as_string(); 738 active_groups->push_back(group); 739 } 740 } 741 } 742 743 // static 744 bool FieldTrialList::CreateTrialsFromString( 745 const std::string& trials_string, 746 const std::set<std::string>& ignored_trial_names) { 747 DCHECK(global_); 748 if (trials_string.empty() || !global_) 749 return true; 750 751 std::vector<FieldTrialStringEntry> entries; 752 if (!ParseFieldTrialsString(trials_string, &entries)) 753 return false; 754 755 for (const auto& entry : entries) { 756 const std::string trial_name = entry.trial_name.as_string(); 757 const std::string group_name = entry.group_name.as_string(); 758 759 if (ContainsKey(ignored_trial_names, trial_name)) 760 continue; 761 762 FieldTrial* trial = CreateFieldTrial(trial_name, group_name); 763 if (!trial) 764 return false; 765 if (entry.activated) { 766 // Call |group()| to mark the trial as "used" and notify observers, if 767 // any. This is useful to ensure that field trials created in child 768 // processes are properly reported in crash reports. 769 trial->group(); 770 } 771 } 772 return true; 773 } 774 775 // static 776 void FieldTrialList::CreateTrialsFromCommandLine( 777 const CommandLine& cmd_line, 778 const char* field_trial_handle_switch, 779 int fd_key) { 780 global_->create_trials_from_command_line_called_ = true; 781 782 #if defined(OS_WIN) 783 if (cmd_line.HasSwitch(field_trial_handle_switch)) { 784 std::string handle_switch = 785 cmd_line.GetSwitchValueASCII(field_trial_handle_switch); 786 bool result = CreateTrialsFromHandleSwitch(handle_switch); 787 DCHECK(result); 788 } 789 #endif 790 791 #if defined(OS_POSIX) && !defined(OS_NACL) 792 // On POSIX, we check if the handle is valid by seeing if the browser process 793 // sent over the switch (we don't care about the value). Invalid handles 794 // occur in some browser tests which don't initialize the allocator. 795 if (cmd_line.HasSwitch(field_trial_handle_switch)) { 796 bool result = CreateTrialsFromDescriptor(fd_key); 797 DCHECK(result); 798 } 799 #endif 800 801 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { 802 bool result = FieldTrialList::CreateTrialsFromString( 803 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), 804 std::set<std::string>()); 805 DCHECK(result); 806 } 807 } 808 809 // static 810 void FieldTrialList::CreateFeaturesFromCommandLine( 811 const base::CommandLine& command_line, 812 const char* enable_features_switch, 813 const char* disable_features_switch, 814 FeatureList* feature_list) { 815 // Fallback to command line if not using shared memory. 816 if (!kUseSharedMemoryForFieldTrials || 817 !global_->field_trial_allocator_.get()) { 818 return feature_list->InitializeFromCommandLine( 819 command_line.GetSwitchValueASCII(enable_features_switch), 820 command_line.GetSwitchValueASCII(disable_features_switch)); 821 } 822 823 feature_list->InitializeFromSharedMemory( 824 global_->field_trial_allocator_.get()); 825 } 826 827 #if defined(OS_WIN) 828 // static 829 void FieldTrialList::AppendFieldTrialHandleIfNeeded( 830 HandlesToInheritVector* handles) { 831 if (!global_) 832 return; 833 if (kUseSharedMemoryForFieldTrials) { 834 InstantiateFieldTrialAllocatorIfNeeded(); 835 if (global_->readonly_allocator_handle_) 836 handles->push_back(global_->readonly_allocator_handle_); 837 } 838 } 839 #endif 840 841 #if defined(OS_POSIX) && !defined(OS_NACL) 842 // static 843 int FieldTrialList::GetFieldTrialHandle() { 844 if (global_ && kUseSharedMemoryForFieldTrials) { 845 InstantiateFieldTrialAllocatorIfNeeded(); 846 // We check for an invalid handle where this gets called. 847 return global_->readonly_allocator_handle_; 848 } 849 return kInvalidPlatformFile; 850 } 851 #endif 852 853 // static 854 void FieldTrialList::CopyFieldTrialStateToFlags( 855 const char* field_trial_handle_switch, 856 const char* enable_features_switch, 857 const char* disable_features_switch, 858 CommandLine* cmd_line) { 859 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, 860 // content browser tests currently don't create a FieldTrialList because they 861 // don't run ChromeBrowserMainParts code where it's done for Chrome. 862 // Some tests depend on the enable and disable features flag switch, though, 863 // so we can still add those even though AllStatesToString() will be a no-op. 864 if (!global_) { 865 AddFeatureAndFieldTrialFlags(enable_features_switch, 866 disable_features_switch, cmd_line); 867 return; 868 } 869 870 // Use shared memory to pass the state if the feature is enabled, otherwise 871 // fallback to passing it via the command line as a string. 872 if (kUseSharedMemoryForFieldTrials) { 873 InstantiateFieldTrialAllocatorIfNeeded(); 874 // If the readonly handle didn't get duplicated properly, then fallback to 875 // original behavior. 876 if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) { 877 AddFeatureAndFieldTrialFlags(enable_features_switch, 878 disable_features_switch, cmd_line); 879 return; 880 } 881 882 global_->field_trial_allocator_->UpdateTrackingHistograms(); 883 884 #if defined(OS_WIN) 885 // We need to pass a named anonymous handle to shared memory over the 886 // command line on Windows, since the child doesn't know which of the 887 // handles it inherited it should open. 888 // PlatformFile is typedef'd to HANDLE which is typedef'd to void *. We 889 // basically cast the handle into an int (uintptr_t, to be exact), stringify 890 // the int, and pass it as a command-line flag. The child process will do 891 // the reverse conversions to retrieve the handle. See 892 // http://stackoverflow.com/a/153077 893 auto uintptr_handle = 894 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); 895 std::string field_trial_handle = std::to_string(uintptr_handle); 896 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); 897 #elif defined(OS_POSIX) 898 // On POSIX, we dup the fd into a fixed fd kFieldTrialDescriptor, so we 899 // don't have to pass over the handle (it's not even the right handle 900 // anyways). But some browser tests don't create the allocator, so we need 901 // to be able to distinguish valid and invalid handles. We do that by just 902 // checking that the flag is set with a dummy value. 903 cmd_line->AppendSwitchASCII(field_trial_handle_switch, "1"); 904 #else 905 #error Unsupported OS 906 #endif 907 return; 908 } 909 910 AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch, 911 cmd_line); 912 } 913 914 // static 915 FieldTrial* FieldTrialList::CreateFieldTrial( 916 const std::string& name, 917 const std::string& group_name) { 918 DCHECK(global_); 919 DCHECK_GE(name.size(), 0u); 920 DCHECK_GE(group_name.size(), 0u); 921 if (name.empty() || group_name.empty() || !global_) 922 return NULL; 923 924 FieldTrial* field_trial = FieldTrialList::Find(name); 925 if (field_trial) { 926 // In single process mode, or when we force them from the command line, 927 // we may have already created the field trial. 928 if (field_trial->group_name_internal() != group_name) 929 return NULL; 930 return field_trial; 931 } 932 const int kTotalProbability = 100; 933 field_trial = new FieldTrial(name, kTotalProbability, group_name, 0); 934 FieldTrialList::Register(field_trial); 935 // Force the trial, which will also finalize the group choice. 936 field_trial->SetForced(); 937 return field_trial; 938 } 939 940 // static 941 void FieldTrialList::AddObserver(Observer* observer) { 942 if (!global_) 943 return; 944 global_->observer_list_->AddObserver(observer); 945 } 946 947 // static 948 void FieldTrialList::RemoveObserver(Observer* observer) { 949 if (!global_) 950 return; 951 global_->observer_list_->RemoveObserver(observer); 952 } 953 954 // static 955 void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { 956 if (!global_) 957 return; 958 if (is_locked) { 959 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), 960 field_trial); 961 } else { 962 AutoLock auto_lock(global_->lock_); 963 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), 964 field_trial); 965 } 966 } 967 968 // static 969 void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { 970 if (!global_) 971 return; 972 973 { 974 AutoLock auto_lock(global_->lock_); 975 if (field_trial->group_reported_) 976 return; 977 field_trial->group_reported_ = true; 978 979 if (!field_trial->enable_field_trial_) 980 return; 981 982 if (kUseSharedMemoryForFieldTrials) 983 ActivateFieldTrialEntryWhileLocked(field_trial); 984 } 985 986 // Recording for stability debugging has to be done inline as a task posted 987 // to an observer may not get executed before a crash. 988 base::debug::GlobalActivityTracker* tracker = 989 base::debug::GlobalActivityTracker::Get(); 990 if (tracker) { 991 tracker->RecordFieldTrial(field_trial->trial_name(), 992 field_trial->group_name_internal()); 993 } 994 995 global_->observer_list_->Notify( 996 FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, 997 field_trial->trial_name(), field_trial->group_name_internal()); 998 } 999 1000 // static 1001 size_t FieldTrialList::GetFieldTrialCount() { 1002 if (!global_) 1003 return 0; 1004 AutoLock auto_lock(global_->lock_); 1005 return global_->registered_.size(); 1006 } 1007 1008 // static 1009 bool FieldTrialList::GetParamsFromSharedMemory( 1010 FieldTrial* field_trial, 1011 std::map<std::string, std::string>* params) { 1012 DCHECK(global_); 1013 // If the field trial allocator is not set up yet, then there are several 1014 // cases: 1015 // - We are in the browser process and the allocator has not been set up 1016 // yet. If we got here, then we couldn't find the params in 1017 // FieldTrialParamAssociator, so it's definitely not here. Return false. 1018 // - Using shared memory for field trials is not enabled. If we got here, 1019 // then there's nothing in shared memory. Return false. 1020 // - We are in the child process and the allocator has not been set up yet. 1021 // If this is the case, then you are calling this too early. The field trial 1022 // allocator should get set up very early in the lifecycle. Try to see if 1023 // you can call it after it's been set up. 1024 AutoLock auto_lock(global_->lock_); 1025 if (!global_->field_trial_allocator_) 1026 return false; 1027 1028 // If ref_ isn't set, then the field trial data can't be in shared memory. 1029 if (!field_trial->ref_) 1030 return false; 1031 1032 const FieldTrial::FieldTrialEntry* entry = 1033 global_->field_trial_allocator_->GetAsObject<FieldTrial::FieldTrialEntry>( 1034 field_trial->ref_); 1035 1036 size_t allocated_size = 1037 global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); 1038 size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size; 1039 if (allocated_size < actual_size) 1040 return false; 1041 1042 return entry->GetParams(params); 1043 } 1044 1045 // static 1046 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { 1047 if (!global_) 1048 return; 1049 1050 AutoLock auto_lock(global_->lock_); 1051 if (!global_->field_trial_allocator_) 1052 return; 1053 1054 // To clear the params, we iterate through every item in the allocator, copy 1055 // just the trial and group name into a newly-allocated segment and then clear 1056 // the existing item. 1057 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); 1058 FieldTrialAllocator::Iterator mem_iter(allocator); 1059 1060 // List of refs to eventually be made iterable. We can't make it in the loop, 1061 // since it would go on forever. 1062 std::vector<FieldTrial::FieldTrialRef> new_refs; 1063 1064 FieldTrial::FieldTrialRef prev_ref; 1065 while ((prev_ref = mem_iter.GetNextOfType<FieldTrial::FieldTrialEntry>()) != 1066 FieldTrialAllocator::kReferenceNull) { 1067 // Get the existing field trial entry in shared memory. 1068 const FieldTrial::FieldTrialEntry* prev_entry = 1069 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(prev_ref); 1070 StringPiece trial_name; 1071 StringPiece group_name; 1072 if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) 1073 continue; 1074 1075 // Write a new entry, minus the params. 1076 Pickle pickle; 1077 pickle.WriteString(trial_name); 1078 pickle.WriteString(group_name); 1079 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); 1080 FieldTrial::FieldTrialEntry* new_entry = 1081 allocator->New<FieldTrial::FieldTrialEntry>(total_size); 1082 subtle::NoBarrier_Store(&new_entry->activated, 1083 subtle::NoBarrier_Load(&prev_entry->activated)); 1084 new_entry->pickle_size = pickle.size(); 1085 1086 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section 1087 // in memory, so we can avoid this memcpy. 1088 char* dst = reinterpret_cast<char*>(new_entry) + 1089 sizeof(FieldTrial::FieldTrialEntry); 1090 memcpy(dst, pickle.data(), pickle.size()); 1091 1092 // Update the ref on the field trial and add it to the list to be made 1093 // iterable. 1094 FieldTrial::FieldTrialRef new_ref = allocator->GetAsReference(new_entry); 1095 FieldTrial* trial = global_->PreLockedFind(trial_name.as_string()); 1096 trial->ref_ = new_ref; 1097 new_refs.push_back(new_ref); 1098 1099 // Mark the existing entry as unused. 1100 allocator->ChangeType(prev_ref, 0, 1101 FieldTrial::FieldTrialEntry::kPersistentTypeId, 1102 /*clear=*/false); 1103 } 1104 1105 for (const auto& ref : new_refs) { 1106 allocator->MakeIterable(ref); 1107 } 1108 } 1109 1110 // static 1111 void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator( 1112 PersistentMemoryAllocator* allocator) { 1113 if (!global_) 1114 return; 1115 AutoLock auto_lock(global_->lock_); 1116 for (const auto& registered : global_->registered_) { 1117 AddToAllocatorWhileLocked(allocator, registered.second); 1118 } 1119 } 1120 1121 // static 1122 std::vector<const FieldTrial::FieldTrialEntry*> 1123 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator( 1124 PersistentMemoryAllocator const& allocator) { 1125 std::vector<const FieldTrial::FieldTrialEntry*> entries; 1126 FieldTrialAllocator::Iterator iter(&allocator); 1127 const FieldTrial::FieldTrialEntry* entry; 1128 while ((entry = iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) != 1129 nullptr) { 1130 entries.push_back(entry); 1131 } 1132 return entries; 1133 } 1134 1135 #if defined(OS_WIN) 1136 // static 1137 bool FieldTrialList::CreateTrialsFromHandleSwitch( 1138 const std::string& handle_switch) { 1139 int field_trial_handle = std::stoi(handle_switch); 1140 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); 1141 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); 1142 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); 1143 } 1144 #endif 1145 1146 #if defined(OS_POSIX) && !defined(OS_NACL) 1147 // static 1148 bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) { 1149 if (!kUseSharedMemoryForFieldTrials) 1150 return false; 1151 1152 if (fd_key == -1) 1153 return false; 1154 1155 int fd = GlobalDescriptors::GetInstance()->MaybeGet(fd_key); 1156 if (fd == -1) 1157 return false; 1158 1159 #if defined(OS_MACOSX) && !defined(OS_IOS) 1160 SharedMemoryHandle shm_handle(FileDescriptor(fd, true)); 1161 #else 1162 SharedMemoryHandle shm_handle(fd, true); 1163 #endif 1164 1165 bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); 1166 DCHECK(result); 1167 return true; 1168 } 1169 #endif 1170 1171 // static 1172 bool FieldTrialList::CreateTrialsFromSharedMemoryHandle( 1173 SharedMemoryHandle shm_handle) { 1174 // shm gets deleted when it gets out of scope, but that's OK because we need 1175 // it only for the duration of this method. 1176 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); 1177 if (!shm.get()->Map(kFieldTrialAllocationSize)) 1178 OnOutOfMemory(kFieldTrialAllocationSize); 1179 1180 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); 1181 } 1182 1183 // static 1184 bool FieldTrialList::CreateTrialsFromSharedMemory( 1185 std::unique_ptr<SharedMemory> shm) { 1186 global_->field_trial_allocator_.reset( 1187 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); 1188 FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); 1189 FieldTrialAllocator::Iterator mem_iter(shalloc); 1190 1191 const FieldTrial::FieldTrialEntry* entry; 1192 while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) != 1193 nullptr) { 1194 StringPiece trial_name; 1195 StringPiece group_name; 1196 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) 1197 return false; 1198 1199 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take 1200 // StringPieces. 1201 FieldTrial* trial = 1202 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); 1203 1204 trial->ref_ = mem_iter.GetAsReference(entry); 1205 if (subtle::NoBarrier_Load(&entry->activated)) { 1206 // Call |group()| to mark the trial as "used" and notify observers, if 1207 // any. This is useful to ensure that field trials created in child 1208 // processes are properly reported in crash reports. 1209 trial->group(); 1210 } 1211 } 1212 return true; 1213 } 1214 1215 // static 1216 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { 1217 if (!global_) 1218 return; 1219 AutoLock auto_lock(global_->lock_); 1220 // Create the allocator if not already created and add all existing trials. 1221 if (global_->field_trial_allocator_ != nullptr) 1222 return; 1223 1224 SharedMemoryCreateOptions options; 1225 options.size = kFieldTrialAllocationSize; 1226 options.share_read_only = true; 1227 #if defined(OS_MACOSX) && !defined(OS_IOS) 1228 options.type = SharedMemoryHandle::POSIX; 1229 #endif 1230 1231 std::unique_ptr<SharedMemory> shm(new SharedMemory()); 1232 if (!shm->Create(options)) 1233 OnOutOfMemory(kFieldTrialAllocationSize); 1234 1235 if (!shm->Map(kFieldTrialAllocationSize)) 1236 OnOutOfMemory(kFieldTrialAllocationSize); 1237 1238 global_->field_trial_allocator_.reset( 1239 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); 1240 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); 1241 1242 // Add all existing field trials. 1243 for (const auto& registered : global_->registered_) { 1244 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), 1245 registered.second); 1246 } 1247 1248 // Add all existing features. 1249 FeatureList::GetInstance()->AddFeaturesToAllocator( 1250 global_->field_trial_allocator_.get()); 1251 1252 #if !defined(OS_NACL) 1253 // Set |readonly_allocator_handle_| so we can pass it to be inherited and 1254 // via the command line. 1255 global_->readonly_allocator_handle_ = 1256 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); 1257 #endif 1258 } 1259 1260 // static 1261 void FieldTrialList::AddToAllocatorWhileLocked( 1262 PersistentMemoryAllocator* allocator, 1263 FieldTrial* field_trial) { 1264 // Don't do anything if the allocator hasn't been instantiated yet. 1265 if (allocator == nullptr) 1266 return; 1267 1268 // Or if the allocator is read only, which means we are in a child process and 1269 // shouldn't be writing to it. 1270 if (allocator->IsReadonly()) 1271 return; 1272 1273 FieldTrial::State trial_state; 1274 if (!field_trial->GetStateWhileLocked(&trial_state)) 1275 return; 1276 1277 // Or if we've already added it. We must check after GetState since it can 1278 // also add to the allocator. 1279 if (field_trial->ref_) 1280 return; 1281 1282 Pickle pickle; 1283 if (!PickleFieldTrial(trial_state, &pickle)) { 1284 NOTREACHED(); 1285 return; 1286 } 1287 1288 size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); 1289 FieldTrial::FieldTrialRef ref = allocator->Allocate( 1290 total_size, FieldTrial::FieldTrialEntry::kPersistentTypeId); 1291 if (ref == FieldTrialAllocator::kReferenceNull) { 1292 NOTREACHED(); 1293 return; 1294 } 1295 1296 FieldTrial::FieldTrialEntry* entry = 1297 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref); 1298 subtle::NoBarrier_Store(&entry->activated, trial_state.activated); 1299 entry->pickle_size = pickle.size(); 1300 1301 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in 1302 // memory, so we can avoid this memcpy. 1303 char* dst = 1304 reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry); 1305 memcpy(dst, pickle.data(), pickle.size()); 1306 1307 allocator->MakeIterable(ref); 1308 field_trial->ref_ = ref; 1309 } 1310 1311 // static 1312 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( 1313 FieldTrial* field_trial) { 1314 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); 1315 1316 // Check if we're in the child process and return early if so. 1317 if (allocator && allocator->IsReadonly()) 1318 return; 1319 1320 FieldTrial::FieldTrialRef ref = field_trial->ref_; 1321 if (ref == FieldTrialAllocator::kReferenceNull) { 1322 // It's fine to do this even if the allocator hasn't been instantiated 1323 // yet -- it'll just return early. 1324 AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), 1325 field_trial); 1326 } else { 1327 // 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 1329 // hit from the child re-synchronizing activation state. 1330 FieldTrial::FieldTrialEntry* entry = 1331 allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref); 1332 subtle::NoBarrier_Store(&entry->activated, 1); 1333 } 1334 } 1335 1336 // static 1337 const FieldTrial::EntropyProvider* 1338 FieldTrialList::GetEntropyProviderForOneTimeRandomization() { 1339 if (!global_) { 1340 used_without_global_ = true; 1341 return NULL; 1342 } 1343 1344 return global_->entropy_provider_.get(); 1345 } 1346 1347 FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { 1348 RegistrationMap::iterator it = registered_.find(name); 1349 if (registered_.end() == it) 1350 return NULL; 1351 return it->second; 1352 } 1353 1354 // static 1355 void FieldTrialList::Register(FieldTrial* trial) { 1356 if (!global_) { 1357 used_without_global_ = true; 1358 return; 1359 } 1360 AutoLock auto_lock(global_->lock_); 1361 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1362 trial->AddRef(); 1363 trial->SetTrialRegistered(); 1364 global_->registered_[trial->trial_name()] = trial; 1365 } 1366 1367 } // namespace base 1368