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

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: Created 8 years, 8 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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698