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

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

Issue 2463223002: Store field trial parameters in shared memory (Closed)
Patch Set: fix tests Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/metrics/field_trial.h" 5 #include "base/metrics/field_trial.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/base_switches.h" 10 #include "base/base_switches.h"
11 #include "base/build_time.h" 11 #include "base/build_time.h"
12 #include "base/command_line.h" 12 #include "base/command_line.h"
13 #include "base/debug/alias.h" 13 #include "base/debug/alias.h"
14 #include "base/feature_list.h" 14 #include "base/feature_list.h"
15 #include "base/logging.h" 15 #include "base/logging.h"
16 #include "base/metrics/field_trial_param_associator.h"
16 #include "base/pickle.h" 17 #include "base/pickle.h"
17 #include "base/process/memory.h" 18 #include "base/process/memory.h"
18 #include "base/rand_util.h" 19 #include "base/rand_util.h"
19 #include "base/strings/string_number_conversions.h" 20 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h" 21 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h" 22 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h" 23 #include "base/strings/utf_string_conversions.h"
23 24
24 namespace base { 25 namespace base {
25 26
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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 &params);
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698