Index: base/metrics/field_trial.cc |
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc |
index bb6ae0cb83fd02fa52b8d17584ee911fcef105ca..253170ae1ba2ea40af08e4b4d7b2b63d3e69113e 100644 |
--- a/base/metrics/field_trial.cc |
+++ b/base/metrics/field_trial.cc |
@@ -13,6 +13,7 @@ |
#include "base/feature_list.h" |
#include "base/logging.h" |
#include "base/metrics/histogram_macros.h" |
+#include "base/pickle.h" |
#include "base/process/memory.h" |
#include "base/rand_util.h" |
#include "base/strings/string_number_conversions.h" |
@@ -46,29 +47,31 @@ const uint32_t kFieldTrialType = 0xABA17E13 + 1; // SHA1(FieldTrialEntry) v1 |
const size_t kFieldTrialAllocationSize = 4 << 10; // 4 KiB = one page |
#endif |
-// We create one FieldTrialEntry struct per field trial in shared memory (via |
-// field_trial_allocator_). It contains whether or not it's activated, and the |
-// offset from the start of the allocated memory to the group name. We don't |
-// need the offset into the trial name because that's always a fixed position |
-// from the start of the struct. Two strings will be appended to the end of this |
-// structure in allocated memory, so the result will look like this: |
-// --------------------------------- |
-// | fte | trial_name | group_name | |
-// --------------------------------- |
+// We create one FieldTrialEntry per field trial in shared memory, via |
+// AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a base::Pickle |
+// object that we unpickle and read from. |
struct FieldTrialEntry { |
bool activated; |
- uint32_t group_name_offset; |
- const char* GetTrialName() const { |
- const char* src = |
- reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)); |
- return src + sizeof(FieldTrialEntry); |
- } |
+ // Size of the pickled structure, NOT the total size of this entry. |
+ uint32_t size; |
+ |
+ // Calling this is only valid when the entry is initialized. That is, it |
+ // resides in shared memory and has a pickle containing the trial name and |
+ // group name following it. |
+ bool GetTrialAndGroupName(StringPiece* trial_name, |
+ StringPiece* group_name) const { |
+ char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) + |
+ sizeof(FieldTrialEntry); |
+ |
+ Pickle pickle(src, size); |
+ PickleIterator pickle_iter(pickle); |
- const char* GetGroupName() const { |
- const char* src = |
- reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)); |
- return src + this->group_name_offset; |
+ if (!pickle_iter.ReadStringPiece(trial_name)) |
+ return false; |
+ if (!pickle_iter.ReadStringPiece(group_name)) |
+ return false; |
+ return true; |
} |
}; |
@@ -798,15 +801,25 @@ void FieldTrialList::CreateTrialsFromSharedMemory( |
std::unique_ptr<SharedMemory> shm) { |
const SharedPersistentMemoryAllocator shalloc(std::move(shm), 0, |
kAllocatorName, true); |
- PersistentMemoryAllocator::Iterator iter(&shalloc); |
+ PersistentMemoryAllocator::Iterator mem_iter(&shalloc); |
SharedPersistentMemoryAllocator::Reference ref; |
- while ((ref = iter.GetNextOfType(kFieldTrialType)) != |
+ while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
SharedPersistentMemoryAllocator::kReferenceNull) { |
const FieldTrialEntry* entry = |
shalloc.GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); |
+ |
+ StringPiece trial_name; |
+ StringPiece group_name; |
+ if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) { |
+ NOTREACHED(); |
+ continue; |
+ } |
+ |
+ // TODO(lawrencewu): Convert the API for CreateFieldTrial to take |
+ // StringPieces. |
FieldTrial* trial = |
- CreateFieldTrial(entry->GetTrialName(), entry->GetGroupName()); |
+ CreateFieldTrial(trial_name.as_string(), group_name.as_string()); |
if (entry->activated) { |
// Call |group()| to mark the trial as "used" and notify observers, if |
@@ -866,34 +879,25 @@ void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
if (!field_trial->GetState(&trial_state)) |
return; |
- size_t trial_name_size = trial_state.trial_name.size() + 1; |
- size_t group_name_size = trial_state.group_name.size() + 1; |
- size_t size = sizeof(FieldTrialEntry) + trial_name_size + group_name_size; |
+ Pickle pickle; |
+ pickle.WriteString(trial_state.trial_name); |
+ pickle.WriteString(trial_state.group_name); |
- // Allocate just enough memory to fit the FieldTrialEntry struct, trial name, |
- // and group name. |
+ size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); |
SharedPersistentMemoryAllocator::Reference ref = |
- allocator->Allocate(size, kFieldTrialType); |
+ allocator->Allocate(total_size, kFieldTrialType); |
if (ref == SharedPersistentMemoryAllocator::kReferenceNull) |
return; |
- // Calculate offsets into the shared memory segment. |
FieldTrialEntry* entry = |
allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
- uint32_t trial_name_offset = sizeof(FieldTrialEntry); |
- uint32_t group_name_offset = trial_name_offset + trial_name_size; |
- |
- // Copy over the data. |
entry->activated = trial_state.activated; |
- entry->group_name_offset = group_name_offset; |
- char* trial_name = reinterpret_cast<char*>(entry) + trial_name_offset; |
- char* group_name = reinterpret_cast<char*>(entry) + group_name_offset; |
- memcpy(trial_name, trial_state.trial_name.data(), trial_name_size); |
- memcpy(group_name, trial_state.group_name.data(), group_name_size); |
- |
- // Null terminate the strings. |
- trial_name[trial_state.trial_name.size()] = '\0'; |
- group_name[trial_state.group_name.size()] = '\0'; |
+ entry->size = pickle.size(); |
+ |
+ // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in |
+ // memory, so we can avoid this memcpy. |
+ char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); |
+ memcpy(dst, pickle.data(), pickle.size()); |
allocator->MakeIterable(ref); |
field_trial->ref_ = ref; |