Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(782)

Side by Side Diff: chrome/common/metrics/experiments_helper.cc

Issue 10151017: Remove the hash fields from FieldTrials. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: header order Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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::SelectedGroupId& 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::SelectedGroupId& 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::SelectedGroupId,
60 experiments_helper::GoogleExperimentID, NameGroupIdCompare> GroupToIDMap; 49 experiments_helper::GoogleExperimentID,
50 experiments_helper::SelectedGroupIdCompare> 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::SelectedGroupId MakeSelectedGroupId(
74 const std::string& trial_name,
75 const std::string& group_name) {
76 experiments_helper::SelectedGroupId 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 GetFieldTrialSelectedGroupIdsForSelectedGroups(
84 const base::FieldTrial::SelectedGroups& selected_groups,
85 std::vector<experiments_helper::SelectedGroupId>* 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(MakeSelectedGroupId(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 GetFieldTrialSelectedGroupIds(
73 const base::FieldTrial::NameGroupId& group_identifier, 100 std::vector<SelectedGroupId>* name_group_ids) {
74 GoogleExperimentID id) { 101 DCHECK(name_group_ids->empty());
75 GroupMapAccessor::GetInstance()->AssociateID(group_identifier, id); 102 // A note on thread safety: Since GetFieldTrialSelectedGroups is thread
103 // safe, and we operate on a separate list of that data, this function is
104 // technically thread safe as well, with respect to the FieldTriaList data.
105 base::FieldTrial::SelectedGroups selected_groups;
106 base::FieldTrialList::GetFieldTrialSelectedGroups(&selected_groups);
107 GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups,
108 name_group_ids);
76 } 109 }
77 110
78 GoogleExperimentID GetGoogleExperimentID( 111 void AssociateGoogleExperimentID(const std::string& trial_name,
79 const base::FieldTrial::NameGroupId& group_identifier) { 112 const std::string& group_name,
80 return GroupMapAccessor::GetInstance()->GetID(group_identifier); 113 GoogleExperimentID id) {
114 GroupMapAccessor::GetInstance()->AssociateID(
115 MakeSelectedGroupId(trial_name, group_name), id);
116 }
117
118 GoogleExperimentID GetGoogleExperimentID(const std::string& trial_name,
119 const std::string& group_name) {
120 return GroupMapAccessor::GetInstance()->GetID(
121 MakeSelectedGroupId(trial_name, group_name));
81 } 122 }
82 123
83 void SetChildProcessLoggingExperimentList() { 124 void SetChildProcessLoggingExperimentList() {
84 std::vector<base::FieldTrial::NameGroupId> name_group_ids; 125 std::vector<SelectedGroupId> name_group_ids;
85 base::FieldTrialList::GetFieldTrialNameGroupIds(&name_group_ids); 126 GetFieldTrialSelectedGroupIds(&name_group_ids);
86 std::vector<string16> experiment_strings(name_group_ids.size()); 127 std::vector<string16> experiment_strings(name_group_ids.size());
87 for (size_t i = 0; i < name_group_ids.size(); ++i) { 128 for (size_t i = 0; i < name_group_ids.size(); ++i) {
88 // Wish there was a StringPrintf for string16... :-( 129 // Wish there was a StringPrintf for string16... :-(
89 experiment_strings[i] = WideToUTF16(base::StringPrintf( 130 experiment_strings[i] = WideToUTF16(base::StringPrintf(
90 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group)); 131 L"%x-%x", name_group_ids[i].name, name_group_ids[i].group));
91 } 132 }
92 child_process_logging::SetExperimentList(experiment_strings); 133 child_process_logging::SetExperimentList(experiment_strings);
93 } 134 }
94 135
95 } // namespace experiments_helper 136 } // namespace experiments_helper
137
138 // Functions below are exposed for testing explicitly behind this namespace.
139 // They simply wrap existing functions in this file.
140 namespace testing {
141
142 void TestGetFieldTrialSelectedGroupIdsForSelectedGroups(
143 const base::FieldTrial::SelectedGroups& selected_groups,
144 std::vector<experiments_helper::SelectedGroupId>* name_group_ids) {
145 ::GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups,
146 name_group_ids);
147 }
148
149 uint32 TestHashName(const std::string& name) {
150 return ::HashName(name);
151 }
152
153 } // namespace testing
OLDNEW
« no previous file with comments | « chrome/common/metrics/experiments_helper.h ('k') | chrome/common/metrics/experiments_helper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698