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

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: Changed GXID API to accept strings 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::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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698