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