Chromium Code Reviews| 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/feature_list.h" | 13 #include "base/feature_list.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/pickle.h" | |
| 16 #include "base/process/memory.h" | 17 #include "base/process/memory.h" |
| 17 #include "base/rand_util.h" | 18 #include "base/rand_util.h" |
| 18 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/strings/string_util.h" | 20 #include "base/strings/string_util.h" |
| 20 #include "base/strings/stringprintf.h" | 21 #include "base/strings/stringprintf.h" |
| 21 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
| 22 | 23 |
| 23 namespace base { | 24 namespace base { |
| 24 | 25 |
| 25 namespace { | 26 namespace { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 39 // crbug.com/653874 | 40 // crbug.com/653874 |
| 40 const bool kUseSharedMemoryForFieldTrials = false; | 41 const bool kUseSharedMemoryForFieldTrials = false; |
| 41 | 42 |
| 42 // Constants for the field trial allocator. | 43 // Constants for the field trial allocator. |
| 43 const char kAllocatorName[] = "FieldTrialAllocator"; | 44 const char kAllocatorName[] = "FieldTrialAllocator"; |
| 44 const uint32_t kFieldTrialType = 0xABA17E13 + 1; // SHA1(FieldTrialEntry) v1 | 45 const uint32_t kFieldTrialType = 0xABA17E13 + 1; // SHA1(FieldTrialEntry) v1 |
| 45 #if !defined(OS_NACL) | 46 #if !defined(OS_NACL) |
| 46 const size_t kFieldTrialAllocationSize = 4 << 10; // 4 KiB = one page | 47 const size_t kFieldTrialAllocationSize = 4 << 10; // 4 KiB = one page |
| 47 #endif | 48 #endif |
| 48 | 49 |
| 49 // We create one FieldTrialEntry struct per field trial in shared memory (via | 50 // We create one FieldTrialEntry per field trial in shared memory, via |
| 50 // field_trial_allocator_). It contains whether or not it's activated, and the | 51 // AddToAllocatorWhileLocked. It contains whether or not it's activated, and a |
| 51 // offset from the start of the allocated memory to the group name. We don't | 52 // base::Pickle object that we unpickle and read from. |
| 52 // need the offset into the trial name because that's always a fixed position | |
| 53 // from the start of the struct. Two strings will be appended to the end of this | |
| 54 // structure in allocated memory, so the result will look like this: | |
| 55 // --------------------------------- | |
| 56 // | fte | trial_name | group_name | | |
| 57 // --------------------------------- | |
| 58 struct FieldTrialEntry { | 53 struct FieldTrialEntry { |
| 59 bool activated; | 54 bool activated; |
| 60 uint32_t group_name_offset; | 55 // Size of the pickled structure, NOT the total size of this entry. |
| 56 uint32_t size; | |
| 61 | 57 |
| 62 const char* GetTrialName() const { | 58 bool GetTrialAndGroupName(StringPiece* trial_name, |
|
Alexei Svitkine (slow)
2016/10/28 15:32:22
Please add a comment, specifically mentioning that
| |
| 63 const char* src = | 59 StringPiece* group_name) const { |
| 64 reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)); | 60 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) + |
| 65 return src + sizeof(FieldTrialEntry); | 61 sizeof(FieldTrialEntry); |
| 66 } | |
| 67 | 62 |
| 68 const char* GetGroupName() const { | 63 Pickle pickle(src, this->size); |
|
Alexei Svitkine (slow)
2016/10/28 15:32:22
Nit: No this->
lawrencewu
2016/10/28 15:38:02
Done.
| |
| 69 const char* src = | 64 PickleIterator pickle_iter(pickle); |
| 70 reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)); | 65 |
| 71 return src + this->group_name_offset; | 66 if (!pickle_iter.ReadStringPiece(trial_name)) |
| 67 return false; | |
| 68 if (!pickle_iter.ReadStringPiece(group_name)) | |
| 69 return false; | |
| 70 return true; | |
| 72 } | 71 } |
| 73 }; | 72 }; |
| 74 | 73 |
| 75 // Created a time value based on |year|, |month| and |day_of_month| parameters. | 74 // Created a time value based on |year|, |month| and |day_of_month| parameters. |
| 76 Time CreateTimeFromParams(int year, int month, int day_of_month) { | 75 Time CreateTimeFromParams(int year, int month, int day_of_month) { |
| 77 DCHECK_GT(year, 1970); | 76 DCHECK_GT(year, 1970); |
| 78 DCHECK_GT(month, 0); | 77 DCHECK_GT(month, 0); |
| 79 DCHECK_LT(month, 13); | 78 DCHECK_LT(month, 13); |
| 80 DCHECK_GT(day_of_month, 0); | 79 DCHECK_GT(day_of_month, 0); |
| 81 DCHECK_LT(day_of_month, 32); | 80 DCHECK_LT(day_of_month, 32); |
| (...skipping 709 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 791 return 0; | 790 return 0; |
| 792 AutoLock auto_lock(global_->lock_); | 791 AutoLock auto_lock(global_->lock_); |
| 793 return global_->registered_.size(); | 792 return global_->registered_.size(); |
| 794 } | 793 } |
| 795 | 794 |
| 796 // static | 795 // static |
| 797 void FieldTrialList::CreateTrialsFromSharedMemory( | 796 void FieldTrialList::CreateTrialsFromSharedMemory( |
| 798 std::unique_ptr<SharedMemory> shm) { | 797 std::unique_ptr<SharedMemory> shm) { |
| 799 const SharedPersistentMemoryAllocator shalloc(std::move(shm), 0, | 798 const SharedPersistentMemoryAllocator shalloc(std::move(shm), 0, |
| 800 kAllocatorName, true); | 799 kAllocatorName, true); |
| 801 PersistentMemoryAllocator::Iterator iter(&shalloc); | 800 PersistentMemoryAllocator::Iterator mem_iter(&shalloc); |
| 802 | 801 |
| 803 SharedPersistentMemoryAllocator::Reference ref; | 802 SharedPersistentMemoryAllocator::Reference ref; |
| 804 while ((ref = iter.GetNextOfType(kFieldTrialType)) != | 803 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
| 805 SharedPersistentMemoryAllocator::kReferenceNull) { | 804 SharedPersistentMemoryAllocator::kReferenceNull) { |
| 806 const FieldTrialEntry* entry = | 805 const FieldTrialEntry* entry = |
| 807 shalloc.GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | 806 shalloc.GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); |
| 807 | |
| 808 StringPiece trial_name; | |
| 809 StringPiece group_name; | |
| 810 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) | |
| 811 NOTREACHED(); | |
|
Alexei Svitkine (slow)
2016/10/28 15:32:22
You can still do a continue after the NOTREACHED j
lawrencewu
2016/10/28 15:38:02
Done.
| |
| 812 | |
| 813 // TODO: Convert the API for CreateFieldTrial to take StringPieces. | |
|
Alexei Svitkine (slow)
2016/10/28 15:32:22
TODOs should have a username or crbug here.
So yo
lawrencewu
2016/10/28 15:38:02
Done.
| |
| 808 FieldTrial* trial = | 814 FieldTrial* trial = |
| 809 CreateFieldTrial(entry->GetTrialName(), entry->GetGroupName()); | 815 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); |
| 810 | 816 |
| 811 if (entry->activated) { | 817 if (entry->activated) { |
| 812 // Call |group()| to mark the trial as "used" and notify observers, if | 818 // Call |group()| to mark the trial as "used" and notify observers, if |
| 813 // any. This is useful to ensure that field trials created in child | 819 // any. This is useful to ensure that field trials created in child |
| 814 // processes are properly reported in crash reports. | 820 // processes are properly reported in crash reports. |
| 815 trial->group(); | 821 trial->group(); |
| 816 } | 822 } |
| 817 } | 823 } |
| 818 } | 824 } |
| 819 | 825 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 859 return; | 865 return; |
| 860 | 866 |
| 861 // Or if we've already added it. | 867 // Or if we've already added it. |
| 862 if (field_trial->ref_ != SharedPersistentMemoryAllocator::kReferenceNull) | 868 if (field_trial->ref_ != SharedPersistentMemoryAllocator::kReferenceNull) |
| 863 return; | 869 return; |
| 864 | 870 |
| 865 FieldTrial::State trial_state; | 871 FieldTrial::State trial_state; |
| 866 if (!field_trial->GetState(&trial_state)) | 872 if (!field_trial->GetState(&trial_state)) |
| 867 return; | 873 return; |
| 868 | 874 |
| 869 size_t trial_name_size = trial_state.trial_name.size() + 1; | 875 Pickle pickle; |
| 870 size_t group_name_size = trial_state.group_name.size() + 1; | 876 pickle.WriteString(trial_state.trial_name); |
| 871 size_t size = sizeof(FieldTrialEntry) + trial_name_size + group_name_size; | 877 pickle.WriteString(trial_state.group_name); |
| 872 | 878 |
| 873 // Allocate just enough memory to fit the FieldTrialEntry struct, trial name, | 879 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); |
| 874 // and group name. | |
| 875 SharedPersistentMemoryAllocator::Reference ref = | 880 SharedPersistentMemoryAllocator::Reference ref = |
| 876 allocator->Allocate(size, kFieldTrialType); | 881 allocator->Allocate(total_size, kFieldTrialType); |
| 877 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) | 882 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) |
| 878 return; | 883 return; |
| 879 | 884 |
| 880 // Calculate offsets into the shared memory segment. | |
| 881 FieldTrialEntry* entry = | 885 FieldTrialEntry* entry = |
| 882 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 886 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
| 883 uint32_t trial_name_offset = sizeof(FieldTrialEntry); | 887 entry->activated = trial_state.activated; |
| 884 uint32_t group_name_offset = trial_name_offset + trial_name_size; | 888 entry->size = pickle.size(); |
| 885 | 889 |
| 886 // Copy over the data. | 890 // TODO: Modify base::Pickle to be able to write over a section in memory, so |
|
Alexei Svitkine (slow)
2016/10/28 15:32:22
Same comment about the TODO.
lawrencewu
2016/10/28 15:38:02
Done.
| |
| 887 entry->activated = trial_state.activated; | 891 // we can avoid this memcpy. |
| 888 entry->group_name_offset = group_name_offset; | 892 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); |
| 889 char* trial_name = reinterpret_cast<char*>(entry) + trial_name_offset; | 893 memcpy(dst, pickle.data(), pickle.size()); |
| 890 char* group_name = reinterpret_cast<char*>(entry) + group_name_offset; | |
| 891 memcpy(trial_name, trial_state.trial_name.data(), trial_name_size); | |
| 892 memcpy(group_name, trial_state.group_name.data(), group_name_size); | |
| 893 | |
| 894 // Null terminate the strings. | |
| 895 trial_name[trial_state.trial_name.size()] = '\0'; | |
| 896 group_name[trial_state.group_name.size()] = '\0'; | |
| 897 | 894 |
| 898 allocator->MakeIterable(ref); | 895 allocator->MakeIterable(ref); |
| 899 field_trial->ref_ = ref; | 896 field_trial->ref_ = ref; |
| 900 } | 897 } |
| 901 | 898 |
| 902 // static | 899 // static |
| 903 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( | 900 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( |
| 904 FieldTrial* field_trial) { | 901 FieldTrial* field_trial) { |
| 905 SharedPersistentMemoryAllocator* allocator = | 902 SharedPersistentMemoryAllocator* allocator = |
| 906 global_->field_trial_allocator_.get(); | 903 global_->field_trial_allocator_.get(); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 944 return; | 941 return; |
| 945 } | 942 } |
| 946 AutoLock auto_lock(global_->lock_); | 943 AutoLock auto_lock(global_->lock_); |
| 947 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 944 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
| 948 trial->AddRef(); | 945 trial->AddRef(); |
| 949 trial->SetTrialRegistered(); | 946 trial->SetTrialRegistered(); |
| 950 global_->registered_[trial->trial_name()] = trial; | 947 global_->registered_[trial->trial_name()] = trial; |
| 951 } | 948 } |
| 952 | 949 |
| 953 } // namespace base | 950 } // namespace base |
| OLD | NEW |