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

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

Issue 2501283002: Refactor field_trial.cc (Closed)
Patch Set: git rebase-update 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
« no previous file with comments | « base/metrics/field_trial.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/metrics/field_trial.h" 5 #include "base/metrics/field_trial.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/base_switches.h" 10 #include "base/base_switches.h"
(...skipping 20 matching lines...) Expand all
31 const char kPersistentStringSeparator = '/'; // Currently a slash. 31 const char kPersistentStringSeparator = '/'; // Currently a slash.
32 32
33 // Define a marker character to be used as a prefix to a trial name on the 33 // Define a marker character to be used as a prefix to a trial name on the
34 // command line which forces its activation. 34 // command line which forces its activation.
35 const char kActivationMarker = '*'; 35 const char kActivationMarker = '*';
36 36
37 // Use shared memory to communicate field trial (experiment) state. Set to false 37 // Use shared memory to communicate field trial (experiment) state. Set to false
38 // for now while the implementation is fleshed out (e.g. data format, single 38 // for now while the implementation is fleshed out (e.g. data format, single
39 // shared memory segment). See https://codereview.chromium.org/2365273004/ and 39 // shared memory segment). See https://codereview.chromium.org/2365273004/ and
40 // crbug.com/653874 40 // crbug.com/653874
41 const bool kUseSharedMemoryForFieldTrials = false; 41 const bool kUseSharedMemoryForFieldTrials = true;
42 42
43 // Constants for the field trial allocator. 43 // Constants for the field trial allocator.
44 const char kAllocatorName[] = "FieldTrialAllocator"; 44 const char kAllocatorName[] = "FieldTrialAllocator";
45 const uint32_t kFieldTrialType = 0xABA17E13 + 2; // SHA1(FieldTrialEntry) v2 45 const uint32_t kFieldTrialType = 0xABA17E13 + 2; // SHA1(FieldTrialEntry) v2
46 46
47 // We allocate 64 KiB to hold all the field trial data. This should be enough, 47 // We allocate 64 KiB to hold all the field trial data. This should be enough,
48 // as currently we use ~8KiB for the field trials, and ~10KiB for experiment 48 // as currently we use ~8KiB for the field trials, and ~10KiB for experiment
49 // parameters (as of 9/11/2016). This also doesn't allocate all 64 KiB at once 49 // parameters (as of 9/11/2016). This also doesn't allocate all 64 KiB at once
50 // -- the pages only get mapped to physical memory when they are touched. If the 50 // -- the pages only get mapped to physical memory when they are touched. If the
51 // size of the allocated field trials does get larger than 64 KiB, then we will 51 // size of the allocated field trials does get larger than 64 KiB, then we will
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 void AddForceFieldTrialsFlag(CommandLine* cmd_line) { 175 void AddForceFieldTrialsFlag(CommandLine* cmd_line) {
176 std::string field_trial_states; 176 std::string field_trial_states;
177 FieldTrialList::AllStatesToString(&field_trial_states); 177 FieldTrialList::AllStatesToString(&field_trial_states);
178 if (!field_trial_states.empty()) { 178 if (!field_trial_states.empty()) {
179 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, 179 cmd_line->AppendSwitchASCII(switches::kForceFieldTrials,
180 field_trial_states); 180 field_trial_states);
181 } 181 }
182 } 182 }
183 183
184 #if defined(OS_WIN) 184 #if defined(OS_WIN)
185 HANDLE CreateReadOnlyHandle(SharedPersistentMemoryAllocator* allocator) { 185 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) {
186 HANDLE src = allocator->shared_memory()->handle().GetHandle(); 186 HANDLE src = allocator->shared_memory()->handle().GetHandle();
187 ProcessHandle process = GetCurrentProcess(); 187 ProcessHandle process = GetCurrentProcess();
188 DWORD access = SECTION_MAP_READ | SECTION_QUERY; 188 DWORD access = SECTION_MAP_READ | SECTION_QUERY;
189 HANDLE dst; 189 HANDLE dst;
190 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) 190 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0))
191 return nullptr; 191 return nullptr;
192 return dst; 192 return dst;
193 } 193 }
194 #endif 194 #endif
195 195
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 divisor_(total_probability), 318 divisor_(total_probability),
319 default_group_name_(default_group_name), 319 default_group_name_(default_group_name),
320 random_(GetGroupBoundaryValue(total_probability, entropy_value)), 320 random_(GetGroupBoundaryValue(total_probability, entropy_value)),
321 accumulated_group_probability_(0), 321 accumulated_group_probability_(0),
322 next_group_number_(kDefaultGroupNumber + 1), 322 next_group_number_(kDefaultGroupNumber + 1),
323 group_(kNotFinalized), 323 group_(kNotFinalized),
324 enable_field_trial_(true), 324 enable_field_trial_(true),
325 forced_(false), 325 forced_(false),
326 group_reported_(false), 326 group_reported_(false),
327 trial_registered_(false), 327 trial_registered_(false),
328 ref_(SharedPersistentMemoryAllocator::kReferenceNull) { 328 ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull) {
329 DCHECK_GT(total_probability, 0); 329 DCHECK_GT(total_probability, 0);
330 DCHECK(!trial_name_.empty()); 330 DCHECK(!trial_name_.empty());
331 DCHECK(!default_group_name_.empty()); 331 DCHECK(!default_group_name_.empty());
332 } 332 }
333 333
334 FieldTrial::~FieldTrial() {} 334 FieldTrial::~FieldTrial() {}
335 335
336 void FieldTrial::SetTrialRegistered() { 336 void FieldTrial::SetTrialRegistered() {
337 DCHECK_EQ(kNotFinalized, group_); 337 DCHECK_EQ(kNotFinalized, group_);
338 DCHECK(!trial_registered_); 338 DCHECK(!trial_registered_);
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after
658 658
659 // static 659 // static
660 void FieldTrialList::CreateTrialsFromCommandLine( 660 void FieldTrialList::CreateTrialsFromCommandLine(
661 const CommandLine& cmd_line, 661 const CommandLine& cmd_line,
662 const char* field_trial_handle_switch) { 662 const char* field_trial_handle_switch) {
663 DCHECK(global_); 663 DCHECK(global_);
664 664
665 #if defined(OS_WIN) && !defined(OS_NACL) 665 #if defined(OS_WIN) && !defined(OS_NACL)
666 if (cmd_line.HasSwitch(field_trial_handle_switch)) { 666 if (cmd_line.HasSwitch(field_trial_handle_switch)) {
667 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); 667 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
668 size_t token = arg.find(","); 668 int field_trial_handle = std::stoi(arg);
669 int field_trial_handle = std::stoi(arg.substr(0, token));
670 size_t field_trial_length = std::stoi(arg.substr(token + 1, arg.length()));
671
672 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); 669 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
673 SharedMemoryHandle shm_handle = 670 bool result = CreateTrialsFromWindowsHandle(handle);
674 SharedMemoryHandle(handle, GetCurrentProcId());
675
676 // Gets deleted when it gets out of scope, but that's OK because we need it
677 // only for the duration of this method.
678 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
679 if (!shm.get()->Map(field_trial_length))
680 TerminateBecauseOutOfMemory(field_trial_length);
681
682 bool result = FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
683 DCHECK(result); 671 DCHECK(result);
684 return;
685 } 672 }
686 #endif 673 #endif
687 674
688 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { 675 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) {
689 bool result = FieldTrialList::CreateTrialsFromString( 676 bool result = FieldTrialList::CreateTrialsFromString(
690 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), 677 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials),
691 std::set<std::string>()); 678 std::set<std::string>());
692 DCHECK(result); 679 DCHECK(result);
693 } 680 }
694 } 681 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
728 AddForceFieldTrialsFlag(cmd_line); 715 AddForceFieldTrialsFlag(cmd_line);
729 return; 716 return;
730 } 717 }
731 718
732 // HANDLE is just typedef'd to void *. We basically cast the handle into an 719 // HANDLE is just typedef'd to void *. We basically cast the handle into an
733 // int (uintptr_t, to be exact), stringify the int, and pass it as a 720 // int (uintptr_t, to be exact), stringify the int, and pass it as a
734 // command-line flag. The child process will do the reverse conversions to 721 // command-line flag. The child process will do the reverse conversions to
735 // retrieve the handle. See http://stackoverflow.com/a/153077 722 // retrieve the handle. See http://stackoverflow.com/a/153077
736 auto uintptr_handle = 723 auto uintptr_handle =
737 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); 724 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_);
738 size_t field_trial_length = 725 std::string field_trial_handle = std::to_string(uintptr_handle);
739 global_->field_trial_allocator_->shared_memory()->mapped_size();
740 std::string field_trial_handle = std::to_string(uintptr_handle) + "," +
741 std::to_string(field_trial_length);
742
743 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); 726 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle);
744 global_->field_trial_allocator_->UpdateTrackingHistograms(); 727 global_->field_trial_allocator_->UpdateTrackingHistograms();
745 return; 728 return;
746 } 729 }
747 #endif 730 #endif
748 731
749 AddForceFieldTrialsFlag(cmd_line); 732 AddForceFieldTrialsFlag(cmd_line);
750 } 733 }
751 734
752 // static 735 // static
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
825 } 808 }
826 809
827 // static 810 // static
828 size_t FieldTrialList::GetFieldTrialCount() { 811 size_t FieldTrialList::GetFieldTrialCount() {
829 if (!global_) 812 if (!global_)
830 return 0; 813 return 0;
831 AutoLock auto_lock(global_->lock_); 814 AutoLock auto_lock(global_->lock_);
832 return global_->registered_.size(); 815 return global_->registered_.size();
833 } 816 }
834 817
818 #if defined(OS_WIN)
819 // static
820 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) {
821 SharedMemoryHandle shm_handle(handle, GetCurrentProcId());
822
823 // shm gets deleted when it gets out of scope, but that's OK because we need
824 // it only for the duration of this method.
825 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
826 if (!shm.get()->Map(kFieldTrialAllocationSize))
827 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
828
829 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
830 }
831 #endif
832
835 // static 833 // static
836 bool FieldTrialList::CreateTrialsFromSharedMemory( 834 bool FieldTrialList::CreateTrialsFromSharedMemory(
837 std::unique_ptr<SharedMemory> shm) { 835 std::unique_ptr<SharedMemory> shm) {
838 global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( 836 global_->field_trial_allocator_.reset(
839 std::move(shm), 0, kAllocatorName, true)); 837 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true));
840 SharedPersistentMemoryAllocator* shalloc = 838 FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get();
841 global_->field_trial_allocator_.get(); 839 FieldTrialAllocator::Iterator mem_iter(shalloc);
842 PersistentMemoryAllocator::Iterator mem_iter(shalloc);
843 840
844 SharedPersistentMemoryAllocator::Reference ref; 841 FieldTrial::FieldTrialRef ref;
845 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != 842 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) !=
846 SharedPersistentMemoryAllocator::kReferenceNull) { 843 FieldTrialAllocator::kReferenceNull) {
847 const FieldTrialEntry* entry = 844 const FieldTrialEntry* entry =
848 shalloc->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); 845 shalloc->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType);
849 846
850 StringPiece trial_name; 847 StringPiece trial_name;
851 StringPiece group_name; 848 StringPiece group_name;
852 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) 849 if (!entry->GetTrialAndGroupName(&trial_name, &group_name))
853 return false; 850 return false;
854 851
855 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take 852 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take
856 // StringPieces. 853 // StringPieces.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
893 return; 890 return;
894 AutoLock auto_lock(global_->lock_); 891 AutoLock auto_lock(global_->lock_);
895 // Create the allocator if not already created and add all existing trials. 892 // Create the allocator if not already created and add all existing trials.
896 if (global_->field_trial_allocator_ != nullptr) 893 if (global_->field_trial_allocator_ != nullptr)
897 return; 894 return;
898 895
899 std::unique_ptr<SharedMemory> shm(new SharedMemory()); 896 std::unique_ptr<SharedMemory> shm(new SharedMemory());
900 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) 897 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize))
901 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); 898 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
902 899
903 global_->field_trial_allocator_.reset(new SharedPersistentMemoryAllocator( 900 global_->field_trial_allocator_.reset(
904 std::move(shm), 0, kAllocatorName, false)); 901 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false));
905 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); 902 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName);
906 903
907 // Add all existing field trials. 904 // Add all existing field trials.
908 for (const auto& registered : global_->registered_) { 905 for (const auto& registered : global_->registered_) {
909 AddToAllocatorWhileLocked(registered.second); 906 AddToAllocatorWhileLocked(registered.second);
910 } 907 }
911 908
912 #if defined(OS_WIN) 909 #if defined(OS_WIN)
913 // Set |readonly_allocator_handle_| so we can pass it to be inherited and 910 // Set |readonly_allocator_handle_| so we can pass it to be inherited and
914 // via the command line. 911 // via the command line.
915 global_->readonly_allocator_handle_ = 912 global_->readonly_allocator_handle_ =
916 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); 913 CreateReadOnlyHandle(global_->field_trial_allocator_.get());
917 #endif 914 #endif
918 } 915 }
919 #endif 916 #endif
920 917
921 // static 918 // static
922 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { 919 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) {
923 SharedPersistentMemoryAllocator* allocator = 920 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
924 global_->field_trial_allocator_.get();
925 921
926 // Don't do anything if the allocator hasn't been instantiated yet. 922 // Don't do anything if the allocator hasn't been instantiated yet.
927 if (allocator == nullptr) 923 if (allocator == nullptr)
928 return; 924 return;
929 925
930 // Or if the allocator is read only, which means we are in a child process and 926 // Or if the allocator is read only, which means we are in a child process and
931 // shouldn't be writing to it. 927 // shouldn't be writing to it.
932 if (allocator->IsReadonly()) 928 if (allocator->IsReadonly())
933 return; 929 return;
934 930
935 FieldTrial::State trial_state; 931 FieldTrial::State trial_state;
936 if (!field_trial->GetStateWhileLocked(&trial_state)) 932 if (!field_trial->GetStateWhileLocked(&trial_state))
937 return; 933 return;
938 934
939 // Or if we've already added it. We must check after GetState since it can 935 // Or if we've already added it. We must check after GetState since it can
940 // also add to the allocator. 936 // also add to the allocator.
941 if (field_trial->ref_) 937 if (field_trial->ref_)
942 return; 938 return;
943 939
944 Pickle pickle; 940 Pickle pickle;
945 pickle.WriteString(trial_state.trial_name); 941 pickle.WriteString(trial_state.trial_name);
946 pickle.WriteString(trial_state.group_name); 942 pickle.WriteString(trial_state.group_name);
947 943
948 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); 944 size_t total_size = sizeof(FieldTrialEntry) + pickle.size();
949 SharedPersistentMemoryAllocator::Reference ref = 945 FieldTrial::FieldTrialRef ref =
950 allocator->Allocate(total_size, kFieldTrialType); 946 allocator->Allocate(total_size, kFieldTrialType);
951 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) 947 if (ref == FieldTrialAllocator::kReferenceNull)
952 return; 948 return;
953 949
954 FieldTrialEntry* entry = 950 FieldTrialEntry* entry =
955 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); 951 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType);
956 entry->activated = trial_state.activated; 952 entry->activated = trial_state.activated;
957 entry->size = pickle.size(); 953 entry->size = pickle.size();
958 954
959 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in 955 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
960 // memory, so we can avoid this memcpy. 956 // memory, so we can avoid this memcpy.
961 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); 957 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry);
962 memcpy(dst, pickle.data(), pickle.size()); 958 memcpy(dst, pickle.data(), pickle.size());
963 959
964 allocator->MakeIterable(ref); 960 allocator->MakeIterable(ref);
965 field_trial->ref_ = ref; 961 field_trial->ref_ = ref;
966 } 962 }
967 963
968 // static 964 // static
969 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( 965 void FieldTrialList::ActivateFieldTrialEntryWhileLocked(
970 FieldTrial* field_trial) { 966 FieldTrial* field_trial) {
971 SharedPersistentMemoryAllocator* allocator = 967 FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
972 global_->field_trial_allocator_.get();
973 968
974 // Check if we're in the child process and return early if so. 969 // Check if we're in the child process and return early if so.
975 if (allocator && allocator->IsReadonly()) 970 if (allocator && allocator->IsReadonly())
976 return; 971 return;
977 972
978 SharedPersistentMemoryAllocator::Reference ref = field_trial->ref_; 973 FieldTrial::FieldTrialRef ref = field_trial->ref_;
979 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) { 974 if (ref == FieldTrialAllocator::kReferenceNull) {
980 // It's fine to do this even if the allocator hasn't been instantiated 975 // It's fine to do this even if the allocator hasn't been instantiated
981 // yet -- it'll just return early. 976 // yet -- it'll just return early.
982 AddToAllocatorWhileLocked(field_trial); 977 AddToAllocatorWhileLocked(field_trial);
983 } else { 978 } else {
984 // It's also okay to do this even though the callee doesn't have a lock -- 979 // It's also okay to do this even though the callee doesn't have a lock --
985 // the only thing that happens on a stale read here is a slight performance 980 // the only thing that happens on a stale read here is a slight performance
986 // hit from the child re-synchronizing activation state. 981 // hit from the child re-synchronizing activation state.
987 FieldTrialEntry* entry = 982 FieldTrialEntry* entry =
988 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); 983 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType);
989 entry->activated = true; 984 entry->activated = true;
(...skipping 25 matching lines...) Expand all
1015 return; 1010 return;
1016 } 1011 }
1017 AutoLock auto_lock(global_->lock_); 1012 AutoLock auto_lock(global_->lock_);
1018 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1013 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1019 trial->AddRef(); 1014 trial->AddRef();
1020 trial->SetTrialRegistered(); 1015 trial->SetTrialRegistered();
1021 global_->registered_[trial->trial_name()] = trial; 1016 global_->registered_[trial->trial_name()] = trial;
1022 } 1017 }
1023 1018
1024 } // namespace base 1019 } // namespace base
OLDNEW
« no previous file with comments | « base/metrics/field_trial.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698