 Chromium Code Reviews
 Chromium Code Reviews Issue 2530573002:
  Share field trial allocator on zygote-using Linuxes  (Closed)
    
  
    Issue 2530573002:
  Share field trial allocator on zygote-using Linuxes  (Closed) 
  | 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/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 #if defined(POSIX_WITH_ZYGOTE) | |
| 25 #include "base/posix/global_descriptors.h" | |
| 26 #endif | |
| 27 | |
| 24 namespace base { | 28 namespace base { | 
| 25 | 29 | 
| 26 namespace { | 30 namespace { | 
| 27 | 31 | 
| 28 // Define a separator character to use when creating a persistent form of an | 32 // 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 | 33 // 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). | 34 // second process to mimic our state (i.e., provide the same group name). | 
| 31 const char kPersistentStringSeparator = '/'; // Currently a slash. | 35 const char kPersistentStringSeparator = '/'; // Currently a slash. | 
| 32 | 36 | 
| 33 // Define a marker character to be used as a prefix to a trial name on the | 37 // 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 Loading... | |
| 225 } | 229 } | 
| 226 } | 230 } | 
| 227 | 231 | 
| 228 #if defined(OS_WIN) | 232 #if defined(OS_WIN) | 
| 229 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | 233 HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | 
| 230 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 234 HANDLE src = allocator->shared_memory()->handle().GetHandle(); | 
| 231 ProcessHandle process = GetCurrentProcess(); | 235 ProcessHandle process = GetCurrentProcess(); | 
| 232 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 236 DWORD access = SECTION_MAP_READ | SECTION_QUERY; | 
| 233 HANDLE dst; | 237 HANDLE dst; | 
| 234 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 238 if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) | 
| 235 return nullptr; | 239 return kInvalidPlatformFile; | 
| 236 return dst; | 240 return dst; | 
| 237 } | 241 } | 
| 238 #endif | 242 #endif | 
| 239 | 243 | 
| 244 #if defined(POSIX_WITH_ZYGOTE) | |
| 245 int CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { | |
| 246 SharedMemoryHandle new_handle; | |
| 247 allocator->shared_memory()->ShareReadOnlyToProcess(GetCurrentProcessHandle(), | |
| 248 &new_handle); | |
| 249 return SharedMemory::GetFdFromSharedMemoryHandle(new_handle); | |
| 250 } | |
| 251 #endif | |
| 252 | |
| 240 } // namespace | 253 } // namespace | 
| 241 | 254 | 
| 242 // statics | 255 // statics | 
| 243 const int FieldTrial::kNotFinalized = -1; | 256 const int FieldTrial::kNotFinalized = -1; | 
| 244 const int FieldTrial::kDefaultGroupNumber = 0; | 257 const int FieldTrial::kDefaultGroupNumber = 0; | 
| 245 bool FieldTrial::enable_benchmarking_ = false; | 258 bool FieldTrial::enable_benchmarking_ = false; | 
| 246 | 259 | 
| 247 int FieldTrialList::kNoExpirationYear = 0; | 260 int FieldTrialList::kNoExpirationYear = 0; | 
| 248 | 261 | 
| 249 //------------------------------------------------------------------------------ | 262 //------------------------------------------------------------------------------ | 
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 729 // processes are properly reported in crash reports. | 742 // processes are properly reported in crash reports. | 
| 730 trial->group(); | 743 trial->group(); | 
| 731 } | 744 } | 
| 732 } | 745 } | 
| 733 return true; | 746 return true; | 
| 734 } | 747 } | 
| 735 | 748 | 
| 736 // static | 749 // static | 
| 737 void FieldTrialList::CreateTrialsFromCommandLine( | 750 void FieldTrialList::CreateTrialsFromCommandLine( | 
| 738 const CommandLine& cmd_line, | 751 const CommandLine& cmd_line, | 
| 739 const char* field_trial_handle_switch) { | 752 const char* field_trial_handle_switch, | 
| 753 const int field_trial_handle) { | |
| 740 global_->create_trials_from_command_line_called_ = true; | 754 global_->create_trials_from_command_line_called_ = true; | 
| 741 | 755 | 
| 742 #if defined(OS_WIN) && !defined(OS_NACL) | |
| 743 if (cmd_line.HasSwitch(field_trial_handle_switch)) { | |
| 744 std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); | |
| 745 int field_trial_handle = std::stoi(arg); | |
| 746 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | |
| 747 bool result = CreateTrialsFromWindowsHandle(handle); | |
| 748 DCHECK(result); | |
| 749 } | |
| 750 #endif | |
| 751 | |
| 752 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { | 756 if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { | 
| 753 bool result = FieldTrialList::CreateTrialsFromString( | 757 bool result = FieldTrialList::CreateTrialsFromString( | 
| 754 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), | 758 cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), | 
| 755 std::set<std::string>()); | 759 std::set<std::string>()); | 
| 756 DCHECK(result); | 760 DCHECK(result); | 
| 761 return; | |
| 757 } | 762 } | 
| 763 | |
| 764 #if defined(POSIX_WITH_ZYGOTE) | |
| 765 if (kUseSharedMemoryForFieldTrials) { | |
| 766 bool result = CreateTrialsFromFdKey(field_trial_handle); | |
| 767 DCHECK(result); | |
| 768 return; | |
| 769 } | |
| 770 #endif | |
| 771 | |
| 772 #if defined(OS_WIN) | |
| 773 if (cmd_line.HasSwitch(field_trial_handle_switch)) { | |
| 774 std::string handle_switch = | |
| 775 cmd_line.GetSwitchValueASCII(field_trial_handle_switch); | |
| 776 bool result = CreateTrialsFromHandleSwitch(handle_switch); | |
| 777 DCHECK(result); | |
| 778 return; | |
| 779 } | |
| 780 #endif | |
| 758 } | 781 } | 
| 759 | 782 | 
| 760 #if defined(OS_WIN) | 783 #if defined(OS_WIN) | 
| 761 // static | 784 // static | 
| 762 void FieldTrialList::AppendFieldTrialHandleIfNeeded( | 785 void FieldTrialList::AppendFieldTrialHandleIfNeeded( | 
| 763 HandlesToInheritVector* handles) { | 786 HandlesToInheritVector* handles) { | 
| 764 if (!global_) | 787 if (!global_) | 
| 765 return; | 788 return; | 
| 766 if (kUseSharedMemoryForFieldTrials) { | 789 if (kUseSharedMemoryForFieldTrials) { | 
| 767 InstantiateFieldTrialAllocatorIfNeeded(); | 790 InstantiateFieldTrialAllocatorIfNeeded(); | 
| 768 if (global_->readonly_allocator_handle_) | 791 if (global_->readonly_allocator_handle_) | 
| 769 handles->push_back(global_->readonly_allocator_handle_); | 792 handles->push_back(global_->readonly_allocator_handle_); | 
| 770 } | 793 } | 
| 771 } | 794 } | 
| 772 #endif | 795 #endif | 
| 773 | 796 | 
| 797 #if defined(OS_POSIX) && !defined(OS_NACL) | |
| 798 // static | |
| 799 int FieldTrialList::GetFieldTrialHandle() { | |
| 800 if (!global_) | |
| 801 return kInvalidPlatformFile; | |
| 802 if (kUseSharedMemoryForFieldTrials) { | |
| 803 InstantiateFieldTrialAllocatorIfNeeded(); | |
| 804 // We check for an invalid handle where this gets called. | |
| 805 return global_->readonly_allocator_handle_; | |
| 806 } | |
| 807 return kInvalidPlatformFile; | |
| 808 } | |
| 809 #endif | |
| 810 | |
| 774 // static | 811 // static | 
| 775 void FieldTrialList::CopyFieldTrialStateToFlags( | 812 void FieldTrialList::CopyFieldTrialStateToFlags( | 
| 776 const char* field_trial_handle_switch, | 813 const char* field_trial_handle_switch, | 
| 777 CommandLine* cmd_line) { | 814 CommandLine* cmd_line) { | 
| 778 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, | 815 // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, | 
| 779 // content browser tests currently don't create a FieldTrialList because they | 816 // content browser tests currently don't create a FieldTrialList because they | 
| 780 // don't run ChromeBrowserMainParts code where it's done for Chrome. | 817 // don't run ChromeBrowserMainParts code where it's done for Chrome. | 
| 781 if (!global_) | 818 if (!global_) | 
| 782 return; | 819 return; | 
| 783 | 820 | 
| 784 #if defined(OS_WIN) | 821 #if defined(OS_WIN) | 
| 785 // Use shared memory to pass the state if the feature is enabled, otherwise | 822 // 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. | 823 // fallback to passing it via the command line as a string. | 
| 787 if (kUseSharedMemoryForFieldTrials) { | 824 if (kUseSharedMemoryForFieldTrials) { | 
| 788 InstantiateFieldTrialAllocatorIfNeeded(); | 825 InstantiateFieldTrialAllocatorIfNeeded(); | 
| 789 // If the readonly handle didn't get duplicated properly, then fallback to | 826 // If the readonly handle didn't get duplicated properly, then fallback to | 
| 790 // original behavior. | 827 // original behavior. | 
| 791 if (!global_->readonly_allocator_handle_) { | 828 if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) { | 
| 792 AddForceFieldTrialsFlag(cmd_line); | 829 AddForceFieldTrialsFlag(cmd_line); | 
| 793 return; | 830 return; | 
| 794 } | 831 } | 
| 795 | 832 | 
| 796 // HANDLE is just typedef'd to void *. We basically cast the handle into an | 833 // PlatformFile is typedef'd to HANDLE which is typedef'd to void *. We | 
| 797 // int (uintptr_t, to be exact), stringify the int, and pass it as a | 834 // basically cast the handle into an int (uintptr_t, to be exact), stringify | 
| 798 // command-line flag. The child process will do the reverse conversions to | 835 // the int, and pass it as a command-line flag. The child process will do | 
| 799 // retrieve the handle. See http://stackoverflow.com/a/153077 | 836 // the reverse conversions to retrieve the handle. See | 
| 837 // http://stackoverflow.com/a/153077 | |
| 800 auto uintptr_handle = | 838 auto uintptr_handle = | 
| 801 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); | 839 reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); | 
| 802 std::string field_trial_handle = std::to_string(uintptr_handle); | 840 std::string field_trial_handle = std::to_string(uintptr_handle); | 
| 803 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); | 841 cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); | 
| 804 global_->field_trial_allocator_->UpdateTrackingHistograms(); | 842 global_->field_trial_allocator_->UpdateTrackingHistograms(); | 
| 
Alexei Svitkine (slow)
2016/11/28 16:56:13
We should also log this histogram on Linux.
 
