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