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. |
Alexei Svitkine (slow)
2016/10/28 15:41:32
Nit: Remove "It contains whether or not it's activ
| |
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. |
Alexei Svitkine (slow)
2016/10/28 15:41:32
Nit: Add a new line above the comment.
| |
56 uint32_t size; | |
61 | 57 |
62 const char* GetTrialName() const { | 58 // Calling this is only valid when the entry is initialized. That is, it |
63 const char* src = | 59 // resides in shared memory and has a pickle containing the trial name and |
64 reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)); | 60 // group name following it. |
65 return src + sizeof(FieldTrialEntry); | 61 bool GetTrialAndGroupName(StringPiece* trial_name, |
66 } | 62 StringPiece* group_name) const { |
63 char* src = reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)) + | |
64 sizeof(FieldTrialEntry); | |
67 | 65 |
68 const char* GetGroupName() const { | 66 Pickle pickle(src, size); |
69 const char* src = | 67 PickleIterator pickle_iter(pickle); |
70 reinterpret_cast<char*>(const_cast<FieldTrialEntry*>(this)); | 68 |
71 return src + this->group_name_offset; | 69 if (!pickle_iter.ReadStringPiece(trial_name)) |
70 return false; | |
71 if (!pickle_iter.ReadStringPiece(group_name)) | |
72 return false; | |
73 return true; | |
72 } | 74 } |
73 }; | 75 }; |
74 | 76 |
75 // Created a time value based on |year|, |month| and |day_of_month| parameters. | 77 // Created a time value based on |year|, |month| and |day_of_month| parameters. |
76 Time CreateTimeFromParams(int year, int month, int day_of_month) { | 78 Time CreateTimeFromParams(int year, int month, int day_of_month) { |
77 DCHECK_GT(year, 1970); | 79 DCHECK_GT(year, 1970); |
78 DCHECK_GT(month, 0); | 80 DCHECK_GT(month, 0); |
79 DCHECK_LT(month, 13); | 81 DCHECK_LT(month, 13); |
80 DCHECK_GT(day_of_month, 0); | 82 DCHECK_GT(day_of_month, 0); |
81 DCHECK_LT(day_of_month, 32); | 83 DCHECK_LT(day_of_month, 32); |
(...skipping 709 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
791 return 0; | 793 return 0; |
792 AutoLock auto_lock(global_->lock_); | 794 AutoLock auto_lock(global_->lock_); |
793 return global_->registered_.size(); | 795 return global_->registered_.size(); |
794 } | 796 } |
795 | 797 |
796 // static | 798 // static |
797 void FieldTrialList::CreateTrialsFromSharedMemory( | 799 void FieldTrialList::CreateTrialsFromSharedMemory( |
798 std::unique_ptr<SharedMemory> shm) { | 800 std::unique_ptr<SharedMemory> shm) { |
799 const SharedPersistentMemoryAllocator shalloc(std::move(shm), 0, | 801 const SharedPersistentMemoryAllocator shalloc(std::move(shm), 0, |
800 kAllocatorName, true); | 802 kAllocatorName, true); |
801 PersistentMemoryAllocator::Iterator iter(&shalloc); | 803 PersistentMemoryAllocator::Iterator mem_iter(&shalloc); |
802 | 804 |
803 SharedPersistentMemoryAllocator::Reference ref; | 805 SharedPersistentMemoryAllocator::Reference ref; |
804 while ((ref = iter.GetNextOfType(kFieldTrialType)) != | 806 while ((ref = mem_iter.GetNextOfType(kFieldTrialType)) != |
805 SharedPersistentMemoryAllocator::kReferenceNull) { | 807 SharedPersistentMemoryAllocator::kReferenceNull) { |
806 const FieldTrialEntry* entry = | 808 const FieldTrialEntry* entry = |
807 shalloc.GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); | 809 shalloc.GetAsObject<const FieldTrialEntry>(ref, kFieldTrialType); |
810 | |
811 StringPiece trial_name; | |
812 StringPiece group_name; | |
813 if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) { | |
814 NOTREACHED(); | |
815 continue; | |
816 } | |
817 | |
818 // TODO(lawrencewu): Convert the API for CreateFieldTrial to take | |
819 // StringPieces. | |
808 FieldTrial* trial = | 820 FieldTrial* trial = |
809 CreateFieldTrial(entry->GetTrialName(), entry->GetGroupName()); | 821 CreateFieldTrial(trial_name.as_string(), group_name.as_string()); |
810 | 822 |
811 if (entry->activated) { | 823 if (entry->activated) { |
812 // Call |group()| to mark the trial as "used" and notify observers, if | 824 // 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 | 825 // any. This is useful to ensure that field trials created in child |
814 // processes are properly reported in crash reports. | 826 // processes are properly reported in crash reports. |
815 trial->group(); | 827 trial->group(); |
816 } | 828 } |
817 } | 829 } |
818 } | 830 } |
819 | 831 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
859 return; | 871 return; |
860 | 872 |
861 // Or if we've already added it. | 873 // Or if we've already added it. |
862 if (field_trial->ref_ != SharedPersistentMemoryAllocator::kReferenceNull) | 874 if (field_trial->ref_ != SharedPersistentMemoryAllocator::kReferenceNull) |
863 return; | 875 return; |
864 | 876 |
865 FieldTrial::State trial_state; | 877 FieldTrial::State trial_state; |
866 if (!field_trial->GetState(&trial_state)) | 878 if (!field_trial->GetState(&trial_state)) |
867 return; | 879 return; |
868 | 880 |
869 size_t trial_name_size = trial_state.trial_name.size() + 1; | 881 Pickle pickle; |
870 size_t group_name_size = trial_state.group_name.size() + 1; | 882 pickle.WriteString(trial_state.trial_name); |
871 size_t size = sizeof(FieldTrialEntry) + trial_name_size + group_name_size; | 883 pickle.WriteString(trial_state.group_name); |
872 | 884 |
873 // Allocate just enough memory to fit the FieldTrialEntry struct, trial name, | 885 size_t total_size = sizeof(FieldTrialEntry) + pickle.size(); |
874 // and group name. | |
875 SharedPersistentMemoryAllocator::Reference ref = | 886 SharedPersistentMemoryAllocator::Reference ref = |
876 allocator->Allocate(size, kFieldTrialType); | 887 allocator->Allocate(total_size, kFieldTrialType); |
877 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) | 888 if (ref == SharedPersistentMemoryAllocator::kReferenceNull) |
878 return; | 889 return; |
879 | 890 |
880 // Calculate offsets into the shared memory segment. | |
881 FieldTrialEntry* entry = | 891 FieldTrialEntry* entry = |
882 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); | 892 allocator->GetAsObject<FieldTrialEntry>(ref, kFieldTrialType); |
883 uint32_t trial_name_offset = sizeof(FieldTrialEntry); | 893 entry->activated = trial_state.activated; |
884 uint32_t group_name_offset = trial_name_offset + trial_name_size; | 894 entry->size = pickle.size(); |
885 | 895 |
886 // Copy over the data. | 896 // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in |
887 entry->activated = trial_state.activated; | 897 // memory, so we can avoid this memcpy. |
888 entry->group_name_offset = group_name_offset; | 898 char* dst = reinterpret_cast<char*>(entry) + sizeof(FieldTrialEntry); |
889 char* trial_name = reinterpret_cast<char*>(entry) + trial_name_offset; | 899 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 | 900 |
898 allocator->MakeIterable(ref); | 901 allocator->MakeIterable(ref); |
899 field_trial->ref_ = ref; | 902 field_trial->ref_ = ref; |
900 } | 903 } |
901 | 904 |
902 // static | 905 // static |
903 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( | 906 void FieldTrialList::ActivateFieldTrialEntryWhileLocked( |
904 FieldTrial* field_trial) { | 907 FieldTrial* field_trial) { |
905 SharedPersistentMemoryAllocator* allocator = | 908 SharedPersistentMemoryAllocator* allocator = |
906 global_->field_trial_allocator_.get(); | 909 global_->field_trial_allocator_.get(); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
944 return; | 947 return; |
945 } | 948 } |
946 AutoLock auto_lock(global_->lock_); | 949 AutoLock auto_lock(global_->lock_); |
947 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); | 950 CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); |
948 trial->AddRef(); | 951 trial->AddRef(); |
949 trial->SetTrialRegistered(); | 952 trial->SetTrialRegistered(); |
950 global_->registered_[trial->trial_name()] = trial; | 953 global_->registered_[trial->trial_name()] = trial; |
951 } | 954 } |
952 | 955 |
953 } // namespace base | 956 } // namespace base |
OLD | NEW |