| Index: chrome/common/metrics/experiments_helper.cc
|
| diff --git a/chrome/common/metrics/experiments_helper.cc b/chrome/common/metrics/experiments_helper.cc
|
| index 11c7c1389fca180304be1513a6498446e06d441e..44621bbdb2ec3e8c8bcdb0fc9e7455bfecd622be 100644
|
| --- a/chrome/common/metrics/experiments_helper.cc
|
| +++ b/chrome/common/metrics/experiments_helper.cc
|
| @@ -7,26 +7,15 @@
|
| #include <vector>
|
|
|
| #include "base/memory/singleton.h"
|
| +#include "base/sha1.h"
|
| #include "base/string16.h"
|
| #include "base/stringprintf.h"
|
| +#include "base/sys_byteorder.h"
|
| #include "base/utf_string_conversions.h"
|
| #include "chrome/common/child_process_logging.h"
|
|
|
| namespace {
|
|
|
| -// We need to pass a Compare class to the std::map template since NameGroupId
|
| -// is a user-defined type.
|
| -struct NameGroupIdCompare {
|
| - bool operator() (const base::FieldTrial::NameGroupId& lhs,
|
| - const base::FieldTrial::NameGroupId& rhs) const {
|
| - // The group and name fields are just SHA-1 Hashes, so we just need to treat
|
| - // them as IDs and do a less-than comparison. We test group first, since
|
| - // name is more likely to collide.
|
| - return lhs.group == rhs.group ? lhs.name < rhs.name :
|
| - lhs.group < rhs.group;
|
| - }
|
| -};
|
| -
|
| // The internal singleton accessor for the map, used to keep it thread-safe.
|
| class GroupMapAccessor {
|
| public:
|
| @@ -38,7 +27,7 @@ class GroupMapAccessor {
|
| GroupMapAccessor() {}
|
| ~GroupMapAccessor() {}
|
|
|
| - void AssociateID(const base::FieldTrial::NameGroupId& group_identifier,
|
| + void AssociateID(const experiments_helper::SelectedGroupId& group_identifier,
|
| experiments_helper::GoogleExperimentID id) {
|
| base::AutoLock scoped_lock(lock_);
|
| DCHECK(group_to_id_map_.find(group_identifier) == group_to_id_map_.end()) <<
|
| @@ -47,7 +36,7 @@ class GroupMapAccessor {
|
| }
|
|
|
| experiments_helper::GoogleExperimentID GetID(
|
| - const base::FieldTrial::NameGroupId& group_identifier) {
|
| + const experiments_helper::SelectedGroupId& group_identifier) {
|
| base::AutoLock scoped_lock(lock_);
|
| GroupToIDMap::const_iterator it = group_to_id_map_.find(group_identifier);
|
| if (it == group_to_id_map_.end())
|
| @@ -56,33 +45,85 @@ class GroupMapAccessor {
|
| }
|
|
|
| private:
|
| - typedef std::map<base::FieldTrial::NameGroupId,
|
| - experiments_helper::GoogleExperimentID, NameGroupIdCompare> GroupToIDMap;
|
| + typedef std::map<experiments_helper::SelectedGroupId,
|
| + experiments_helper::GoogleExperimentID,
|
| + experiments_helper::SelectedGroupIdCompare> GroupToIDMap;
|
|
|
| base::Lock lock_;
|
| GroupToIDMap group_to_id_map_;
|
| };
|
|
|
| +// Creates unique identifier for the trial by hashing a name string, whether
|
| +// it's for the field trial or the group name.
|
| +uint32 HashName(const std::string& name) {
|
| + // SHA-1 is designed to produce a uniformly random spread in its output space,
|
| + // even for nearly-identical inputs.
|
| + unsigned char sha1_hash[base::kSHA1Length];
|
| + base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()),
|
| + name.size(),
|
| + sha1_hash);
|
| +
|
| + COMPILE_ASSERT(sizeof(uint32) < sizeof(sha1_hash), need_more_data);
|
| + uint32 bits;
|
| + memcpy(&bits, sha1_hash, sizeof(bits));
|
| +
|
| + return base::ByteSwapToLE32(bits);
|
| +}
|
| +
|
| +experiments_helper::SelectedGroupId MakeSelectedGroupId(
|
| + const std::string& trial_name,
|
| + const std::string& group_name) {
|
| + experiments_helper::SelectedGroupId id;
|
| + id.name = HashName(trial_name);
|
| + id.group = HashName(group_name);
|
| + return id;
|
| +}
|
| +
|
| +// Populates |name_group_ids| based on |selected_groups|.
|
| +void GetFieldTrialSelectedGroupIdsForSelectedGroups(
|
| + const base::FieldTrial::SelectedGroups& selected_groups,
|
| + std::vector<experiments_helper::SelectedGroupId>* name_group_ids) {
|
| + DCHECK(name_group_ids->empty());
|
| + for (base::FieldTrial::SelectedGroups::const_iterator it =
|
| + selected_groups.begin(); it != selected_groups.end(); ++it) {
|
| + name_group_ids->push_back(MakeSelectedGroupId(it->trial, it->group));
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace experiments_helper {
|
|
|
| const GoogleExperimentID kEmptyGoogleExperimentID = 0;
|
|
|
| -void AssociateGoogleExperimentID(
|
| - const base::FieldTrial::NameGroupId& group_identifier,
|
| - GoogleExperimentID id) {
|
| - GroupMapAccessor::GetInstance()->AssociateID(group_identifier, id);
|
| +void GetFieldTrialSelectedGroupIds(
|
| + std::vector<SelectedGroupId>* name_group_ids) {
|
| + DCHECK(name_group_ids->empty());
|
| + // A note on thread safety: Since GetFieldTrialSelectedGroups is thread
|
| + // safe, and we operate on a separate list of that data, this function is
|
| + // technically thread safe as well, with respect to the FieldTriaList data.
|
| + base::FieldTrial::SelectedGroups selected_groups;
|
| + base::FieldTrialList::GetFieldTrialSelectedGroups(&selected_groups);
|
| + GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups,
|
| + name_group_ids);
|
| +}
|
| +
|
| +void AssociateGoogleExperimentID(const std::string& trial_name,
|
| + const std::string& group_name,
|
| + GoogleExperimentID id) {
|
| + GroupMapAccessor::GetInstance()->AssociateID(
|
| + MakeSelectedGroupId(trial_name, group_name), id);
|
| }
|
|
|
| -GoogleExperimentID GetGoogleExperimentID(
|
| - const base::FieldTrial::NameGroupId& group_identifier) {
|
| - return GroupMapAccessor::GetInstance()->GetID(group_identifier);
|
| +GoogleExperimentID GetGoogleExperimentID(const std::string& trial_name,
|
| + const std::string& group_name) {
|
| + return GroupMapAccessor::GetInstance()->GetID(
|
| + MakeSelectedGroupId(trial_name, group_name));
|
| }
|
|
|
| void SetChildProcessLoggingExperimentList() {
|
| - std::vector<base::FieldTrial::NameGroupId> name_group_ids;
|
| - base::FieldTrialList::GetFieldTrialNameGroupIds(&name_group_ids);
|
| + std::vector<SelectedGroupId> name_group_ids;
|
| + GetFieldTrialSelectedGroupIds(&name_group_ids);
|
| std::vector<string16> experiment_strings(name_group_ids.size());
|
| for (size_t i = 0; i < name_group_ids.size(); ++i) {
|
| // Wish there was a StringPrintf for string16... :-(
|
| @@ -93,3 +134,20 @@ void SetChildProcessLoggingExperimentList() {
|
| }
|
|
|
| } // namespace experiments_helper
|
| +
|
| +// Functions below are exposed for testing explicitly behind this namespace.
|
| +// They simply wrap existing functions in this file.
|
| +namespace testing {
|
| +
|
| +void TestGetFieldTrialSelectedGroupIdsForSelectedGroups(
|
| + const base::FieldTrial::SelectedGroups& selected_groups,
|
| + std::vector<experiments_helper::SelectedGroupId>* name_group_ids) {
|
| + ::GetFieldTrialSelectedGroupIdsForSelectedGroups(selected_groups,
|
| + name_group_ids);
|
| +}
|
| +
|
| +uint32 TestHashName(const std::string& name) {
|
| + return ::HashName(name);
|
| +}
|
| +
|
| +} // namespace testing
|
|
|