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 |
19 const uint32 kReservedHashValue = 0; | |
20 | |
17 // We need to pass a Compare class to the std::map template since NameGroupId | 21 // We need to pass a Compare class to the std::map template since NameGroupId |
18 // is a user-defined type. | 22 // is a user-defined type. |
19 struct NameGroupIdCompare { | 23 struct NameGroupIdCompare { |
20 bool operator() (const base::FieldTrial::NameGroupId& lhs, | 24 bool operator() (const experiments_helper::NameGroupId& lhs, |
21 const base::FieldTrial::NameGroupId& rhs) const { | 25 const experiments_helper::NameGroupId& rhs) const { |
22 // The group and name fields are just SHA-1 Hashes, so we just need to treat | 26 // 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 | 27 // them as IDs and do a less-than comparison. We test group first, since |
24 // name is more likely to collide. | 28 // name is more likely to collide. |
25 return lhs.group == rhs.group ? lhs.name < rhs.name : | 29 return lhs.group == rhs.group ? lhs.name < rhs.name : |
26 lhs.group < rhs.group; | 30 lhs.group < rhs.group; |
27 } | 31 } |
28 }; | 32 }; |
29 | 33 |
30 // The internal singleton accessor for the map, used to keep it thread-safe. | 34 // The internal singleton accessor for the map, used to keep it thread-safe. |
31 class GroupMapAccessor { | 35 class GroupMapAccessor { |
32 public: | 36 public: |
33 // Retrieve the singleton. | 37 // Retrieve the singleton. |
34 static GroupMapAccessor* GetInstance() { | 38 static GroupMapAccessor* GetInstance() { |
35 return Singleton<GroupMapAccessor>::get(); | 39 return Singleton<GroupMapAccessor>::get(); |
36 } | 40 } |
37 | 41 |
38 GroupMapAccessor() {} | 42 GroupMapAccessor() {} |
39 ~GroupMapAccessor() {} | 43 ~GroupMapAccessor() {} |
40 | 44 |
41 void AssociateID(const base::FieldTrial::NameGroupId& group_identifier, | 45 void AssociateID(const experiments_helper::NameGroupId& group_identifier, |
42 experiments_helper::GoogleExperimentID id) { | 46 experiments_helper::GoogleExperimentID id) { |
43 base::AutoLock scoped_lock(lock_); | 47 base::AutoLock scoped_lock(lock_); |
44 DCHECK(group_to_id_map_.find(group_identifier) == group_to_id_map_.end()) << | 48 DCHECK(group_to_id_map_.find(group_identifier) == group_to_id_map_.end()) << |
45 "You can associate a group with a GoogleExperimentID only once."; | 49 "You can associate a group with a GoogleExperimentID only once."; |
46 group_to_id_map_[group_identifier] = id; | 50 group_to_id_map_[group_identifier] = id; |
47 } | 51 } |
48 | 52 |
49 experiments_helper::GoogleExperimentID GetID( | 53 experiments_helper::GoogleExperimentID GetID( |
50 const base::FieldTrial::NameGroupId& group_identifier) { | 54 const experiments_helper::NameGroupId& group_identifier) { |
51 base::AutoLock scoped_lock(lock_); | 55 base::AutoLock scoped_lock(lock_); |
52 GroupToIDMap::const_iterator it = group_to_id_map_.find(group_identifier); | 56 GroupToIDMap::const_iterator it = group_to_id_map_.find(group_identifier); |
53 if (it == group_to_id_map_.end()) | 57 if (it == group_to_id_map_.end()) |
54 return experiments_helper::kEmptyGoogleExperimentID; | 58 return experiments_helper::kEmptyGoogleExperimentID; |
55 return it->second; | 59 return it->second; |
56 } | 60 } |
57 | 61 |
58 private: | 62 private: |
59 typedef std::map<base::FieldTrial::NameGroupId, | 63 typedef std::map<experiments_helper::NameGroupId, |
60 experiments_helper::GoogleExperimentID, NameGroupIdCompare> GroupToIDMap; | 64 experiments_helper::GoogleExperimentID, NameGroupIdCompare> GroupToIDMap; |
61 | 65 |
62 base::Lock lock_; | 66 base::Lock lock_; |
63 GroupToIDMap group_to_id_map_; | 67 GroupToIDMap group_to_id_map_; |
64 }; | 68 }; |
65 | 69 |
70 // Creates unique identifier for the trial by hashing a name string, whether | |
71 // it's for the field trial or the group name. | |
72 uint32 HashName(const std::string& name) { | |
73 // SHA-1 is designed to produce a uniformly random spread in its output space, | |
74 // even for nearly-identical inputs. | |
75 unsigned char sha1_hash[base::kSHA1Length]; | |
76 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()), | |
77 name.size(), | |
78 sha1_hash); | |
79 | |
80 COMPILE_ASSERT(sizeof(uint32) < sizeof(sha1_hash), need_more_data); | |
81 uint32 bits; | |
82 memcpy(&bits, sha1_hash, sizeof(bits)); | |
83 | |
84 // We only DCHECK, since this should not happen because the registration | |
85 // of the experiment on the server should have already warn the developer. | |
86 // If this ever happen, we'll ignore this experiment/group when reporting. | |
87 DCHECK(bits != kReservedHashValue); | |
MAD
2012/04/25 20:03:22
Actually, I don't think we need this anymore. This
SteveT
2012/04/26 19:03:31
Ah okay, I was wondering about that. I've removed
| |
88 return base::ByteSwapToLE32(bits); | |
89 } | |
90 | |
66 } // namespace | 91 } // namespace |
67 | 92 |
68 namespace experiments_helper { | 93 namespace experiments_helper { |
69 | 94 |
70 const GoogleExperimentID kEmptyGoogleExperimentID = 0; | 95 const GoogleExperimentID kEmptyGoogleExperimentID = 0; |
71 | 96 |
97 void GetFieldTrialNameGroupIds(std::vector<NameGroupId>* name_group_ids) { | |
98 DCHECK(name_group_ids->empty()); | |
99 // A note on thread safety: Since GetFieldTrialSelectedGroups is thread | |
100 // safe, and we operate on a separate list of that data, this function is | |
101 // technically thread safe as well, with respect to the FieldTriaList data. | |
102 SelectedGroups selected_groups; | |
103 base::FieldTrialList::GetFieldTrialSelectedGroups(&selected_groups); | |
104 GetFieldTrialNameGroupIdsForSelectedGroups(selected_groups, name_group_ids); | |
105 } | |
106 | |
107 void GetFieldTrialNameGroupIdsForSelectedGroups( | |
MAD
2012/04/25 20:03:22
Maybe add a comment about the separation from prev
SteveT
2012/04/26 19:03:31
Cool. I have a comment around the testing namespac
| |
108 const SelectedGroups& selected_groups, | |
109 std::vector<NameGroupId>* name_group_ids) { | |
110 DCHECK(name_group_ids->empty()); | |
111 for (SelectedGroups::const_iterator it = selected_groups.begin(); | |
112 it != selected_groups.end(); ++it) | |
MAD
2012/04/25 20:03:22
We use {} when the condition/loop has more than on
SteveT
2012/04/26 19:03:31
I do recall pkasting telling me that they are only
| |
113 name_group_ids->push_back(MakeNameGroupId(it->trial, it->group)); | |
114 } | |
115 | |
116 NameGroupId MakeNameGroupId(const std::string& trial_name, | |
117 const std::string& group_name) { | |
118 NameGroupId id; | |
119 id.name = HashName(trial_name); | |
120 id.group = HashName(group_name); | |
121 return id; | |
122 } | |
123 | |
124 uint32 TestingHashName(const std::string& name) { | |
125 return HashName(name); | |
126 } | |
127 | |
72 void AssociateGoogleExperimentID( | 128 void AssociateGoogleExperimentID( |
73 const base::FieldTrial::NameGroupId& group_identifier, | 129 const NameGroupId& group_identifier, |
74 GoogleExperimentID id) { | 130 GoogleExperimentID id) { |
75 GroupMapAccessor::GetInstance()->AssociateID(group_identifier, id); | 131 GroupMapAccessor::GetInstance()->AssociateID(group_identifier, id); |
76 } | 132 } |
77 | 133 |
78 GoogleExperimentID GetGoogleExperimentID( | 134 GoogleExperimentID GetGoogleExperimentID( |
79 const base::FieldTrial::NameGroupId& group_identifier) { | 135 const NameGroupId& group_identifier) { |
80 return GroupMapAccessor::GetInstance()->GetID(group_identifier); | 136 return GroupMapAccessor::GetInstance()->GetID(group_identifier); |
81 } | 137 } |
82 | 138 |
83 void SetChildProcessLoggingExperimentList() { | 139 void SetChildProcessLoggingExperimentList() { |
84 std::vector<base::FieldTrial::NameGroupId> name_group_ids; | 140 std::vector<NameGroupId> name_group_ids; |
85 base::FieldTrialList::GetFieldTrialNameGroupIds(&name_group_ids); | 141 GetFieldTrialNameGroupIds(&name_group_ids); |
86 std::vector<string16> experiment_strings(name_group_ids.size()); | 142 std::vector<string16> experiment_strings(name_group_ids.size()); |
87 for (size_t i = 0; i < name_group_ids.size(); ++i) { | 143 for (size_t i = 0; i < name_group_ids.size(); ++i) { |
88 // Wish there was a StringPrintf for string16... :-( | 144 // Wish there was a StringPrintf for string16... :-( |
89 experiment_strings[i] = WideToUTF16(base::StringPrintf( | 145 experiment_strings[i] = WideToUTF16(base::StringPrintf( |
90 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group)); | 146 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group)); |
91 } | 147 } |
92 child_process_logging::SetExperimentList(experiment_strings); | 148 child_process_logging::SetExperimentList(experiment_strings); |
93 } | 149 } |
94 | 150 |
95 } // namespace experiments_helper | 151 } // namespace experiments_helper |
OLD | NEW |