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 3 // found in the LICENSE file. 4 5 #include "chrome/common/metrics/metrics_log_base.h" 6 7 #include "base/base64.h" 8 #include "base/basictypes.h" 9 #include "base/md5.h" 10 #include "base/metrics/histogram_base.h" 11 #include "base/metrics/histogram_samples.h" 12 #include "base/sys_byteorder.h" 13 #include "base/sys_info.h" 14 #include "chrome/common/chrome_version_info.h" 15 #include "chrome/common/logging_chrome.h" 16 #include "chrome/common/metrics/proto/histogram_event.pb.h" 17 #include "chrome/common/metrics/proto/system_profile.pb.h" 18 #include "chrome/common/metrics/proto/user_action_event.pb.h" 19 20 using base::Histogram; 21 using base::HistogramBase; 22 using base::HistogramSamples; 23 using base::SampleCountIterator; 24 using base::Time; 25 using base::TimeDelta; 26 using metrics::HistogramEventProto; 27 using metrics::SystemProfileProto; 28 using metrics::UserActionEventProto; 29 30 namespace { 31 32 // Any id less than 16 bytes is considered to be a testing id. 33 bool IsTestingID(const std::string& id) { 34 return id.size() < 16; 35 } 36 37 // Converts the 8-byte prefix of an MD5 hash into a uint64 value. 38 inline uint64 HashToUInt64(const std::string& hash) { 39 uint64 value; 40 DCHECK_GE(hash.size(), sizeof(value)); 41 memcpy(&value, hash.data(), sizeof(value)); 42 return base::HostToNet64(value); 43 } 44 45 SystemProfileProto::Channel AsProtobufChannel( 46 chrome::VersionInfo::Channel channel) { 47 switch (channel) { 48 case chrome::VersionInfo::CHANNEL_UNKNOWN: 49 return SystemProfileProto::CHANNEL_UNKNOWN; 50 case chrome::VersionInfo::CHANNEL_CANARY: 51 return SystemProfileProto::CHANNEL_CANARY; 52 case chrome::VersionInfo::CHANNEL_DEV: 53 return SystemProfileProto::CHANNEL_DEV; 54 case chrome::VersionInfo::CHANNEL_BETA: 55 return SystemProfileProto::CHANNEL_BETA; 56 case chrome::VersionInfo::CHANNEL_STABLE: 57 return SystemProfileProto::CHANNEL_STABLE; 58 default: 59 NOTREACHED(); 60 return SystemProfileProto::CHANNEL_UNKNOWN; 61 } 62 } 63 64 } // namespace 65 66 MetricsLogBase::MetricsLogBase(const std::string& client_id, int session_id, 67 const std::string& version_string) 68 : num_events_(0), 69 locked_(false) { 70 if (IsTestingID(client_id)) 71 uma_proto_.set_client_id(0); 72 else 73 uma_proto_.set_client_id(Hash(client_id)); 74 75 uma_proto_.set_session_id(session_id); 76 uma_proto_.mutable_system_profile()->set_build_timestamp(GetBuildTime()); 77 uma_proto_.mutable_system_profile()->set_app_version(version_string); 78 uma_proto_.mutable_system_profile()->set_channel( 79 AsProtobufChannel(chrome::VersionInfo::GetChannel())); 80 } 81 82 MetricsLogBase::~MetricsLogBase() {} 83 84 // static 85 uint64 MetricsLogBase::Hash(const std::string& value) { 86 // Create an MD5 hash of the given |value|, represented as a byte buffer 87 // encoded as an std::string. 88 base::MD5Context context; 89 base::MD5Init(&context); 90 base::MD5Update(&context, value); 91 92 base::MD5Digest digest; 93 base::MD5Final(&digest, &context); 94 95 std::string hash_str(reinterpret_cast<char*>(digest.a), arraysize(digest.a)); 96 uint64 hash = HashToUInt64(hash_str); 97 98 // The following log is VERY helpful when folks add some named histogram into 99 // the code, but forgot to update the descriptive list of histograms. When 100 // that happens, all we get to see (server side) is a hash of the histogram 101 // name. We can then use this logging to find out what histogram name was 102 // being hashed to a given MD5 value by just running the version of Chromium 103 // in question with --enable-logging. 104 DVLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]"; 105 106 return hash; 107 } 108 109 // static 110 int64 MetricsLogBase::GetBuildTime() { 111 static int64 integral_build_time = 0; 112 if (!integral_build_time) { 113 Time time; 114 const char* kDateTime = __DATE__ " " __TIME__ " GMT"; 115 bool result = Time::FromString(kDateTime, &time); 116 DCHECK(result); 117 integral_build_time = static_cast<int64>(time.ToTimeT()); 118 } 119 return integral_build_time; 120 } 121 122 // static 123 int64 MetricsLogBase::GetCurrentTime() { 124 return (base::TimeTicks::Now() - base::TimeTicks()).InSeconds(); 125 } 126 127 void MetricsLogBase::CloseLog() { 128 DCHECK(!locked_); 129 locked_ = true; 130 } 131 132 void MetricsLogBase::GetEncodedLog(std::string* encoded_log) { 133 DCHECK(locked_); 134 uma_proto_.SerializeToString(encoded_log); 135 } 136 137 void MetricsLogBase::RecordUserAction(const char* key) { 138 DCHECK(!locked_); 139 140 UserActionEventProto* user_action = uma_proto_.add_user_action_event(); 141 user_action->set_name_hash(Hash(key)); 142 user_action->set_time(GetCurrentTime()); 143 144 ++num_events_; 145 } 146 147 void MetricsLogBase::RecordHistogramDelta(const std::string& histogram_name, 148 const HistogramSamples& snapshot) { 149 DCHECK(!locked_); 150 DCHECK_NE(0, snapshot.TotalCount()); 151 152 // We will ignore the MAX_INT/infinite value in the last element of range[]. 153 154 HistogramEventProto* histogram_proto = uma_proto_.add_histogram_event(); 155 histogram_proto->set_name_hash(Hash(histogram_name)); 156 histogram_proto->set_sum(snapshot.sum()); 157 158 for (scoped_ptr<SampleCountIterator> it = snapshot.Iterator(); 159 !it->Done(); 160 it->Next()) { 161 HistogramBase::Sample min; 162 HistogramBase::Sample max; 163 HistogramBase::Count count; 164 it->Get(&min, &max, &count); 165 HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket(); 166 bucket->set_min(min); 167 bucket->set_max(max); 168 bucket->set_count(count); 169 170 size_t index; 171 if (it->GetBucketIndex(&index)) 172 bucket->set_bucket_index(index); 173 } 174 } 175