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 "chrome/common/metrics/experiments_helper.h" | 5 #include "chrome/common/metrics/variations_util.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/memory/singleton.h" | 10 #include "base/memory/singleton.h" |
11 #include "base/sha1.h" | 11 #include "base/sha1.h" |
12 #include "base/string16.h" | 12 #include "base/string16.h" |
13 #include "base/stringprintf.h" | 13 #include "base/stringprintf.h" |
14 #include "base/sys_byteorder.h" | 14 #include "base/sys_byteorder.h" |
15 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
16 #include "chrome/common/child_process_logging.h" | 16 #include "chrome/common/child_process_logging.h" |
17 #include "chrome/common/metrics/variation_ids.h" | 17 #include "chrome/common/metrics/variation_ids.h" |
18 | 18 |
19 namespace { | 19 namespace { |
20 | 20 |
21 // The internal singleton accessor for the map, used to keep it thread-safe. | 21 // The internal singleton accessor for the map, used to keep it thread-safe. |
22 class GroupMapAccessor { | 22 class GroupMapAccessor { |
23 public: | 23 public: |
24 // Retrieve the singleton. | 24 // Retrieve the singleton. |
25 static GroupMapAccessor* GetInstance() { | 25 static GroupMapAccessor* GetInstance() { |
26 return Singleton<GroupMapAccessor>::get(); | 26 return Singleton<GroupMapAccessor>::get(); |
27 } | 27 } |
28 | 28 |
29 GroupMapAccessor() {} | 29 GroupMapAccessor() {} |
30 ~GroupMapAccessor() {} | 30 ~GroupMapAccessor() {} |
31 | 31 |
32 // Note that this normally only sets the ID for a group the first time, unless | 32 // Note that this normally only sets the ID for a group the first time, unless |
33 // |force| is set to true, in which case it will always override it. | 33 // |force| is set to true, in which case it will always override it. |
34 void AssociateID(const experiments_helper::SelectedGroupId& group_identifier, | 34 void AssociateID(const chrome_variations::SelectedGroupId& group_identifier, |
35 chrome_variations::VariationID id, | 35 chrome_variations::VariationID id, |
36 const bool force) { | 36 const bool force) { |
37 base::AutoLock scoped_lock(lock_); | 37 base::AutoLock scoped_lock(lock_); |
38 if (force || | 38 if (force || |
39 group_to_id_map_.find(group_identifier) == group_to_id_map_.end()) | 39 group_to_id_map_.find(group_identifier) == group_to_id_map_.end()) |
40 group_to_id_map_[group_identifier] = id; | 40 group_to_id_map_[group_identifier] = id; |
41 } | 41 } |
42 | 42 |
43 chrome_variations::VariationID GetID( | 43 chrome_variations::VariationID GetID( |
44 const experiments_helper::SelectedGroupId& group_identifier) { | 44 const chrome_variations::SelectedGroupId& group_identifier) { |
45 base::AutoLock scoped_lock(lock_); | 45 base::AutoLock scoped_lock(lock_); |
46 GroupToIDMap::const_iterator it = group_to_id_map_.find(group_identifier); | 46 GroupToIDMap::const_iterator it = group_to_id_map_.find(group_identifier); |
47 if (it == group_to_id_map_.end()) | 47 if (it == group_to_id_map_.end()) |
48 return chrome_variations::kEmptyID; | 48 return chrome_variations::kEmptyID; |
49 return it->second; | 49 return it->second; |
50 } | 50 } |
51 | 51 |
52 private: | 52 private: |
53 typedef std::map<experiments_helper::SelectedGroupId, | 53 typedef std::map<chrome_variations::SelectedGroupId, |
54 chrome_variations::VariationID, | 54 chrome_variations::VariationID, |
55 experiments_helper::SelectedGroupIdCompare> GroupToIDMap; | 55 chrome_variations::SelectedGroupIdCompare> GroupToIDMap; |
56 | 56 |
57 base::Lock lock_; | 57 base::Lock lock_; |
58 GroupToIDMap group_to_id_map_; | 58 GroupToIDMap group_to_id_map_; |
59 }; | 59 }; |
60 | 60 |
61 // Creates unique identifier for the trial by hashing a name string, whether | 61 // Creates unique identifier for the trial by hashing a name string, whether |
62 // it's for the field trial or the group name. | 62 // it's for the field trial or the group name. |
63 uint32 HashName(const std::string& name) { | 63 uint32 HashName(const std::string& name) { |
64 // SHA-1 is designed to produce a uniformly random spread in its output space, | 64 // SHA-1 is designed to produce a uniformly random spread in its output space, |
65 // even for nearly-identical inputs. | 65 // even for nearly-identical inputs. |
66 unsigned char sha1_hash[base::kSHA1Length]; | 66 unsigned char sha1_hash[base::kSHA1Length]; |
67 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()), | 67 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()), |
68 name.size(), | 68 name.size(), |
69 sha1_hash); | 69 sha1_hash); |
70 | 70 |
71 COMPILE_ASSERT(sizeof(uint32) < sizeof(sha1_hash), need_more_data); | 71 COMPILE_ASSERT(sizeof(uint32) < sizeof(sha1_hash), need_more_data); |
72 uint32 bits; | 72 uint32 bits; |
73 memcpy(&bits, sha1_hash, sizeof(bits)); | 73 memcpy(&bits, sha1_hash, sizeof(bits)); |
74 | 74 |
75 return base::ByteSwapToLE32(bits); | 75 return base::ByteSwapToLE32(bits); |
76 } | 76 } |
77 | 77 |
78 experiments_helper::SelectedGroupId MakeSelectedGroupId( | 78 chrome_variations::SelectedGroupId MakeSelectedGroupId( |
79 const std::string& trial_name, | 79 const std::string& trial_name, |
80 const std::string& group_name) { | 80 const std::string& group_name) { |
81 experiments_helper::SelectedGroupId id; | 81 chrome_variations::SelectedGroupId id; |
82 id.name = HashName(trial_name); | 82 id.name = HashName(trial_name); |
83 id.group = HashName(group_name); | 83 id.group = HashName(group_name); |
84 return id; | 84 return id; |
85 } | 85 } |
86 | 86 |
87 // Populates |name_group_ids| based on |selected_groups|. | 87 // Populates |name_group_ids| based on |selected_groups|. |
88 void GetFieldTrialSelectedGroupIdsForSelectedGroups( | 88 void GetFieldTrialSelectedGroupIdsForSelectedGroups( |
89 const base::FieldTrial::SelectedGroups& selected_groups, | 89 const base::FieldTrial::SelectedGroups& selected_groups, |
90 std::vector<experiments_helper::SelectedGroupId>* name_group_ids) { | 90 std::vector<chrome_variations::SelectedGroupId>* name_group_ids) { |
91 DCHECK(name_group_ids->empty()); | 91 DCHECK(name_group_ids->empty()); |
92 for (base::FieldTrial::SelectedGroups::const_iterator it = | 92 for (base::FieldTrial::SelectedGroups::const_iterator it = |
93 selected_groups.begin(); it != selected_groups.end(); ++it) { | 93 selected_groups.begin(); it != selected_groups.end(); ++it) { |
94 name_group_ids->push_back(MakeSelectedGroupId(it->trial, it->group)); | 94 name_group_ids->push_back(MakeSelectedGroupId(it->trial, it->group)); |
95 } | 95 } |
96 } | 96 } |
97 | 97 |
98 } // namespace | 98 } // namespace |
99 | 99 |
100 namespace experiments_helper { | 100 namespace chrome_variations { |
101 | 101 |
102 void GetFieldTrialSelectedGroupIds( | 102 void GetFieldTrialSelectedGroupIds( |
103 std::vector<SelectedGroupId>* name_group_ids) { | 103 std::vector<SelectedGroupId>* name_group_ids) { |
104 DCHECK(name_group_ids->empty()); | 104 DCHECK(name_group_ids->empty()); |
105 // A note on thread safety: Since GetFieldTrialSelectedGroups is thread | 105 // A note on thread safety: Since GetFieldTrialSelectedGroups is thread |
106 // safe, and we operate on a separate list of that data, this function is | 106 // safe, and we operate on a separate list of that data, this function is |
107 // technically thread safe as well, with respect to the FieldTriaList data. | 107 // technically thread safe as well, with respect to the FieldTriaList data. |
108 base::FieldTrial::SelectedGroups selected_groups; | 108 base::FieldTrial::SelectedGroups selected_groups; |
109 base::FieldTrialList::GetFieldTrialSelectedGroups(&selected_groups); | 109 base::FieldTrialList::GetFieldTrialSelectedGroups(&selected_groups); |
110 GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups, | 110 GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups, |
(...skipping 14 matching lines...) Expand all Loading... |
125 MakeSelectedGroupId(trial_name, group_name), id, true); | 125 MakeSelectedGroupId(trial_name, group_name), id, true); |
126 } | 126 } |
127 | 127 |
128 chrome_variations::VariationID GetGoogleVariationID( | 128 chrome_variations::VariationID GetGoogleVariationID( |
129 const std::string& trial_name, | 129 const std::string& trial_name, |
130 const std::string& group_name) { | 130 const std::string& group_name) { |
131 return GroupMapAccessor::GetInstance()->GetID( | 131 return GroupMapAccessor::GetInstance()->GetID( |
132 MakeSelectedGroupId(trial_name, group_name)); | 132 MakeSelectedGroupId(trial_name, group_name)); |
133 } | 133 } |
134 | 134 |
135 void GenerateExperimentChunks(const std::vector<string16>& experiments, | 135 void GenerateVariationChunks(const std::vector<string16>& experiments, |
136 std::vector<string16>* chunks) { | 136 std::vector<string16>* chunks) { |
137 string16 current_chunk; | 137 string16 current_chunk; |
138 for (size_t i = 0; i < experiments.size(); ++i) { | 138 for (size_t i = 0; i < experiments.size(); ++i) { |
139 const size_t needed_length = | 139 const size_t needed_length = |
140 (current_chunk.empty() ? 1 : 0) + experiments[i].length(); | 140 (current_chunk.empty() ? 1 : 0) + experiments[i].length(); |
141 if (current_chunk.length() + needed_length > kMaxExperimentChunkSize) { | 141 if (current_chunk.length() + needed_length > kMaxVariationChunkSize) { |
142 chunks->push_back(current_chunk); | 142 chunks->push_back(current_chunk); |
143 current_chunk = experiments[i]; | 143 current_chunk = experiments[i]; |
144 } else { | 144 } else { |
145 if (!current_chunk.empty()) | 145 if (!current_chunk.empty()) |
146 current_chunk.push_back(','); | 146 current_chunk.push_back(','); |
147 current_chunk += experiments[i]; | 147 current_chunk += experiments[i]; |
148 } | 148 } |
149 } | 149 } |
150 if (!current_chunk.empty()) | 150 if (!current_chunk.empty()) |
151 chunks->push_back(current_chunk); | 151 chunks->push_back(current_chunk); |
152 } | 152 } |
153 | 153 |
154 void SetChildProcessLoggingExperimentList() { | 154 void SetChildProcessLoggingVariationList() { |
155 std::vector<SelectedGroupId> name_group_ids; | 155 std::vector<SelectedGroupId> name_group_ids; |
156 GetFieldTrialSelectedGroupIds(&name_group_ids); | 156 GetFieldTrialSelectedGroupIds(&name_group_ids); |
157 std::vector<string16> experiment_strings(name_group_ids.size()); | 157 std::vector<string16> experiment_strings(name_group_ids.size()); |
158 for (size_t i = 0; i < name_group_ids.size(); ++i) { | 158 for (size_t i = 0; i < name_group_ids.size(); ++i) { |
159 // Wish there was a StringPrintf for string16... :-( | 159 experiment_strings[i] = UTF8ToUTF16(base::StringPrintf( |
160 experiment_strings[i] = WideToUTF16(base::StringPrintf( | 160 "%x-%x", name_group_ids[i].name, name_group_ids[i].group)); |
161 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group)); | |
162 } | 161 } |
163 child_process_logging::SetExperimentList(experiment_strings); | 162 child_process_logging::SetExperimentList(experiment_strings); |
164 } | 163 } |
165 | 164 |
166 } // namespace experiments_helper | 165 } // namespace chrome_variations |
167 | 166 |
168 // Functions below are exposed for testing explicitly behind this namespace. | 167 // Functions below are exposed for testing explicitly behind this namespace. |
169 // They simply wrap existing functions in this file. | 168 // They simply wrap existing functions in this file. |
170 namespace testing { | 169 namespace testing { |
171 | 170 |
172 void TestGetFieldTrialSelectedGroupIdsForSelectedGroups( | 171 void TestGetFieldTrialSelectedGroupIdsForSelectedGroups( |
173 const base::FieldTrial::SelectedGroups& selected_groups, | 172 const base::FieldTrial::SelectedGroups& selected_groups, |
174 std::vector<experiments_helper::SelectedGroupId>* name_group_ids) { | 173 std::vector<chrome_variations::SelectedGroupId>* name_group_ids) { |
175 ::GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups, | 174 ::GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups, |
176 name_group_ids); | 175 name_group_ids); |
177 } | 176 } |
178 | 177 |
179 uint32 TestHashName(const std::string& name) { | 178 uint32 TestHashName(const std::string& name) { |
180 return ::HashName(name); | 179 return ::HashName(name); |
181 } | 180 } |
182 | 181 |
183 } // namespace testing | 182 } // namespace testing |
OLD | NEW |