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

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

Issue 2530573002: Share field trial allocator on zygote-using Linuxes (Closed)
Patch Set: fix call from content_main_runner Created 4 years 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/pickle.h" 16 #include "base/pickle.h"
17 #include "base/process/memory.h" 17 #include "base/process/memory.h"
18 #include "base/rand_util.h" 18 #include "base/rand_util.h"
19 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h" 20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h" 21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h" 22 #include "base/strings/utf_string_conversions.h"
23 23
24 // On systems that use the zygote process to spawn child processes, we must
25 // retrieve the correct fd using the mapping in GlobalDescriptors.
26 #if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_MACOSX) && \
27 !defined(OS_ANDROID)
28 #define POSIX_WITH_ZYGOTE 1
29 #endif
30
31 #if defined(POSIX_WITH_ZYGOTE)
32 #include "base/posix/global_descriptors.h"
33 #endif
34
24 namespace base { 35 namespace base {
25 36
26 namespace { 37 namespace {
27 38
28 // Define a separator character to use when creating a persistent form of an 39 // Define a separator character to use when creating a persistent form of an
29 // instance. This is intended for use as a command line argument, passed to a 40 // instance. This is intended for use as a command line argument, passed to a
30 // second process to mimic our state (i.e., provide the same group name). 41 // second process to mimic our state (i.e., provide the same group name).
31 const char kPersistentStringSeparator = '/'; // Currently a slash. 42 const char kPersistentStringSeparator = '/'; // Currently a slash.
32 43
33 // Define a marker character to be used as a prefix to a trial name on the 44 // Define a marker character to be used as a prefix to a trial name on the
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
225 } 236 }
226 } 237 }
227 238
228 #if defined(OS_WIN) 239 #if defined(OS_WIN)
229 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { 240 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) {
230 HANDLE src = allocator->shared_memory()->handle().GetHandle(); 241 HANDLE src = allocator->shared_memory()->handle().GetHandle();
231 ProcessHandle process = GetCurrentProcess(); 242 ProcessHandle process = GetCurrentProcess();
232 DWORD access = SECTION_MAP_READ | SECTION_QUERY; 243 DWORD access = SECTION_MAP_READ | SECTION_QUERY;
233 HANDLE dst; 244 HANDLE dst;
234 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) 245 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0))
235 return nullptr; 246 return kInvalidPlatformFile;
236 return dst; 247 return dst;
237 } 248 }
238 #endif 249 #endif
239 250
251 #if defined(POSIX_WITH_ZYGOTE)
252 int CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) {
253 SharedMemoryHandle new_handle;
254 allocator->shared_memory()->ShareReadOnlyToProcess(GetCurrentProcessHandle(),
255 &new_handle);
256 return SharedMemory::GetFdFromSharedMemoryHandle(new_handle);
257 }
258 #endif
259
240 } // namespace 260 } // namespace
241 261
242 // statics 262 // statics
243 const int FieldTrial::kNotFinalized = -1; 263 const int FieldTrial::kNotFinalized = -1;
244 const int FieldTrial::kDefaultGroupNumber = 0; 264 const int FieldTrial::kDefaultGroupNumber = 0;
245 bool FieldTrial::enable_benchmarking_ = false; 265 bool FieldTrial::enable_benchmarking_ = false;
246 266
247 int FieldTrialList::kNoExpirationYear = 0; 267 int FieldTrialList::kNoExpirationYear = 0;
248 268
249 //------------------------------------------------------------------------------ 269 //------------------------------------------------------------------------------
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after
729 // processes are properly reported in crash reports. 749 // processes are properly reported in crash reports.
730 trial->group(); 750 trial->group();
731 } 751 }
732 } 752 }
733 return true; 753 return true;
734 } 754 }
735 755
736 // static 756 // static
737 void FieldTrialList::CreateTrialsFromCommandLine( 757 void FieldTrialList::CreateTrialsFromCommandLine(
738 const CommandLine& cmd_line, 758 const CommandLine& cmd_line,
739 const char* field_trial_handle_switch) { 759 const char* field_trial_handle_switch,
760 int field_trial_descriptor_id) {
740 global_->create_trials_from_command_line_called_ = true; 761 global_->create_trials_from_command_line_called_ = true;
741 762
742 #if defined(OS_WIN) && !defined(OS_NACL) 763 #if defined(OS_WIN)
743 if (cmd_line.HasSwitch(field_trial_handle_switch)) { 764 if (cmd_line.HasSwitch(field_trial_handle_switch)) {
744 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); 765 std::string handle_switch =
745 int field_trial_handle = std::stoi(arg); 766 cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
746 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); 767 bool result = CreateTrialsFromHandleSwitch(handle_switch);
747 bool result = CreateTrialsFromWindowsHandle(handle);
748 DCHECK(result); 768 DCHECK(result);
749 } 769 }
750 #endif 770 #endif
751 771
772 #if defined(POSIX_WITH_ZYGOTE)
773 bool result = CreateTrialsFromDescriptor(field_trial_descriptor_id);
Alexei Svitkine (slow) 2016/11/29 19:32:47 Nit: Just inline this into the if.
lawrencewu 2016/11/29 19:52:44 Done.
774 // If we failed to create trials from the descriptor, fallback to the command
775 // line.
776 if (result)
777 return;
778 #endif
779
752 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { 780 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) {
753 bool result = FieldTrialList::CreateTrialsFromString( 781 bool result = FieldTrialList::CreateTrialsFromString(
754 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), 782 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials),
755 std::set<std::string>()); 783 std::set<std::string>());
756 DCHECK(result); 784 DCHECK(result);
757 } 785 }
758 } 786 }
759 787
788 #if defined(POSIX_WITH_ZYGOTE)
789 // static
790 bool FieldTrialList::CreateTrialsFromDescriptor(int field_trial_descriptor_id) {
791 if (!kUseSharedMemoryForFieldTrials)
792 return false;
793
794 if (field_trial_descriptor_id == -1) {
Alexei Svitkine (slow) 2016/11/29 19:32:47 Nit: No {}
lawrencewu 2016/11/29 19:52:44 Done.
795 return false;
796 }
797
798 int fd =
799 GlobalDescriptors::GetInstance()->MaybeGet(field_trial_descriptor_id);
800 if (fd == -1)
801 return false;
802
803 global_->create_trials_from_command_line_called_ = true;
Alexei Svitkine (slow) 2016/11/29 19:32:47 This is not needed anymore since you're calling it
lawrencewu 2016/11/29 19:52:44 Removed.
804
805 SharedMemoryHandle shm_handle(fd, true);
806 bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle);
807 DCHECK(result);
808 return true;
809 }
810 #endif
811
760 #if defined(OS_WIN) 812 #if defined(OS_WIN)
761 // static 813 // static
762 void FieldTrialList::AppendFieldTrialHandleIfNeeded( 814 void FieldTrialList::AppendFieldTrialHandleIfNeeded(
763 HandlesToInheritVector* handles) { 815 HandlesToInheritVector* handles) {
764 if (!global_) 816 if (!global_)
765 return; 817 return;
766 if (kUseSharedMemoryForFieldTrials) { 818 if (kUseSharedMemoryForFieldTrials) {
767 InstantiateFieldTrialAllocatorIfNeeded(); 819 InstantiateFieldTrialAllocatorIfNeeded();
768 if (global_->readonly_allocator_handle_) 820 if (global_->readonly_allocator_handle_)
769 handles->push_back(global_->readonly_allocator_handle_); 821 handles->push_back(global_->readonly_allocator_handle_);
770 } 822 }
771 } 823 }
772 #endif 824 #endif
773 825
826 #if defined(OS_POSIX) && !defined(OS_NACL)
827 // static
828 int FieldTrialList::GetFieldTrialHandle() {
829 if (!global_)
Alexei Svitkine (slow) 2016/11/29 19:32:47 Does this actually get hit?
lawrencewu 2016/11/29 19:52:44 I added a log statement here and it doesn't get hi
Alexei Svitkine (slow) 2016/11/29 20:19:27 I think it would be better to remove this if it's
lawrencewu 2016/11/29 21:40:18 Actually, it looks like some tests do hit that and
830 return kInvalidPlatformFile;
831 if (kUseSharedMemoryForFieldTrials) {
832 InstantiateFieldTrialAllocatorIfNeeded();
833 // We check for an invalid handle where this gets called.
834 return global_->readonly_allocator_handle_;
835 }
836 return kInvalidPlatformFile;
837 }
838 #endif
839
774 // static 840 // static
775 void FieldTrialList::CopyFieldTrialStateToFlags( 841 void FieldTrialList::CopyFieldTrialStateToFlags(
776 const char* field_trial_handle_switch, 842 const char* field_trial_handle_switch,
777 CommandLine* cmd_line) { 843 CommandLine* cmd_line) {
778 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, 844 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However,
779 // content browser tests currently don't create a FieldTrialList because they 845 // content browser tests currently don't create a FieldTrialList because they
780 // don't run ChromeBrowserMainParts code where it's done for Chrome. 846 // don't run ChromeBrowserMainParts code where it's done for Chrome.
781 if (!global_) 847 if (!global_)
782 return; 848 return;
783 849
784 #if defined(OS_WIN) 850 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE)
785 // Use shared memory to pass the state if the feature is enabled, otherwise 851 // Use shared memory to pass the state if the feature is enabled, otherwise
786 // fallback to passing it via the command line as a string. 852 // fallback to passing it via the command line as a string.
787 if (kUseSharedMemoryForFieldTrials) { 853 if (kUseSharedMemoryForFieldTrials) {
788 InstantiateFieldTrialAllocatorIfNeeded(); 854 InstantiateFieldTrialAllocatorIfNeeded();
789 // If the readonly handle didn't get duplicated properly, then fallback to 855 // If the readonly handle didn't get duplicated properly, then fallback to
790 // original behavior. 856 // original behavior.
791 if (!global_->readonly_allocator_handle_) { 857 if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) {
792 AddForceFieldTrialsFlag(cmd_line); 858 AddForceFieldTrialsFlag(cmd_line);
793 return; 859 return;
794 } 860 }
795 861
796 // HANDLE is just typedef'd to void *. We basically cast the handle into an 862 // We need to pass a named anonymous handle to shared memory over the command
Alexei Svitkine (slow) 2016/11/29 19:32:47 Nit: Indent this comment.
lawrencewu 2016/11/29 19:52:44 Done.
797 // int (uintptr_t, to be exact), stringify the int, and pass it as a 863 // line on Windows, since the child doesn't know which of the handles it
798 // command-line flag. The child process will do the reverse conversions to 864 // inherited it should open. On POSIX, we don't need to do this -- we dup the fd
799 // retrieve the handle. See http://stackoverflow.com/a/153077 865 // into a fixed fd kFieldTrialDescriptor, so we can just look it up there.
866 #if defined(OS_WIN)
867 // PlatformFile is typedef'd to HANDLE which is typedef'd to void *. We
868 // basically cast the handle into an int (uintptr_t, to be exact), stringify
869 // the int, and pass it as a command-line flag. The child process will do
870 // the reverse conversions to retrieve the handle. See
871 // http://stackoverflow.com/a/153077
800 auto uintptr_handle = 872 auto uintptr_handle =
801 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); 873 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_);
802 std::string field_trial_handle = std::to_string(uintptr_handle); 874 std::string field_trial_handle = std::to_string(uintptr_handle);
803 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); 875 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle);
876 #endif
804 global_->field_trial_allocator_->UpdateTrackingHistograms(); 877 global_->field_trial_allocator_->UpdateTrackingHistograms();
Alexei Svitkine (slow) 2016/11/29 19:32:46 Nit: Move this line above to line 861.
lawrencewu 2016/11/29 19:52:44 Done.
805 return; 878 return;
806 } 879 }
807 #endif 880 #endif
808 881
809 AddForceFieldTrialsFlag(cmd_line); 882 AddForceFieldTrialsFlag(cmd_line);
810 } 883 }
811 884
812 // static 885 // static
813 FieldTrial* FieldTrialList::CreateFieldTrial( 886 FieldTrial* FieldTrialList::CreateFieldTrial(
814 const std::string& name, 887 const std::string& name,
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
887 // static 960 // static
888 size_t FieldTrialList::GetFieldTrialCount() { 961 size_t FieldTrialList::GetFieldTrialCount() {
889 if (!global_) 962 if (!global_)
890 return 0; 963 return 0;
891 AutoLock auto_lock(global_->lock_); 964 AutoLock auto_lock(global_->lock_);
892 return global_->registered_.size(); 965 return global_->registered_.size();
893 } 966 }
894 967
895 #if defined(OS_WIN) 968 #if defined(OS_WIN)
896 // static 969 // static
897 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) { 970 bool FieldTrialList::CreateTrialsFromHandleSwitch(
971 const std::string& handle_switch) {
972 int field_trial_handle = std::stoi(handle_switch);
973 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
898 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); 974 SharedMemoryHandle shm_handle(handle, GetCurrentProcId());
975 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle);
976 }
977 #endif
899 978
979 #if !defined(OS_NACL)
980 // static
981 bool FieldTrialList::CreateTrialsFromSharedMemoryHandle(
982 SharedMemoryHandle shm_handle) {
900 // shm gets deleted when it gets out of scope, but that's OK because we need 983 // shm gets deleted when it gets out of scope, but that's OK because we need
901 // it only for the duration of this method. 984 // it only for the duration of this method.
902 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); 985 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
903 if (!shm.get()->Map(kFieldTrialAllocationSize)) 986 if (!shm.get()->Map(kFieldTrialAllocationSize))
904 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); 987 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
905 988
906 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); 989 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
907 } 990 }
908 #endif 991 #endif
909 992
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
979 #if !defined(OS_NACL) 1062 #if !defined(OS_NACL)
980 // static 1063 // static
981 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { 1064 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() {
982 if (!global_) 1065 if (!global_)
983 return; 1066 return;
984 AutoLock auto_lock(global_->lock_); 1067 AutoLock auto_lock(global_->lock_);
985 // Create the allocator if not already created and add all existing trials. 1068 // Create the allocator if not already created and add all existing trials.
986 if (global_->field_trial_allocator_ != nullptr) 1069 if (global_->field_trial_allocator_ != nullptr)
987 return; 1070 return;
988 1071
1072 SharedMemoryCreateOptions options;
1073 options.size = kFieldTrialAllocationSize;
1074 options.share_read_only = true;
1075
989 std::unique_ptr<SharedMemory> shm(new SharedMemory()); 1076 std::unique_ptr<SharedMemory> shm(new SharedMemory());
990 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) 1077 if (!shm->Create(options))
1078 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
1079
1080 if (!shm->Map(kFieldTrialAllocationSize))
991 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); 1081 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize);
992 1082
993 global_->field_trial_allocator_.reset( 1083 global_->field_trial_allocator_.reset(
994 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); 1084 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false));
995 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); 1085 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName);
996 1086
997 // Add all existing field trials. 1087 // Add all existing field trials.
998 for (const auto& registered : global_->registered_) { 1088 for (const auto& registered : global_->registered_) {
999 AddToAllocatorWhileLocked(registered.second); 1089 AddToAllocatorWhileLocked(registered.second);
1000 } 1090 }
1001 1091
1002 #if defined(OS_WIN) 1092 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE)
1003 // Set |readonly_allocator_handle_| so we can pass it to be inherited and 1093 // Set |readonly_allocator_handle_| so we can pass it to be inherited and
1004 // via the command line. 1094 // via the command line.
1005 global_->readonly_allocator_handle_ = 1095 global_->readonly_allocator_handle_ =
1006 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); 1096 CreateReadOnlyHandle(global_->field_trial_allocator_.get());
1007 #endif 1097 #endif
1008 } 1098 }
1009 #endif 1099 #endif
1010 1100
1011 // static 1101 // static
1012 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { 1102 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) {
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
1107 return; 1197 return;
1108 } 1198 }
1109 AutoLock auto_lock(global_->lock_); 1199 AutoLock auto_lock(global_->lock_);
1110 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); 1200 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
1111 trial->AddRef(); 1201 trial->AddRef();
1112 trial->SetTrialRegistered(); 1202 trial->SetTrialRegistered();
1113 global_->registered_[trial->trial_name()] = trial; 1203 global_->registered_[trial->trial_name()] = trial;
1114 } 1204 }
1115 1205
1116 } // namespace base 1206 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698