Pass the GUID for the SharedMemoryHandle used by base::FieldTrialList.

SharedMemoryHandle now has a GUID used to track shared memory usage across
processes. The SharedMemoryHandle used by base::FieldTrialList is passed
manually by command line flag and Process creation options. This CL appends the
GUID to the command line flag.

BUG=713763

Review-Url: https://codereview.chromium.org/2862123002
Cr-Commit-Position: refs/heads/master@{#470700}


CrOS-Libchrome-Original-Commit: 2525d998c7c50bce5ca17fb934c2aeb8e27e1db0
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 78602f1..5711aec 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 @@
   }
 }
 
-#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 @@
 #endif
 }
 
+#if !defined(OS_NACL)
+// Returns whether the operation succeeded.
+bool DeserializeGUIDFromStringPieces(base::StringPiece first,
+                                     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 @@
 
 #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 @@
   // 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 @@
     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 @@
     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 @@
   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
-bool FieldTrialList::CreateTrialsFromDescriptor(int fd_key) {
+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,
+    const std::string& switch_value) {
   if (!kUseSharedMemoryForFieldTrials)
     return false;
 
@@ -1158,16 +1197,16 @@
   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 @@
   // 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
 }
 
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index 60a6592..c1f5f6b 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -72,6 +72,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/shared_memory.h"
+#include "base/memory/shared_memory_handle.h"
 #include "base/metrics/persistent_memory_allocator.h"
 #include "base/observer_list_threadsafe.h"
 #include "base/pickle.h"
@@ -573,9 +574,9 @@
 
 #if defined(OS_POSIX) && !defined(OS_NACL)
   // On POSIX, we also need to explicitly pass down this file descriptor that
-  // should be shared with the child process. Returns kInvalidPlatformFile if no
-  // handle exists or was not initialized properly.
-  static PlatformFile GetFieldTrialHandle();
+  // should be shared with the child process. Returns an invalid handle if it
+  // was not initialized properly.
+  static base::SharedMemoryHandle GetFieldTrialHandle();
 #endif
 
   // Adds a switch to the command line containing the field trial state as a
@@ -645,19 +646,39 @@
                            DoNotAddSimulatedFieldTrialsToAllocator);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AssociateFieldTrialParams);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory);
+  FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
+                           SerializeSharedMemoryHandleMetadata);
+
+  // Serialization is used to pass information about the handle to child
+  // processes. It passes a reference to the relevant OS resource, and it passes
+  // a GUID. Serialization and deserialization doesn't actually transport the
+  // underlying OS resource - that must be done by the Process launcher.
+  static std::string SerializeSharedMemoryHandleMetadata(
+      const SharedMemoryHandle& shm);
+#if defined(OS_WIN)
+  static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata(
+      const std::string& switch_value);
+#elif defined(OS_POSIX) && !defined(OS_NACL)
+  static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata(
+      int fd,
+      const std::string& switch_value);
+#endif
 
 #if defined(OS_WIN)
   // Takes in |handle_switch| from the command line which represents the shared
   // memory handle for field trials, parses it, and creates the field trials.
   // Returns true on success, false on failure.
-  static bool CreateTrialsFromHandleSwitch(const std::string& handle_switch);
+  // |switch_value| also contains the serialized GUID.
+  static bool CreateTrialsFromSwitchValue(const std::string& switch_value);
 #endif
 
 #if defined(OS_POSIX) && !defined(OS_NACL)
   // On POSIX systems that use the zygote, we look up the correct fd that backs
   // the shared memory segment containing the field trials by looking it up via
   // an fd key in GlobalDescriptors. Returns true on success, false on failure.
-  static bool CreateTrialsFromDescriptor(int fd_key);
+  // |switch_value| also contains the serialized GUID.
+  static bool CreateTrialsFromDescriptor(int fd_key,
+                                         const std::string& switch_value);
 #endif
 
   // Takes an unmapped SharedMemoryHandle, creates a SharedMemory object from it
@@ -731,7 +752,7 @@
   // Readonly copy of the handle to the allocator. Needs to be a member variable
   // because it's needed from both CopyFieldTrialStateToFlags() and
   // AppendFieldTrialHandleIfNeeded().
-  PlatformFile readonly_allocator_handle_ = kInvalidPlatformFile;
+  base::SharedMemoryHandle readonly_allocator_handle_;
 
   // Tracks whether CreateTrialsFromCommandLine() has been called.
   bool create_trials_from_command_line_called_ = false;
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 54672e6..4ca5c11 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -1369,4 +1369,23 @@
   EXPECT_EQ("value2", shm_params["key2"]);
 }
 
+#if !defined(OS_NACL)
+TEST(FieldTrialListTest, SerializeSharedMemoryHandleMetadata) {
+  std::unique_ptr<base::SharedMemory> shm(new SharedMemory());
+  shm->CreateAndMapAnonymous(4 << 10);
+
+  std::string serialized =
+      FieldTrialList::SerializeSharedMemoryHandleMetadata(shm->handle());
+#if defined(OS_WIN)
+  SharedMemoryHandle deserialized =
+      FieldTrialList::DeserializeSharedMemoryHandleMetadata(serialized);
+#else
+  SharedMemoryHandle deserialized =
+      FieldTrialList::DeserializeSharedMemoryHandleMetadata(-1, serialized);
+#endif
+  EXPECT_EQ(deserialized.GetGUID(), shm->handle().GetGUID());
+  EXPECT_FALSE(deserialized.GetGUID().is_empty());
+}
+#endif  // !defined(OS_NACL)
+
 }  // namespace base