Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/metrics/field_trial.h" | 5 #include "base/metrics/field_trial.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/base_switches.h" | 10 #include "base/base_switches.h" |
| 11 #include "base/build_time.h" | 11 #include "base/build_time.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/debug/alias.h" | 13 #include "base/debug/alias.h" |
| 14 #include "base/feature_list.h" | 14 #include "base/feature_list.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/metrics/field_trial_param_associator.h" | |
| 16 #include "base/pickle.h" | 17 #include "base/pickle.h" |
| 17 #include "base/process/memory.h" | 18 #include "base/process/memory.h" |
| 18 #include "base/rand_util.h" | 19 #include "base/rand_util.h" |
| 19 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
| 21 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
| 22 #include "base/strings/utf_string_conversions.h" | 23 #include "base/strings/utf_string_conversions.h" |
| 23 | 24 |
| 24 namespace base { | 25 namespace base { |
| 25 | 26 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 61 // object that we unpickle and read from. Any changes to this structure requires | 62 // object that we unpickle and read from. Any changes to this structure requires |
| 62 // a bump in kFieldTrialType id defined above. | 63 // a bump in kFieldTrialType id defined above. |
| 63 struct FieldTrialEntry { | 64 struct FieldTrialEntry { |
| 64 // Whether or not this field trial is activated. This is really just a boolean | 65 // Whether or not this field trial is activated. This is really just a boolean |
| 65 // but marked as a uint32_t for portability reasons. | 66 // but marked as a uint32_t for portability reasons. |
| 66 uint32_t activated; | 67 uint32_t activated; |
| 67 | 68 |
| 68 // Size of the pickled structure, NOT the total size of this entry. | 69 // Size of the pickled structure, NOT the total size of this entry. |
| 69 uint32_t size; | 70 uint32_t size; |
| 70 | 71 |
| 72 // Returns an iterator over the data containing names and params. | |
| 73 PickleIterator GetPickleIterator() const { | |
| 74 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) + | |
| 75 sizeof(FieldTrialEntry); | |
| 76 | |
| 77 Pickle pickle(src, size); | |
| 78 return PickleIterator(pickle); | |
| 79 } | |
| 80 | |
| 81 // Takes the iterator and writes out the first two items into |trial_name| and | |
| 82 // |group_name|. | |
| 83 bool ReadStringPair(PickleIterator iter, | |
| 84 StringPiece* trial_name, | |
| 85 StringPiece* group_name) const { | |
| 86 if (!iter.ReadStringPiece(trial_name)) | |
| 87 return false; | |
| 88 if (!iter.ReadStringPiece(group_name)) | |
| 89 return false; | |
| 90 return true; | |
| 91 } | |
| 92 | |
| 71 // Calling this is only valid when the entry is initialized. That is, it | 93 // Calling this is only valid when the entry is initialized. That is, it |
| 72 // resides in shared memory and has a pickle containing the trial name and | 94 // resides in shared memory and has a pickle containing the trial name and |
| 73 // group name following it. | 95 // group name following it. |
| 74 bool GetTrialAndGroupName(StringPiece* trial_name, | 96 bool GetTrialAndGroupName(StringPiece* trial_name, |
| 75 StringPiece* group_name) const { | 97 StringPiece* group_name) const { |
| 76 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) + | 98 return ReadStringPair(GetPickleIterator(), trial_name, group_name); |
| 77 sizeof(FieldTrialEntry); | 99 } |
| 78 | 100 |
| 79 Pickle pickle(src, size); | 101 // Calling this is only valid when the entry is initialized as well. Reads the |
| 80 PickleIterator pickle_iter(pickle); | 102 // parameters following the trial and group name and stores them as key-value |
| 103 // mappings in |params|. | |
| 104 bool GetParams(std::map<std::string, std::string>* params) const { | |
| 105 PickleIterator iter = GetPickleIterator(); | |
| 106 StringPiece tmp; | |
| 107 if (!ReadStringPair(iter, &tmp, &tmp)) | |
|
Alexei Svitkine (slow)
2016/11/22 17:46:37
You're passing iter by value - which means it gets
lawrencewu
2016/11/22 20:25:44
Done.
| |
| 108 return false; | |
| 81 | 109 |
| 82 if (!pickle_iter.ReadStringPiece(trial_name)) | 110 while (true) { |
| 83 return false; | 111 StringPiece key; |
| 84 if (!pickle_iter.ReadStringPiece(group_name)) | 112 StringPiece value; |
| 85 return false; | 113 if (!iter.ReadStringPiece(&key)) |
| 86 return true; | 114 return true; // No more params to read. |
| 115 if (!iter.ReadStringPiece(&value)) | |
| 116 return false; | |
| 117 (*params)[key.as_string()] = value.as_string(); | |
| 118 } | |
| 87 } | 119 } |
| 88 }; | 120 }; |
| 89 | 121 |
| 90 // Created a time value based on |year|, |month| and |day_of_month| parameters. | 122 // Created a time value based on |year|, |month| and |day_of_month| parameters. |
| 91 Time CreateTimeFromParams(int year, int month, int day_of_month) { | 123 Time CreateTimeFromParams(int year, int month, int day_of_month) { |
| 92 DCHECK_GT(year, 1970); | 124 DCHECK_GT(year, 1970); |
| 93 DCHECK_GT(month, 0); | 125 DCHECK_GT(month, 0); |
| 94 DCHECK_LT(month, 13); | 126 DCHECK_LT(month, 13); |
| 95 DCHECK_GT(day_of_month, 0); | 127 DCHECK_GT(day_of_month, 0); |
| 96 DCHECK_LT(day_of_month, 32); | 128 DCHECK_LT(day_of_month, 32); |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 218 HANDLE src = allocator->shared_memory()->handle().GetHandle(); |
| 187 ProcessHandle process = GetCurrentProcess(); | 219 ProcessHandle process = GetCurrentProcess(); |
| 188 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 220 DWORD access = SECTION_MAP_READ | SECTION_QUERY; |
| 189 HANDLE dst; | 221 HANDLE dst; |
| 190 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 222 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) |
| 191 return nullptr; | 223 return nullptr; |
| 192 return dst; | 224 return dst; |
| 193 } | 225 } |
| 194 #endif | 226 #endif |
| 195 | 227 |
| 228 bool PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) { | |
|
Alexei Svitkine (slow)
2016/11/22 17:46:37
Add a comment.
lawrencewu
2016/11/22 20:25:44
Done.
| |
| 229 if (!pickle->WriteString(trial_state.trial_name)) | |
| 230 return false; | |
| 231 if (!pickle->WriteString(trial_state.group_name)) | |
| 232 return false; | |
| 233 | |
| 234 // Get field trial params. | |
| 235 std::map<std::string, std::string> params; | |
| 236 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback( | |
| 237 trial_state.trial_name.as_string(), trial_state.group_name.as_string(), | |
| 238 ¶ms); | |
| 239 | |
| 240 // Write params to pickle. | |
| 241 for (const auto& param : params) { | |
| 242 pickle->WriteString(StringPiece(param.first)); | |
| 243 pickle->WriteString(StringPiece(param.second)); | |
| 244 } | |
| 245 return true; | |
| 246 } | |
|
Alexei Svitkine (slow)
2016/11/22 17:46:37
Can you put this function right underneath struct
lawrencewu
2016/11/22 20:25:44
Done.
| |
| 247 | |
| 196 } // namespace | 248 } // namespace |
| 197 | 249 |
| 198 // statics | 250 // statics |
| 199 const int FieldTrial::kNotFinalized = -1; | 251 const int FieldTrial::kNotFinalized = -1; |
| 200 const int FieldTrial::kDefaultGroupNumber = 0; | 252 const int FieldTrial::kDefaultGroupNumber = 0; |
| 201 bool FieldTrial::enable_benchmarking_ = false; | 253 bool FieldTrial::enable_benchmarking_ = false; |
| 202 | 254 |
| 203 int FieldTrialList::kNoExpirationYear = 0; | 255 int FieldTrialList::kNoExpirationYear = 0; |
| 204 | 256 |
| 205 //------------------------------------------------------------------------------ | 257 //------------------------------------------------------------------------------ |
| (...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 532 | 584 |
| 533 // static | 585 // static |
| 534 std::string FieldTrialList::FindFullName(const std::string& trial_name) { | 586 std::string FieldTrialList::FindFullName(const std::string& trial_name) { |
| 535 FieldTrial* field_trial = Find(trial_name); | 587 FieldTrial* field_trial = Find(trial_name); |
| 536 if (field_trial) | 588 if (field_trial) |
| 537 return field_trial->group_name(); | 589 return field_trial->group_name(); |
| 538 return std::string(); | 590 return std::string(); |
| 539 } | 591 } |
| 540 | 592 |
| 541 // static | 593 // static |
| 594 bool FieldTrialList::GetParamsFromSharedMemory( | |
| 595 FieldTrial* field_trial, | |
| 596 std::map<std::string, std::string>* params) { | |
| 597 DCHECK(global_); | |
| 598 // If the field trial allocator is not set up yet, then we are in the browser | |
| 599 // process, which should have all the parameters. So if we got here, then we | |
|
Alexei Svitkine (slow)
2016/11/22 17:46:37
The first part of the comment is not the only reas
lawrencewu
2016/11/22 20:25:44
Done.
| |
| 600 // couldn't find the params in field_trial_param_associator, so it's | |
| 601 // definitely not here. Return false. | |
| 602 if (!global_->field_trial_allocator_) | |
| 603 return false; | |
| 604 | |
| 605 SharedPersistentMemoryAllocator::Reference ref = field_trial->ref_; | |
|
Alexei Svitkine (slow)
2016/11/22 17:46:37
Nit: I'm not sure the local var is needed here - c
lawrencewu
2016/11/22 20:25:44
Done.
| |
| 606 if (!ref) | |
|
Alexei Svitkine (slow)
2016/11/22 17:46:37
How about a comment about when this wouldn't be se
lawrencewu
2016/11/22 20:25:44
Done.
| |
| 607 return false; | |
| 608 | |
| 609 const FieldTrialEntry* entry = | |
| 610 global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>( | |
| 611 ref, kFieldTrialType); | |
| 612 return entry->GetParams(params); | |
| 613 } | |
| 614 | |
| 615 // static | |
| 616 void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { | |
| 617 if (!global_ || !global_->field_trial_allocator_) | |
| 618 return; | |
| 619 // To clear the params, we iterate through every item in the allocator, copy | |
| 620 // just the trial and group name into a newly-allocated segment and then clear | |
| 621 // the existing item. | |
|
Alexei Svitkine (slow)
2016/11/22 17:46:37
This seems like a lot of work to do. I'm not sure
lawrencewu
2016/11/22 20:25:44
Although that's possible, and would make this unne
| |
| 622 SharedPersistentMemoryAllocator* allocator = | |
| 623 global_->field_trial_allocator_.get(); | |
| 624 PersistentMemoryAllocator::Iterator mem_iter(allocator); | |
| 625 | |
| 626 SharedPersistentMemoryAllocator::Reference prev_ref; | |
| 627 while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) != | |
| 628 SharedPersistentMemoryAllocator::kReferenceNull) { | |
| 629 // Get the existing field trial entry in shared memory. | |
| 630 const FieldTrialEntry* prev_entry = | |
| 631 allocator->GetAsObject<const FieldTrialEntry>(prev_ref, | |
| 632 kFieldTrialType); | |
| 633 StringPiece trial_name; | |
| 634 StringPiece group_name; | |
| 635 prev_entry->GetTrialAndGroupName(&trial_name, &group_name); | |
| 636 | |
| 637 // Write a new entry, minus the params. | |
| 638 Pickle pickle; | |
| 639 pickle.WriteString(trial_name); | |
| 640 pickle.WriteString(group_name); | |
| 641 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); | |
| 642 SharedPersistentMemoryAllocator::Reference new_ref = | |
| 643 allocator->Allocate(total_size, kFieldTrialType); | |
| 644 FieldTrialEntry* new_entry = | |
| 645 allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType); | |
| 646 new_entry->activated = prev_entry->activated; | |
| 647 new_entry->size = pickle.size(); | |
| 648 | |
| 649 // Update the ref on the field trial. | |
| 650 FieldTrial* trial = Find(trial_name.as_string()); | |
| 651 trial->ref_ = new_ref; | |
| 652 | |
| 653 // Mark the existing entry as unused. | |
| 654 uint32_t oldType = allocator->GetType(prev_ref); | |
| 655 allocator->ChangeType(prev_ref, 0, oldType); | |
| 656 } | |
| 657 } | |
| 658 | |
| 659 // static | |
| 542 bool FieldTrialList::TrialExists(const std::string& trial_name) { | 660 bool FieldTrialList::TrialExists(const std::string& trial_name) { |
| 543 return Find(trial_name) != NULL; | 661 return Find(trial_name) != NULL; |
| 544 } | 662 } |
| 545 | 663 |
| 546 // static | 664 // static |
| 547 bool FieldTrialList::IsTrialActive(const std::string& trial_name) { | 665 bool FieldTrialList::IsTrialActive(const std::string& trial_name) { |
| 548 FieldTrial* field_trial = Find(trial_name); | 666 FieldTrial* field_trial = Find(trial_name); |
| 549 FieldTrial::ActiveGroup active_group; | 667 FieldTrial::ActiveGroup active_group; |
| 550 return field_trial && field_trial->GetActiveGroup(&active_group); | 668 return field_trial && field_trial->GetActiveGroup(&active_group); |
| 551 } | 669 } |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 931 FieldTrial::State trial_state; | 1049 FieldTrial::State trial_state; |
| 932 if (!field_trial->GetStateWhileLocked(&trial_state)) | 1050 if (!field_trial->GetStateWhileLocked(&trial_state)) |
| 933 return; | 1051 return; |
| 934 | 1052 |
| 935 // Or if we've already added it. We must check after GetState since it can | 1053 // Or if we've already added it. We must check after GetState since it can |
| 936 // also add to the allocator. | 1054 // also add to the allocator. |
| 937 if (field_trial->ref_) | 1055 if (field_trial->ref_) |
| 938 return; | 1056 return; |
| 939 | 1057 |
| 940 Pickle pickle; | 1058 Pickle pickle; |
| 941 pickle.WriteString(trial_state.trial_name); | 1059 if (!PickleFieldTrial(trial_state, &pickle)) |
| 942 pickle.WriteString(trial_state.group_name); | 1060 return; |
| 943 | 1061 |
| 944 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); | 1062 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); |
| 945 FieldTrial::FieldTrialRef ref = | 1063 FieldTrial::FieldTrialRef ref = |
| 946 allocator->Allocate(total_size, kFieldTrialType); | 1064 allocator->Allocate(total_size, kFieldTrialType); |
| 947 if (ref == FieldTrialAllocator::kReferenceNull) | 1065 if (ref == FieldTrialAllocator::kReferenceNull) |
| 948 return; | 1066 return; |
| 949 | 1067 |
| 950 FieldTrialEntry* entry = | 1068 FieldTrialEntry* entry = |
| 951 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 1069 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
| 952 entry->activated = trial_state.activated; | 1070 entry->activated = trial_state.activated; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1010 return; | 1128 return; |
| 1011 } | 1129 } |
| 1012 AutoLock auto_lock(global_->lock_); | 1130 AutoLock auto_lock(global_->lock_); |
| 1013 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1131 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 1014 trial->AddRef(); | 1132 trial->AddRef(); |
| 1015 trial->SetTrialRegistered(); | 1133 trial->SetTrialRegistered(); |
| 1016 global_->registered_[trial->trial_name()] = trial; | 1134 global_->registered_[trial->trial_name()] = trial; |
| 1017 } | 1135 } |
| 1018 | 1136 |
| 1019 } // namespace base | 1137 } // namespace base |
| OLD | NEW |