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/experiments_helper.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/memory/singleton.h" | 9 #include "base/memory/singleton.h" |
10 #include "base/sha1.h" | |
10 #include "base/string16.h" | 11 #include "base/string16.h" |
11 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
13 #include "base/sys_byteorder.h" | |
12 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
13 #include "chrome/common/child_process_logging.h" | 15 #include "chrome/common/child_process_logging.h" |
14 | 16 |
15 namespace { | 17 namespace { |
16 | 18 |
17 // We need to pass a Compare class to the std::map template since NameGroupId | |
18 // is a user-defined type. | |
19 struct NameGroupIdCompare { | |
20 bool operator() (const base::FieldTrial::NameGroupId& lhs, | |
21 const base::FieldTrial::NameGroupId& rhs) const { | |
22 // The group and name fields are just SHA-1 Hashes, so we just need to treat | |
23 // them as IDs and do a less-than comparison. We test group first, since | |
24 // name is more likely to collide. | |
25 return lhs.group == rhs.group ? lhs.name < rhs.name : | |
26 lhs.group < rhs.group; | |
27 } | |
28 }; | |
29 | |
30 // The internal singleton accessor for the map, used to keep it thread-safe. | 19 // The internal singleton accessor for the map, used to keep it thread-safe. |
31 class GroupMapAccessor { | 20 class GroupMapAccessor { |
32 public: | 21 public: |
33 // Retrieve the singleton. | 22 // Retrieve the singleton. |
34 static GroupMapAccessor* GetInstance() { | 23 static GroupMapAccessor* GetInstance() { |
35 return Singleton<GroupMapAccessor>::get(); | 24 return Singleton<GroupMapAccessor>::get(); |
36 } | 25 } |
37 | 26 |
38 GroupMapAccessor() {} | 27 GroupMapAccessor() {} |
39 ~GroupMapAccessor() {} | 28 ~GroupMapAccessor() {} |
40 | 29 |
41 void AssociateID(const base::FieldTrial::NameGroupId& group_identifier, | 30 void AssociateID(const experiments_helper::NameGroupId& group_identifier, |
42 experiments_helper::GoogleExperimentID id) { | 31 experiments_helper::GoogleExperimentID id) { |
43 base::AutoLock scoped_lock(lock_); | 32 base::AutoLock scoped_lock(lock_); |
44 DCHECK(group_to_id_map_.find(group_identifier) == group_to_id_map_.end()) << | 33 DCHECK(group_to_id_map_.find(group_identifier) == group_to_id_map_.end()) << |
45 "You can associate a group with a GoogleExperimentID only once."; | 34 "You can associate a group with a GoogleExperimentID only once."; |
46 group_to_id_map_[group_identifier] = id; | 35 group_to_id_map_[group_identifier] = id; |
47 } | 36 } |
48 | 37 |
49 experiments_helper::GoogleExperimentID GetID( | 38 experiments_helper::GoogleExperimentID GetID( |
50 const base::FieldTrial::NameGroupId& group_identifier) { | 39 const experiments_helper::NameGroupId& group_identifier) { |
51 base::AutoLock scoped_lock(lock_); | 40 base::AutoLock scoped_lock(lock_); |
52 GroupToIDMap::const_iterator it = group_to_id_map_.find(group_identifier); | 41 GroupToIDMap::const_iterator it = group_to_id_map_.find(group_identifier); |
53 if (it == group_to_id_map_.end()) | 42 if (it == group_to_id_map_.end()) |
54 return experiments_helper::kEmptyGoogleExperimentID; | 43 return experiments_helper::kEmptyGoogleExperimentID; |
55 return it->second; | 44 return it->second; |
56 } | 45 } |
57 | 46 |
58 private: | 47 private: |
59 typedef std::map<base::FieldTrial::NameGroupId, | 48 typedef std::map<experiments_helper::NameGroupId, |
60 experiments_helper::GoogleExperimentID, NameGroupIdCompare> GroupToIDMap; | 49 experiments_helper::GoogleExperimentID, |
50 experiments_helper::NameGroupIdCompare> GroupToIDMap; | |
61 | 51 |
62 base::Lock lock_; | 52 base::Lock lock_; |
63 GroupToIDMap group_to_id_map_; | 53 GroupToIDMap group_to_id_map_; |
64 }; | 54 }; |
65 | 55 |
56 // Creates unique identifier for the trial by hashing a name string, whether | |
57 // it's for the field trial or the group name. | |
58 uint32 HashName(const std::string& name) { | |
59 // SHA-1 is designed to produce a uniformly random spread in its output space, | |
60 // even for nearly-identical inputs. | |
61 unsigned char sha1_hash[base::kSHA1Length]; | |
62 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()), | |
63 name.size(), | |
64 sha1_hash); | |
65 | |
66 COMPILE_ASSERT(sizeof(uint32) < sizeof(sha1_hash), need_more_data); | |
67 uint32 bits; | |
68 memcpy(&bits, sha1_hash, sizeof(bits)); | |
69 | |
70 return base::ByteSwapToLE32(bits); | |
71 } | |
72 | |
73 experiments_helper::NameGroupId MakeNameGroupId( | |
74 const std::string& trial_name, | |
75 const std::string& group_name) { | |
jar (doing other things)
2012/04/26 22:24:22
nit: This would probably be cleaner taking a:
cons
SteveT
2012/04/26 23:42:47
I'm going to have to push back on this one... This
jar (doing other things)
2012/04/27 19:05:44
Is your concern focused on a performance impact?
SteveT
2012/04/27 19:15:13
I'm currently more concerned about code cleanlines
jar (doing other things)
2012/04/27 19:31:55
The win is not that big either way (currently)...
| |
76 experiments_helper::NameGroupId id; | |
77 id.name = HashName(trial_name); | |
78 id.group = HashName(group_name); | |
79 return id; | |
80 } | |
81 | |
82 // Populates |name_group_ids| based on |selected_groups|. | |
83 void GetFieldTrialNameGroupIdsForSelectedGroups( | |
84 const base::FieldTrial::SelectedGroups& selected_groups, | |
85 std::vector<experiments_helper::NameGroupId>* name_group_ids) { | |
86 DCHECK(name_group_ids->empty()); | |
87 for (base::FieldTrial::SelectedGroups::const_iterator it = | |
88 selected_groups.begin(); it != selected_groups.end(); ++it) { | |
89 name_group_ids->push_back(MakeNameGroupId(it->trial, it->group)); | |
90 } | |
91 } | |
92 | |
66 } // namespace | 93 } // namespace |
67 | 94 |
68 namespace experiments_helper { | 95 namespace experiments_helper { |
69 | 96 |
70 const GoogleExperimentID kEmptyGoogleExperimentID = 0; | 97 const GoogleExperimentID kEmptyGoogleExperimentID = 0; |
71 | 98 |
72 void AssociateGoogleExperimentID( | 99 void GetFieldTrialNameGroupIds(std::vector<NameGroupId>* name_group_ids) { |
73 const base::FieldTrial::NameGroupId& group_identifier, | 100 DCHECK(name_group_ids->empty()); |
74 GoogleExperimentID id) { | 101 // A note on thread safety: Since GetFieldTrialSelectedGroups is thread |
75 GroupMapAccessor::GetInstance()->AssociateID(group_identifier, id); | 102 // safe, and we operate on a separate list of that data, this function is |
103 // technically thread safe as well, with respect to the FieldTriaList data. | |
104 base::FieldTrial::SelectedGroups selected_groups; | |
105 base::FieldTrialList::GetFieldTrialSelectedGroups(&selected_groups); | |
106 GetFieldTrialNameGroupIdsForSelectedGroups(selected_groups, name_group_ids); | |
76 } | 107 } |
77 | 108 |
78 GoogleExperimentID GetGoogleExperimentID( | 109 void AssociateGoogleExperimentID(const std::string& trial_name, |
79 const base::FieldTrial::NameGroupId& group_identifier) { | 110 const std::string& group_name, |
80 return GroupMapAccessor::GetInstance()->GetID(group_identifier); | 111 GoogleExperimentID id) { |
112 GroupMapAccessor::GetInstance()->AssociateID( | |
113 MakeNameGroupId(trial_name, group_name), id); | |
114 } | |
115 | |
116 GoogleExperimentID GetGoogleExperimentID(const std::string& trial_name, | |
117 const std::string& group_name) { | |
118 return GroupMapAccessor::GetInstance()->GetID( | |
119 MakeNameGroupId(trial_name, group_name)); | |
81 } | 120 } |
82 | 121 |
83 void SetChildProcessLoggingExperimentList() { | 122 void SetChildProcessLoggingExperimentList() { |
84 std::vector<base::FieldTrial::NameGroupId> name_group_ids; | 123 std::vector<NameGroupId> name_group_ids; |
85 base::FieldTrialList::GetFieldTrialNameGroupIds(&name_group_ids); | 124 GetFieldTrialNameGroupIds(&name_group_ids); |
86 std::vector<string16> experiment_strings(name_group_ids.size()); | 125 std::vector<string16> experiment_strings(name_group_ids.size()); |
87 for (size_t i = 0; i < name_group_ids.size(); ++i) { | 126 for (size_t i = 0; i < name_group_ids.size(); ++i) { |
88 // Wish there was a StringPrintf for string16... :-( | 127 // Wish there was a StringPrintf for string16... :-( |
89 experiment_strings[i] = WideToUTF16(base::StringPrintf( | 128 experiment_strings[i] = WideToUTF16(base::StringPrintf( |
90 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group)); | 129 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group)); |
91 } | 130 } |
92 child_process_logging::SetExperimentList(experiment_strings); | 131 child_process_logging::SetExperimentList(experiment_strings); |
93 } | 132 } |
94 | 133 |
95 } // namespace experiments_helper | 134 } // namespace experiments_helper |
135 | |
136 // Functions below are exposed for testing explicitly behind this namespace. | |
137 // They simply wrap existing functions in this file. | |
138 namespace testing { | |
139 | |
140 void TestGetFieldTrialNameGroupIdsForSelectedGroups( | |
141 const base::FieldTrial::SelectedGroups& selected_groups, | |
142 std::vector<experiments_helper::NameGroupId>* name_group_ids) { | |
143 ::GetFieldTrialNameGroupIdsForSelectedGroups(selected_groups, name_group_ids); | |
144 } | |
145 | |
146 uint32 TestHashName(const std::string& name) { | |
147 return ::HashName(name); | |
148 } | |
149 | |
150 } // namespace testing | |
OLD | NEW |