blob: 138cfcca1cdef0c86ce5cceeb0a37cf9bbe052ce [file] [log] [blame]
[email protected]df32e89c2012-05-17 17:47:341// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]896220042010-03-23 18:14:282// Use of this source code is governed by a BSD-style license that can be
[email protected]5ccaa412009-11-13 22:00:163// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/external_metrics.h"
6
7#include <fcntl.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
[email protected]5ccaa412009-11-13 22:00:1611#include <sys/file.h>
12#include <sys/stat.h>
13#include <sys/types.h>
[email protected]c38831a12011-10-28 12:44:4914#include <unistd.h>
[email protected]5ccaa412009-11-13 22:00:1615
[email protected]d8e1cf9c2011-11-03 19:37:5716#include <string>
17
[email protected]5ccaa412009-11-13 22:00:1618#include "base/basictypes.h"
[email protected]7f6f44c2011-12-14 13:23:3819#include "base/bind.h"
[email protected]5ccaa412009-11-13 22:00:1620#include "base/eintr_wrapper.h"
[email protected]835d7c82010-10-14 04:38:3821#include "base/metrics/histogram.h"
[email protected]cbf14442012-09-05 03:00:2522#include "base/metrics/statistics_recorder.h"
[email protected]d2b6af672010-07-01 20:38:5423#include "base/perftimer.h"
[email protected]5ccaa412009-11-13 22:00:1624#include "base/time.h"
[email protected]c1834a92011-01-21 18:21:0325#include "chrome/browser/browser_process.h"
[email protected]c1834a92011-01-21 18:21:0326#include "chrome/browser/metrics/metrics_service.h"
[email protected]c38831a12011-10-28 12:44:4927#include "content/public/browser/browser_thread.h"
[email protected]7f6f44c2011-12-14 13:23:3828#include "content/public/browser/user_metrics.h"
[email protected]5ccaa412009-11-13 22:00:1629
[email protected]631bb742011-11-02 11:29:3930using content::BrowserThread;
[email protected]7f6f44c2011-12-14 13:23:3831using content::UserMetricsAction;
[email protected]631bb742011-11-02 11:29:3932
[email protected]5ccaa412009-11-13 22:00:1633namespace chromeos {
34
[email protected]df32e89c2012-05-17 17:47:3435// The interval between external metrics collections in seconds
36static const int kExternalMetricsCollectionIntervalSeconds = 30;
[email protected]5ccaa412009-11-13 22:00:1637
[email protected]e24a8532012-08-03 07:18:4438class SystemHistogram : public base::Histogram {
39 public:
40 static bool CheckValues(const std::string& name,
41 int minimum,
42 int maximum,
43 size_t bucket_count) {
44 return base::Histogram::InspectConstructionArguments(
45 name, &minimum, &maximum, &bucket_count);
46 }
[email protected]cbf14442012-09-05 03:00:2547 static bool CheckLinearValues(const std::string& name, int maximum) {
48 if (!CheckValues(name, 1, maximum, maximum + 1))
49 return false;
50 Histogram* histogram = base::StatisticsRecorder::FindHistogram(name);
51 if (!histogram)
52 return true;
53 return histogram->HasConstructionArguments(1, maximum, maximum + 1);
54 }
[email protected]e24a8532012-08-03 07:18:4455};
56
[email protected]196ff472011-01-21 19:25:2757ExternalMetrics::ExternalMetrics()
58 : test_recorder_(NULL) {
[email protected]5ccaa412009-11-13 22:00:1659}
60
[email protected]41baad02011-05-15 20:37:4661ExternalMetrics::~ExternalMetrics() {}
62
[email protected]29cf16772010-04-21 15:13:4763void ExternalMetrics::Start() {
[email protected]196ff472011-01-21 19:25:2764 // Register user actions external to the browser.
65 // chrome/tools/extract_actions.py won't understand these lines, so all of
66 // these are explicitly added in that script.
67 // TODO(derat): We shouldn't need to verify actions before reporting them;
68 // remove all of this once http://crosbug.com/11125 is fixed.
[email protected]54e9f752011-09-19 22:38:1469 valid_user_actions_.insert("Cryptohome.PKCS11InitFail");
[email protected]1078339c2011-09-16 18:30:0170 valid_user_actions_.insert("Updater.ServerCertificateChanged");
71 valid_user_actions_.insert("Updater.ServerCertificateFailed");
[email protected]196ff472011-01-21 19:25:2772
[email protected]5ccaa412009-11-13 22:00:1673 ScheduleCollector();
74}
75
[email protected]29cf16772010-04-21 15:13:4776void ExternalMetrics::RecordActionUI(std::string action_string) {
[email protected]196ff472011-01-21 19:25:2777 if (valid_user_actions_.count(action_string)) {
[email protected]7f6f44c2011-12-14 13:23:3878 content::RecordComputedAction(action_string);
[email protected]5ccaa412009-11-13 22:00:1679 } else {
[email protected]196ff472011-01-21 19:25:2780 LOG(ERROR) << "undefined UMA action: " << action_string;
[email protected]5ccaa412009-11-13 22:00:1681 }
82}
83
[email protected]29cf16772010-04-21 15:13:4784void ExternalMetrics::RecordAction(const char* action) {
85 std::string action_string(action);
[email protected]cca169b52010-10-08 22:15:5586 BrowserThread::PostTask(
87 BrowserThread::UI, FROM_HERE,
[email protected]d8e1cf9c2011-11-03 19:37:5788 base::Bind(&ExternalMetrics::RecordActionUI, this, action_string));
[email protected]29cf16772010-04-21 15:13:4789}
90
[email protected]c1834a92011-01-21 18:21:0391void ExternalMetrics::RecordCrashUI(const std::string& crash_kind) {
92 if (g_browser_process && g_browser_process->metrics_service()) {
93 g_browser_process->metrics_service()->LogChromeOSCrash(crash_kind);
94 }
95}
96
97void ExternalMetrics::RecordCrash(const std::string& crash_kind) {
98 BrowserThread::PostTask(
99 BrowserThread::UI, FROM_HERE,
[email protected]d8e1cf9c2011-11-03 19:37:57100 base::Bind(&ExternalMetrics::RecordCrashUI, this, crash_kind));
[email protected]c1834a92011-01-21 18:21:03101}
102
[email protected]29cf16772010-04-21 15:13:47103void ExternalMetrics::RecordHistogram(const char* histogram_data) {
104 int sample, min, max, nbuckets;
105 char name[128]; // length must be consistent with sscanf format below.
106 int n = sscanf(histogram_data, "%127s %d %d %d %d",
107 name, &sample, &min, &max, &nbuckets);
108 if (n != 5) {
109 LOG(ERROR) << "bad histogram request: " << histogram_data;
110 return;
111 }
[email protected]e24a8532012-08-03 07:18:44112
113 if (!SystemHistogram::CheckValues(name, min, max, nbuckets)) {
114 LOG(ERROR) << "Invalid histogram " << name
115 << ", min=" << min
116 << ", max=" << max
117 << ", nbuckets=" << nbuckets;
118 return;
119 }
[email protected]e37f2c02010-04-21 20:36:47120 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
121 // instance and thus only work if |name| is constant.
[email protected]81ce9f3b2011-04-05 04:48:53122 base::Histogram* counter = base::Histogram::FactoryGet(
[email protected]835d7c82010-10-14 04:38:38123 name, min, max, nbuckets, base::Histogram::kUmaTargetedHistogramFlag);
[email protected]e37f2c02010-04-21 20:36:47124 counter->Add(sample);
[email protected]29cf16772010-04-21 15:13:47125}
126
127void ExternalMetrics::RecordLinearHistogram(const char* histogram_data) {
128 int sample, max;
129 char name[128]; // length must be consistent with sscanf format below.
130 int n = sscanf(histogram_data, "%127s %d %d", name, &sample, &max);
131 if (n != 3) {
132 LOG(ERROR) << "bad linear histogram request: " << histogram_data;
133 return;
134 }
[email protected]d3cc70162012-08-24 00:50:05135
[email protected]cbf14442012-09-05 03:00:25136 if (!SystemHistogram::CheckLinearValues(name, max)) {
[email protected]d3cc70162012-08-24 00:50:05137 LOG(ERROR) << "Invalid linear histogram " << name
138 << ", max=" << max;
139 return;
140 }
[email protected]e37f2c02010-04-21 20:36:47141 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
142 // instance and thus only work if |name| is constant.
[email protected]81ce9f3b2011-04-05 04:48:53143 base::Histogram* counter = base::LinearHistogram::FactoryGet(
[email protected]835d7c82010-10-14 04:38:38144 name, 1, max, max + 1, base::Histogram::kUmaTargetedHistogramFlag);
[email protected]e37f2c02010-04-21 20:36:47145 counter->Add(sample);
[email protected]29cf16772010-04-21 15:13:47146}
147
[email protected]5ccaa412009-11-13 22:00:16148void ExternalMetrics::CollectEvents() {
[email protected]29cf16772010-04-21 15:13:47149 const char* event_file_path = "/var/log/metrics/uma-events";
[email protected]5ccaa412009-11-13 22:00:16150 struct stat stat_buf;
151 int result;
[email protected]29cf16772010-04-21 15:13:47152 if (!test_path_.empty()) {
153 event_file_path = test_path_.value().c_str();
154 }
[email protected]5ccaa412009-11-13 22:00:16155 result = stat(event_file_path, &stat_buf);
156 if (result < 0) {
157 if (errno != ENOENT) {
[email protected]29cf16772010-04-21 15:13:47158 PLOG(ERROR) << event_file_path << ": bad metrics file stat";
[email protected]5ccaa412009-11-13 22:00:16159 }
160 // Nothing to collect---try later.
161 return;
162 }
163 if (stat_buf.st_size == 0) {
164 // Also nothing to collect.
165 return;
166 }
167 int fd = open(event_file_path, O_RDWR);
168 if (fd < 0) {
169 PLOG(ERROR) << event_file_path << ": cannot open";
170 return;
171 }
172 result = flock(fd, LOCK_EX);
173 if (result < 0) {
174 PLOG(ERROR) << event_file_path << ": cannot lock";
[email protected]29cf16772010-04-21 15:13:47175 close(fd);
[email protected]5ccaa412009-11-13 22:00:16176 return;
177 }
178 // This processes all messages in the log. Each message starts with a 4-byte
179 // field containing the length of the entire message. The length is followed
180 // by a name-value pair of null-terminated strings. When all messages are
181 // read and processed, or an error occurs, truncate the file to zero size.
182 for (;;) {
183 int32 message_size;
184 result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
185 if (result < 0) {
186 PLOG(ERROR) << "reading metrics message header";
187 break;
188 }
189 if (result == 0) { // normal EOF
190 break;
191 }
192 if (result < static_cast<int>(sizeof(message_size))) {
193 LOG(ERROR) << "bad read size " << result <<
194 ", expecting " << sizeof(message_size);
195 break;
196 }
197 // kMetricsMessageMaxLength applies to the entire message: the 4-byte
198 // length field and the two null-terminated strings.
199 if (message_size < 2 + static_cast<int>(sizeof(message_size)) ||
200 message_size > static_cast<int>(kMetricsMessageMaxLength)) {
201 LOG(ERROR) << "bad message size " << message_size;
202 break;
203 }
204 message_size -= sizeof(message_size); // already read this much
205 uint8 buffer[kMetricsMessageMaxLength];
206 result = HANDLE_EINTR(read(fd, buffer, message_size));
207 if (result < 0) {
208 PLOG(ERROR) << "reading metrics message body";
209 break;
210 }
211 if (result < message_size) {
212 LOG(ERROR) << "message too short: length " << result <<
213 ", expected " << message_size;
214 break;
215 }
216 // The buffer should now contain a pair of null-terminated strings.
217 uint8* p = reinterpret_cast<uint8*>(memchr(buffer, '\0', message_size));
218 uint8* q = NULL;
219 if (p != NULL) {
220 q = reinterpret_cast<uint8*>(
221 memchr(p + 1, '\0', message_size - (p + 1 - buffer)));
222 }
223 if (q == NULL) {
224 LOG(ERROR) << "bad name-value pair for metrics";
225 break;
226 } else {
227 char* name = reinterpret_cast<char*>(buffer);
228 char* value = reinterpret_cast<char*>(p + 1);
[email protected]29cf16772010-04-21 15:13:47229 if (test_recorder_ != NULL) {
230 test_recorder_(name, value);
[email protected]c1834a92011-01-21 18:21:03231 } else if (strcmp(name, "crash") == 0) {
232 RecordCrash(value);
[email protected]29cf16772010-04-21 15:13:47233 } else if (strcmp(name, "histogram") == 0) {
234 RecordHistogram(value);
235 } else if (strcmp(name, "linearhistogram") == 0) {
236 RecordLinearHistogram(value);
237 } else if (strcmp(name, "useraction") == 0) {
238 RecordAction(value);
239 } else {
240 LOG(ERROR) << "invalid event type: " << name;
241 }
[email protected]5ccaa412009-11-13 22:00:16242 }
243 }
244
245 result = ftruncate(fd, 0);
246 if (result < 0) {
247 PLOG(ERROR) << "truncate metrics log";
248 }
249 result = flock(fd, LOCK_UN);
250 if (result < 0) {
251 PLOG(ERROR) << "unlock metrics log";
252 }
253 result = close(fd);
254 if (result < 0) {
255 PLOG(ERROR) << "close metrics log";
256 }
257}
258
259void ExternalMetrics::CollectEventsAndReschedule() {
[email protected]d2b6af672010-07-01 20:38:54260 PerfTimer timer;
[email protected]5ccaa412009-11-13 22:00:16261 CollectEvents();
[email protected]d2b6af672010-07-01 20:38:54262 UMA_HISTOGRAM_TIMES("UMA.CollectExternalEventsTime", timer.Elapsed());
[email protected]5ccaa412009-11-13 22:00:16263 ScheduleCollector();
264}
265
266void ExternalMetrics::ScheduleCollector() {
267 bool result;
[email protected]cca169b52010-10-08 22:15:55268 result = BrowserThread::PostDelayedTask(
[email protected]d8e1cf9c2011-11-03 19:37:57269 BrowserThread::FILE, FROM_HERE,
270 base::Bind(&chromeos::ExternalMetrics::CollectEventsAndReschedule, this),
[email protected]df32e89c2012-05-17 17:47:34271 base::TimeDelta::FromSeconds(kExternalMetricsCollectionIntervalSeconds));
[email protected]5ccaa412009-11-13 22:00:16272 DCHECK(result);
273}
274
275} // namespace chromeos