Chromium Code Reviews| Index: base/metrics/field_trial.cc |
| diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc |
| index 78602f1e613d39f00020dea6f1f18f093bcfa72f..6dacf66976e7943a096917f092e3af57a88e54b1 100644 |
| --- a/base/metrics/field_trial.cc |
| +++ b/base/metrics/field_trial.cc |
| @@ -16,6 +16,7 @@ |
| #include "base/process/memory.h" |
| #include "base/rand_util.h" |
| #include "base/strings/string_number_conversions.h" |
| +#include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| @@ -208,25 +209,6 @@ void AddFeatureAndFieldTrialFlags(const char* enable_features_switch, |
| } |
| } |
| -#if defined(OS_WIN) |
| -HANDLE CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
| - HANDLE src = allocator->shared_memory()->handle().GetHandle(); |
| - ProcessHandle process = GetCurrentProcess(); |
| - DWORD access = SECTION_MAP_READ | SECTION_QUERY; |
| - HANDLE dst; |
| - if (!::DuplicateHandle(process, src, process, &dst, access, true, 0)) |
| - return kInvalidPlatformFile; |
| - return dst; |
| -} |
| -#endif |
| - |
| -#if defined(OS_POSIX) && !defined(OS_NACL) |
| -int CreateReadOnlyHandle(FieldTrialList::FieldTrialAllocator* allocator) { |
| - SharedMemoryHandle handle = allocator->shared_memory()->GetReadOnlyHandle(); |
| - return SharedMemory::GetFdFromSharedMemoryHandle(handle); |
| -} |
| -#endif |
| - |
| void OnOutOfMemory(size_t size) { |
| #if defined(OS_NACL) |
| NOTREACHED(); |
| @@ -235,6 +217,23 @@ void OnOutOfMemory(size_t size) { |
| #endif |
| } |
| +#if !defined(OS_NACL) |
| +// Returns whether the operation succeeded. |
| +bool DeserializeGUIDFromStringPieces(const base::StringPiece& first, |
|
Alexei Svitkine (slow)
2017/05/09 14:47:40
Nit: StringPiece is meant to be passed by value -
erikchen
2017/05/09 16:53:52
Done.
|
| + const base::StringPiece& second, |
| + base::UnguessableToken* guid) { |
| + uint64_t high = 0; |
| + uint64_t low = 0; |
| + if (!base::StringToUint64(first, &high) || |
| + !base::StringToUint64(second, &low)) { |
| + return false; |
| + } |
| + |
| + *guid = base::UnguessableToken::Deserialize(high, low); |
| + return true; |
| +} |
| +#endif |
| + |
| } // namespace |
| // statics |
| @@ -781,9 +780,9 @@ void FieldTrialList::CreateTrialsFromCommandLine( |
| #if defined(OS_WIN) |
| if (cmd_line.HasSwitch(field_trial_handle_switch)) { |
| - std::string handle_switch = |
| + std::string switch_value = |
| cmd_line.GetSwitchValueASCII(field_trial_handle_switch); |
| - bool result = CreateTrialsFromHandleSwitch(handle_switch); |
| + bool result = CreateTrialsFromSwitchValue(switch_value); |
| DCHECK(result); |
| } |
| #endif |
| @@ -793,7 +792,9 @@ void FieldTrialList::CreateTrialsFromCommandLine( |
| // sent over the switch (we don't care about the value). Invalid handles |
| // occur in some browser tests which don't initialize the allocator. |
| if (cmd_line.HasSwitch(field_trial_handle_switch)) { |
| - bool result = CreateTrialsFromDescriptor(fd_key); |
| + std::string switch_value = |
| + cmd_line.GetSwitchValueASCII(field_trial_handle_switch); |
| + bool result = CreateTrialsFromDescriptor(fd_key, switch_value); |
| DCHECK(result); |
| } |
| #endif |
| @@ -832,21 +833,21 @@ void FieldTrialList::AppendFieldTrialHandleIfNeeded( |
| return; |
| if (kUseSharedMemoryForFieldTrials) { |
| InstantiateFieldTrialAllocatorIfNeeded(); |
| - if (global_->readonly_allocator_handle_) |
| - handles->push_back(global_->readonly_allocator_handle_); |
| + if (global_->readonly_allocator_handle_.IsValid()) |
| + handles->push_back(global_->readonly_allocator_handle_.GetHandle()); |
| } |
| } |
| #endif |
| #if defined(OS_POSIX) && !defined(OS_NACL) |
| // static |
| -int FieldTrialList::GetFieldTrialHandle() { |
| +SharedMemoryHandle FieldTrialList::GetFieldTrialHandle() { |
| if (global_ && kUseSharedMemoryForFieldTrials) { |
| InstantiateFieldTrialAllocatorIfNeeded(); |
| // We check for an invalid handle where this gets called. |
| return global_->readonly_allocator_handle_; |
| } |
| - return kInvalidPlatformFile; |
| + return SharedMemoryHandle(); |
| } |
| #endif |
| @@ -873,37 +874,16 @@ void FieldTrialList::CopyFieldTrialStateToFlags( |
| InstantiateFieldTrialAllocatorIfNeeded(); |
| // If the readonly handle didn't get duplicated properly, then fallback to |
| // original behavior. |
| - if (global_->readonly_allocator_handle_ == kInvalidPlatformFile) { |
| + if (!global_->readonly_allocator_handle_.IsValid()) { |
| AddFeatureAndFieldTrialFlags(enable_features_switch, |
| disable_features_switch, cmd_line); |
| return; |
| } |
| global_->field_trial_allocator_->UpdateTrackingHistograms(); |
| - |
| -#if defined(OS_WIN) |
| - // We need to pass a named anonymous handle to shared memory over the |
| - // command line on Windows, since the child doesn't know which of the |
| - // handles it inherited it should open. |
| - // PlatformFile is typedef'd to HANDLE which is typedef'd to void *. We |
| - // basically cast the handle into an int (uintptr_t, to be exact), stringify |
| - // the int, and pass it as a command-line flag. The child process will do |
| - // the reverse conversions to retrieve the handle. See |
| - // http://stackoverflow.com/a/153077 |
| - auto uintptr_handle = |
| - reinterpret_cast<uintptr_t>(global_->readonly_allocator_handle_); |
| - std::string field_trial_handle = std::to_string(uintptr_handle); |
| - cmd_line->AppendSwitchASCII(field_trial_handle_switch, field_trial_handle); |
| -#elif defined(OS_POSIX) |
| - // On POSIX, we dup the fd into a fixed fd kFieldTrialDescriptor, so we |
| - // don't have to pass over the handle (it's not even the right handle |
| - // anyways). But some browser tests don't create the allocator, so we need |
| - // to be able to distinguish valid and invalid handles. We do that by just |
| - // checking that the flag is set with a dummy value. |
| - cmd_line->AppendSwitchASCII(field_trial_handle_switch, "1"); |
| -#else |
| -#error Unsupported OS |
| -#endif |
| + std::string switch_value = SerializeSharedMemoryHandleMetadata( |
| + global_->readonly_allocator_handle_); |
| + cmd_line->AppendSwitchASCII(field_trial_handle_switch, switch_value); |
| return; |
| } |
| @@ -1132,22 +1112,81 @@ FieldTrialList::GetAllFieldTrialsFromPersistentAllocator( |
| return entries; |
| } |
| +// static |
| +std::string FieldTrialList::SerializeSharedMemoryHandleMetadata( |
| + const SharedMemoryHandle& shm) { |
| + std::stringstream ss; |
| +#if defined(OS_WIN) |
| + // Tell the child process the name of the inherited HANDLE. |
| + uintptr_t uintptr_handle = reinterpret_cast<uintptr_t>(shm.GetHandle()); |
| + ss << uintptr_handle << ","; |
| +#elif !defined(OS_POSIX) |
| +#error Unsupported OS |
| +#endif |
| + |
| + base::UnguessableToken guid = shm.GetGUID(); |
| + ss << guid.GetHighForSerialization() << "," << guid.GetLowForSerialization(); |
| + return ss.str(); |
| +} |
| + |
| #if defined(OS_WIN) |
| // static |
| -bool FieldTrialList::CreateTrialsFromHandleSwitch( |
| - const std::string& handle_switch) { |
| - int field_trial_handle = std::stoi(handle_switch); |
| +SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( |
| + const std::string& switch_value) { |
| + std::vector<base::StringPiece> tokens = base::SplitStringPiece( |
| + switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| + |
| + if (tokens.size() != 3) |
| + return SharedMemoryHandle(); |
| + |
| + int field_trial_handle = 0; |
| + if (!base::StringToInt(tokens[0], &field_trial_handle)) |
| + return SharedMemoryHandle(); |
| HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle); |
| - // TODO(erikchen): Plumb a GUID for this SharedMemoryHandle. |
| - // https://crbug.com/713763. |
| - SharedMemoryHandle shm_handle(handle, base::UnguessableToken::Create()); |
| - return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); |
| + |
| + base::UnguessableToken guid; |
| + if (!DeserializeGUIDFromStringPieces(tokens[1], tokens[2], &guid)) |
| + return SharedMemoryHandle(); |
| + |
| + return SharedMemoryHandle(handle, guid); |
| } |
| -#endif |
| +#endif // defined(OS_WIN) |
| + |
| +#if defined(OS_POSIX) && !defined(OS_NACL) |
| +// static |
| +SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( |
| + int fd, |
| + const std::string& switch_value) { |
| + std::vector<base::StringPiece> tokens = base::SplitStringPiece( |
| + switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| + |
| + if (tokens.size() != 2) |
| + return SharedMemoryHandle(); |
| + |
| + base::UnguessableToken guid; |
| + if (!DeserializeGUIDFromStringPieces(tokens[0], tokens[1], &guid)) |
| + return SharedMemoryHandle(); |
| + |
| + return SharedMemoryHandle(FileDescriptor(fd, true), guid); |
| +} |
| +#endif // defined(OS_POSIX) && !defined(OS_NACL) |
| + |
| +#if defined(OS_WIN) |
| +// static |
| +bool FieldTrialList::CreateTrialsFromSwitchValue( |
| + const std::string& switch_value) { |
| + SharedMemoryHandle shm = DeserializeSharedMemoryHandleMetadata(switch_value); |
| + if (!shm.IsValid()) |
| + return false; |
| + return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm); |
| +} |
| +#endif // defined(OS_WIN) |
| #if defined(OS_POSIX) && !defined(OS_NACL) |
| // static |
| -bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) { |
| +bool FieldTrialList::CreateTrialsFromDescriptor( |
| + int fd_key, |
| + const std::string& switch_value) { |
| if (!kUseSharedMemoryForFieldTrials) |
| return false; |
| @@ -1158,16 +1197,16 @@ bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) { |
| if (fd == -1) |
| return false; |
| - // TODO(erikchen): Plumb a GUID for this SharedMemoryHandle. |
| - // https://crbug.com/713763. |
| - SharedMemoryHandle shm_handle(FileDescriptor(fd, true), |
| - base::UnguessableToken::Create()); |
| + SharedMemoryHandle shm = |
| + DeserializeSharedMemoryHandleMetadata(fd, switch_value); |
| + if (!shm.IsValid()) |
| + return false; |
| - bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm_handle); |
| + bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm); |
| DCHECK(result); |
| return true; |
| } |
| -#endif |
| +#endif // defined(OS_POSIX) && !defined(OS_NACL) |
| // static |
| bool FieldTrialList::CreateTrialsFromSharedMemoryHandle( |
| @@ -1254,7 +1293,7 @@ void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { |
| // Set |readonly_allocator_handle_| so we can pass it to be inherited and |
| // via the command line. |
| global_->readonly_allocator_handle_ = |
| - CreateReadOnlyHandle(global_->field_trial_allocator_.get()); |
| + global_->field_trial_allocator_->shared_memory()->GetReadOnlyHandle(); |
| #endif |
| } |