Chromium Code Reviews| Index: base/metrics/field_trial.cc |
| diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc |
| index b53c932bd2008162510603e62df46030b84a8912..c5c6af0c5a845290a7e87108ab46cd47725a28f9 100644 |
| --- a/base/metrics/field_trial.cc |
| +++ b/base/metrics/field_trial.cc |
| @@ -12,7 +12,6 @@ |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/metrics/field_trial_param_associator.h" |
| -#include "base/pickle.h" |
| #include "base/process/memory.h" |
| #include "base/rand_util.h" |
| #include "base/strings/string_number_conversions.h" |
| @@ -69,70 +68,6 @@ const uint32_t kFieldTrialType = 0xABA17E13 + 2; // SHA1(FieldTrialEntry) v2 |
| const size_t kFieldTrialAllocationSize = 128 << 10; // 128 KiB |
| #endif |
| -// 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. Any changes to this structure requires |
| -// a bump in kFieldTrialType id defined above. |
| -struct FieldTrialEntry { |
| - // Expected size for 32/64-bit check. |
| - static constexpr size_t kExpectedInstanceSize = 8; |
| - |
| - // Whether or not this field trial is activated. This is really just a boolean |
| - // but marked as a uint32_t for portability reasons. |
| - uint32_t activated; |
| - |
| - // Size of the pickled structure, NOT the total size of this entry. |
| - uint32_t pickle_size; |
| - |
| - // Returns an iterator over the data containing names and params. |
| - PickleIterator GetPickleIterator() const { |
| - const char* src = |
| - reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry); |
| - |
| - Pickle pickle(src, pickle_size); |
| - return PickleIterator(pickle); |
| - } |
| - |
| - // Takes the iterator and writes out the first two items into |trial_name| and |
| - // |group_name|. |
| - bool ReadStringPair(PickleIterator* iter, |
| - StringPiece* trial_name, |
| - StringPiece* group_name) const { |
| - if (!iter->ReadStringPiece(trial_name)) |
| - return false; |
| - if (!iter->ReadStringPiece(group_name)) |
| - return false; |
| - return true; |
| - } |
| - |
| - // 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 { |
| - PickleIterator iter = GetPickleIterator(); |
| - return ReadStringPair(&iter, trial_name, group_name); |
| - } |
| - |
| - // Calling this is only valid when the entry is initialized as well. Reads the |
| - // parameters following the trial and group name and stores them as key-value |
| - // mappings in |params|. |
| - bool GetParams(std::map<std::string, std::string>* params) const { |
| - PickleIterator iter = GetPickleIterator(); |
| - StringPiece tmp; |
| - if (!ReadStringPair(&iter, &tmp, &tmp)) |
| - return false; |
| - |
| - while (true) { |
| - StringPiece key; |
| - StringPiece value; |
| - if (!ReadStringPair(&iter, &key, &value)) |
| - return key.empty(); // Non-empty is bad: got one of a pair. |
| - (*params)[key.as_string()] = value.as_string(); |
| - } |
| - } |
| -}; |
| - |
| // Writes out string1 and then string2 to pickle. |
| bool WriteStringPair(Pickle* pickle, |
| const StringPiece& string1, |
| @@ -314,6 +249,48 @@ FieldTrial::State::State(const State& other) = default; |
| FieldTrial::State::~State() {} |
| +PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const { |
| + const char* src = |
| + reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry); |
| + |
| + Pickle pickle(src, pickle_size); |
| + return PickleIterator(pickle); |
| +} |
| + |
| +bool FieldTrial::FieldTrialEntry::ReadStringPair( |
| + PickleIterator* iter, |
| + StringPiece* trial_name, |
| + StringPiece* group_name) const { |
| + if (!iter->ReadStringPiece(trial_name)) |
| + return false; |
| + if (!iter->ReadStringPiece(group_name)) |
| + return false; |
| + return true; |
| +} |
| + |
| +bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName( |
| + StringPiece* trial_name, |
| + StringPiece* group_name) const { |
| + PickleIterator iter = GetPickleIterator(); |
| + return ReadStringPair(&iter, trial_name, group_name); |
| +} |
| + |
| +bool FieldTrial::FieldTrialEntry::GetParams( |
| + std::map<std::string, std::string>* params) const { |
| + PickleIterator iter = GetPickleIterator(); |
| + StringPiece tmp; |
| + if (!ReadStringPair(&iter, &tmp, &tmp)) |
| + return false; |
| + |
| + while (true) { |
| + StringPiece key; |
| + StringPiece value; |
| + if (!ReadStringPair(&iter, &key, &value)) |
| + return key.empty(); // Non-empty is bad: got one of a pair. |
| + (*params)[key.as_string()] = value.as_string(); |
| + } |
| +} |
| + |
| void FieldTrial::Disable() { |
| DCHECK(!group_reported_); |
| enable_field_trial_ = false; |
| @@ -742,8 +719,9 @@ void FieldTrialList::GetInitiallyActiveFieldTrials( |
| FieldTrial::FieldTrialRef ref; |
| while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| SharedPersistentMemoryAllocator::kReferenceNull) { |
| - const FieldTrialEntry* entry = |
| - allocator->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); |
| + const FieldTrial::FieldTrialEntry* entry = |
| + allocator->GetAsObject<const FieldTrial::FieldTrialEntry>( |
| + ref, kFieldTrialType); |
| StringPiece trial_name; |
| StringPiece group_name; |
| @@ -985,10 +963,12 @@ void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { |
| if (!global_) |
| return; |
| if (is_locked) { |
| - AddToAllocatorWhileLocked(field_trial); |
| + AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| + field_trial); |
| } else { |
| AutoLock auto_lock(global_->lock_); |
| - AddToAllocatorWhileLocked(field_trial); |
| + AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| + field_trial); |
| } |
| } |
| @@ -1047,13 +1027,14 @@ bool FieldTrialList::GetParamsFromSharedMemory( |
| if (!field_trial->ref_) |
| return false; |
| - const FieldTrialEntry* entry = |
| - global_->field_trial_allocator_->GetAsObject<const FieldTrialEntry>( |
| - field_trial->ref_, kFieldTrialType); |
| + const FieldTrial::FieldTrialEntry* entry = |
| + global_->field_trial_allocator_ |
| + ->GetAsObject<const FieldTrial::FieldTrialEntry>(field_trial->ref_, |
| + kFieldTrialType); |
| size_t allocated_size = |
| global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); |
| - size_t actual_size = sizeof(FieldTrialEntry) + entry->pickle_size; |
| + size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size; |
| if (allocated_size < actual_size) |
| return false; |
| @@ -1083,9 +1064,9 @@ void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { |
| while ((prev_ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| FieldTrialAllocator::kReferenceNull) { |
| // Get the existing field trial entry in shared memory. |
| - const FieldTrialEntry* prev_entry = |
| - allocator->GetAsObject<const FieldTrialEntry>(prev_ref, |
| - kFieldTrialType); |
| + const FieldTrial::FieldTrialEntry* prev_entry = |
| + allocator->GetAsObject<const FieldTrial::FieldTrialEntry>( |
| + prev_ref, kFieldTrialType); |
| StringPiece trial_name; |
| StringPiece group_name; |
| if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) |
| @@ -1095,17 +1076,19 @@ void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { |
| Pickle pickle; |
| pickle.WriteString(trial_name); |
| pickle.WriteString(group_name); |
| - size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); |
| + size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); |
| FieldTrial::FieldTrialRef new_ref = |
| allocator->Allocate(total_size, kFieldTrialType); |
| - FieldTrialEntry* new_entry = |
| - allocator->GetAsObject<FieldTrialEntry>(new_ref, kFieldTrialType); |
| + FieldTrial::FieldTrialEntry* new_entry = |
| + allocator->GetAsObject<FieldTrial::FieldTrialEntry>(new_ref, |
| + kFieldTrialType); |
| new_entry->activated = prev_entry->activated; |
| new_entry->pickle_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*>(new_entry) + sizeof(FieldTrialEntry); |
| + char* dst = reinterpret_cast<char*>(new_entry) + |
| + sizeof(FieldTrial::FieldTrialEntry); |
| memcpy(dst, pickle.data(), pickle.size()); |
| // Update the ref on the field trial and add it to the list to be made |
| @@ -1123,6 +1106,34 @@ void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { |
| } |
| } |
| +// static |
| +void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator( |
| + PersistentMemoryAllocator* allocator) { |
| + if (!global_) |
| + return; |
| + AutoLock auto_lock(global_->lock_); |
| + for (const auto& registered : global_->registered_) { |
| + AddToAllocatorWhileLocked(allocator, registered.second); |
| + } |
| +} |
| + |
| +// static |
| +std::vector<FieldTrial::FieldTrialEntry*> |
| +FieldTrialList::GetAllFieldTrialsFromPersistentAllocator( |
| + PersistentMemoryAllocator* allocator) { |
| + std::vector<FieldTrial::FieldTrialEntry*> field_trial_entries; |
| + FieldTrial::FieldTrialRef ref; |
| + FieldTrialAllocator::Iterator iter(allocator); |
| + while ((ref = iter.GetNextOfType(kFieldTrialType)) != |
| + FieldTrialAllocator::kReferenceNull) { |
| + FieldTrial::FieldTrialEntry* entry = |
|
bcwhite
2016/12/09 19:40:28
const
lawrencewu
2016/12/09 19:47:59
Done.
|
| + allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, |
| + kFieldTrialType); |
| + field_trial_entries.push_back(entry); |
| + } |
| + return field_trial_entries; |
| +} |
| + |
| #if defined(OS_WIN) |
| // static |
| bool FieldTrialList::CreateTrialsFromHandleSwitch( |
| @@ -1159,8 +1170,9 @@ bool FieldTrialList::CreateTrialsFromSharedMemory( |
| FieldTrial::FieldTrialRef ref; |
| while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| FieldTrialAllocator::kReferenceNull) { |
| - const FieldTrialEntry* entry = |
| - shalloc->GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); |
| + const FieldTrial::FieldTrialEntry* entry = |
| + shalloc->GetAsObject<const FieldTrial::FieldTrialEntry>( |
| + ref, kFieldTrialType); |
| StringPiece trial_name; |
| StringPiece group_name; |
| @@ -1210,7 +1222,8 @@ void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { |
| // Add all existing field trials. |
| for (const auto& registered : global_->registered_) { |
| - AddToAllocatorWhileLocked(registered.second); |
| + AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| + registered.second); |
| } |
| // Add all existing features. |
| @@ -1227,9 +1240,9 @@ void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { |
| #endif |
| // static |
| -void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
| - FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); |
| - |
| +void FieldTrialList::AddToAllocatorWhileLocked( |
| + PersistentMemoryAllocator* allocator, |
| + FieldTrial* field_trial) { |
| // Don't do anything if the allocator hasn't been instantiated yet. |
| if (allocator == nullptr) |
| return; |
| @@ -1254,7 +1267,7 @@ void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
| return; |
| } |
| - size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); |
| + size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); |
| FieldTrial::FieldTrialRef ref = |
| allocator->Allocate(total_size, kFieldTrialType); |
| if (ref == FieldTrialAllocator::kReferenceNull) { |
| @@ -1262,14 +1275,15 @@ void FieldTrialList::AddToAllocatorWhileLocked(FieldTrial* field_trial) { |
| return; |
| } |
| - FieldTrialEntry* entry = |
| - allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
| + FieldTrial::FieldTrialEntry* entry = |
| + allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, kFieldTrialType); |
| entry->activated = trial_state.activated; |
| entry->pickle_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); |
| + char* dst = |
| + reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry); |
| memcpy(dst, pickle.data(), pickle.size()); |
| allocator->MakeIterable(ref); |
| @@ -1289,13 +1303,15 @@ void FieldTrialList::ActivateFieldTrialEntryWhileLocked( |
| if (ref == FieldTrialAllocator::kReferenceNull) { |
| // It's fine to do this even if the allocator hasn't been instantiated |
| // yet -- it'll just return early. |
| - AddToAllocatorWhileLocked(field_trial); |
| + AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), |
| + field_trial); |
| } else { |
| // It's also okay to do this even though the callee doesn't have a lock -- |
| // the only thing that happens on a stale read here is a slight performance |
| // hit from the child re-synchronizing activation state. |
| - FieldTrialEntry* entry = |
| - allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
| + FieldTrial::FieldTrialEntry* entry = |
| + allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref, |
| + kFieldTrialType); |
| entry->activated = true; |
| } |
| } |