lawrencewu
2016/11/29 14:38:07
Done.
 | |
| 805 return; | 843 return; | 
| 806 } | 844 } | 
| 807 #endif | 845 #endif | 
| 808 | 846 | 
| 809 AddForceFieldTrialsFlag(cmd_line); | 847 AddForceFieldTrialsFlag(cmd_line); | 
| 
Alexei Svitkine (slow)
2016/11/28 16:56:13
This results in force field trials flag always bei
 
lawrencewu
2016/11/29 14:38:07
Fixed.
 | |
| 810 } | 848 } | 
| 811 | 849 | 
| 812 // static | 850 // static | 
| 813 FieldTrial* FieldTrialList::CreateFieldTrial( | 851 FieldTrial* FieldTrialList::CreateFieldTrial( | 
| 814 const std::string& name, | 852 const std::string& name, | 
| 815 const std::string& group_name) { | 853 const std::string& group_name) { | 
| 816 DCHECK(global_); | 854 DCHECK(global_); | 
| 817 DCHECK_GE(name.size(), 0u); | 855 DCHECK_GE(name.size(), 0u); | 
| 818 DCHECK_GE(group_name.size(), 0u); | 856 DCHECK_GE(group_name.size(), 0u); | 
| 819 if (name.empty() || group_name.empty() || !global_) | 857 if (name.empty() || group_name.empty() || !global_) | 
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 887 // static | 925 // static | 
| 888 size_t FieldTrialList::GetFieldTrialCount() { | 926 size_t FieldTrialList::GetFieldTrialCount() { | 
| 889 if (!global_) | 927 if (!global_) | 
| 890 return 0; | 928 return 0; | 
| 891 AutoLock auto_lock(global_->lock_); | 929 AutoLock auto_lock(global_->lock_); | 
| 892 return global_->registered_.size(); | 930 return global_->registered_.size(); | 
| 893 } | 931 } | 
| 894 | 932 | 
| 895 #if defined(OS_WIN) | 933 #if defined(OS_WIN) | 
| 896 // static | 934 // static | 
| 897 bool FieldTrialList::CreateTrialsFromWindowsHandle(HANDLE handle) { | 935 bool FieldTrialList::CreateTrialsFromHandleSwitch( | 
| 936 const std::string& handle_switch) { | |
| 937 int field_trial_handle = std::stoi(handle_switch); | |
| 938 HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); | |
| 898 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); | 939 SharedMemoryHandle shm_handle(handle, GetCurrentProcId()); | 
| 940 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); | |
| 941 } | |
| 942 #endif | |
| 899 | 943 | 
| 944 #if defined(POSIX_WITH_ZYGOTE) | |
| 945 // static | |
| 946 bool FieldTrialList::CreateTrialsFromFdKey(const int fd_key) { | |
| 947 int fd = GlobalDescriptors::GetInstance()->Get(fd_key); | |
| 948 SharedMemoryHandle shm_handle(fd, true); | |
| 949 return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); | |
| 950 } | |
| 951 #endif | |
| 952 | |
| 953 #if !defined(OS_NACL) | |
| 954 // static | |
| 955 bool FieldTrialList::CreateTrialsFromSharedMemoryHandle( | |
| 956 SharedMemoryHandle shm_handle) { | |
| 900 // shm gets deleted when it gets out of scope, but that's OK because we need | 957 // 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. | 958 // it only for the duration of this method. | 
| 902 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); | 959 std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true)); | 
| 903 if (!shm.get()->Map(kFieldTrialAllocationSize)) | 960 if (!shm.get()->Map(kFieldTrialAllocationSize)) | 
| 904 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 961 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 
| 905 | 962 | 
| 906 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); | 963 return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); | 
| 907 } | 964 } | 
| 908 #endif | 965 #endif | 
| 909 | 966 | 
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 979 #if !defined(OS_NACL) | 1036 #if !defined(OS_NACL) | 
| 980 // static | 1037 // static | 
| 981 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { | 1038 void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { | 
| 982 if (!global_) | 1039 if (!global_) | 
| 983 return; | 1040 return; | 
| 984 AutoLock auto_lock(global_->lock_); | 1041 AutoLock auto_lock(global_->lock_); | 
| 985 // Create the allocator if not already created and add all existing trials. | 1042 // Create the allocator if not already created and add all existing trials. | 
| 986 if (global_->field_trial_allocator_ != nullptr) | 1043 if (global_->field_trial_allocator_ != nullptr) | 
| 987 return; | 1044 return; | 
| 988 | 1045 | 
| 1046 SharedMemoryCreateOptions options; | |
| 1047 options.size = kFieldTrialAllocationSize; | |
| 1048 options.share_read_only = true; | |
| 1049 | |
| 989 std::unique_ptr<SharedMemory> shm(new SharedMemory()); | 1050 std::unique_ptr<SharedMemory> shm(new SharedMemory()); | 
| 990 if (!shm->CreateAndMapAnonymous(kFieldTrialAllocationSize)) | 1051 if (!shm->Create(options)) | 
| 1052 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | |
| 1053 | |
| 1054 if (!shm->Map(kFieldTrialAllocationSize)) | |
| 991 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 1055 TerminateBecauseOutOfMemory(kFieldTrialAllocationSize); | 
| 992 | 1056 | 
| 993 global_->field_trial_allocator_.reset( | 1057 global_->field_trial_allocator_.reset( | 
| 994 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); | 1058 new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); | 
| 995 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 1059 global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); | 
| 996 | 1060 | 
| 997 // Add all existing field trials. | 1061 // Add all existing field trials. | 
| 998 for (const auto& registered : global_->registered_) { | 1062 for (const auto& registered : global_->registered_) { | 
| 999 AddToAllocatorWhileLocked(registered.second); | 1063 AddToAllocatorWhileLocked(registered.second); | 
| 1000 } | 1064 } | 
| 1001 | 1065 | 
| 1002 #if defined(OS_WIN) | 1066 #if defined(OS_WIN) || defined(POSIX_WITH_ZYGOTE) | 
| 1003 // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 1067 // Set |readonly_allocator_handle_| so we can pass it to be inherited and | 
| 1004 // via the command line. | 1068 // via the command line. | 
| 1005 global_->readonly_allocator_handle_ = | 1069 global_->readonly_allocator_handle_ = | 
| 1006 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 1070 CreateReadOnlyHandle(global_->field_trial_allocator_.get()); | 
| 1007 #endif | 1071 #endif | 
| 1008 } | 1072 } | 
| 1009 #endif | 1073 #endif | 
| 1010 | 1074 | 
| 1011 // static | 1075 // static | 
| 1012 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { | 1076 void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { | 
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1107 return; | 1171 return; | 
| 1108 } | 1172 } | 
| 1109 AutoLock auto_lock(global_->lock_); | 1173 AutoLock auto_lock(global_->lock_); | 
| 1110 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 1174 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 
| 1111 trial->AddRef(); | 1175 trial->AddRef(); | 
| 1112 trial->SetTrialRegistered(); | 1176 trial->SetTrialRegistered(); | 
| 1113 global_->registered_[trial->trial_name()] = trial; | 1177 global_->registered_[trial->trial_name()] = trial; | 
| 1114 } | 1178 } | 
| 1115 | 1179 | 
| 1116 } // namespace base | 1180 } // namespace base | 
| OLD | NEW |