blob: fbb2eec8518730a8174f8c55cc06e2628821baac [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]d2b6af672010-07-01 20:38:5422#include "base/perftimer.h"
[email protected]5ccaa412009-11-13 22:00:1623#include "base/time.h"
[email protected]c1834a92011-01-21 18:21:0324#include "chrome/browser/browser_process.h"
[email protected]c1834a92011-01-21 18:21:0325#include "chrome/browser/metrics/metrics_service.h"
[email protected]c38831a12011-10-28 12:44:4926#include "content/public/browser/browser_thread.h"
[email protected]7f6f44c2011-12-14 13:23:3827#include "content/public/browser/user_metrics.h"
[email protected]5ccaa412009-11-13 22:00:1628
[email protected]631bb742011-11-02 11:29:3929using content::BrowserThread;
[email protected]7f6f44c2011-12-14 13:23:3830using content::UserMetricsAction;
[email protected]631bb742011-11-02 11:29:3931
[email protected]5ccaa412009-11-13 22:00:1632namespace chromeos {
33
[email protected]df32e89c2012-05-17 17:47:3434// The interval between external metrics collections in seconds
35static const int kExternalMetricsCollectionIntervalSeconds = 30;
[email protected]5ccaa412009-11-13 22:00:1636
[email protected]196ff472011-01-21 19:25:2737ExternalMetrics::ExternalMetrics()
38 : test_recorder_(NULL) {
[email protected]5ccaa412009-11-13 22:00:1639}
40
[email protected]41baad02011-05-15 20:37:4641ExternalMetrics::~ExternalMetrics() {}
42
[email protected]29cf16772010-04-21 15:13:4743void ExternalMetrics::Start() {
[email protected]196ff472011-01-21 19:25:2744 // Register user actions external to the browser.
45 // chrome/tools/extract_actions.py won't understand these lines, so all of
46 // these are explicitly added in that script.
47 // TODO(derat): We shouldn't need to verify actions before reporting them;
48 // remove all of this once http://crosbug.com/11125 is fixed.
49 valid_user_actions_.insert("Accel_NextWindow_Tab");
50 valid_user_actions_.insert("Accel_PrevWindow_Tab");
51 valid_user_actions_.insert("Accel_NextWindow_F5");
52 valid_user_actions_.insert("Accel_PrevWindow_F5");
53 valid_user_actions_.insert("Accel_BrightnessDown_F6");
54 valid_user_actions_.insert("Accel_BrightnessUp_F7");
[email protected]54e9f752011-09-19 22:38:1455 valid_user_actions_.insert("Cryptohome.PKCS11InitFail");
[email protected]1078339c2011-09-16 18:30:0156 valid_user_actions_.insert("Updater.ServerCertificateChanged");
57 valid_user_actions_.insert("Updater.ServerCertificateFailed");
[email protected]196ff472011-01-21 19:25:2758
[email protected]5ccaa412009-11-13 22:00:1659 ScheduleCollector();
60}
61
[email protected]29cf16772010-04-21 15:13:4762void ExternalMetrics::RecordActionUI(std::string action_string) {
[email protected]196ff472011-01-21 19:25:2763 if (valid_user_actions_.count(action_string)) {
[email protected]7f6f44c2011-12-14 13:23:3864 content::RecordComputedAction(action_string);
[email protected]5ccaa412009-11-13 22:00:1665 } else {
[email protected]196ff472011-01-21 19:25:2766 LOG(ERROR) << "undefined UMA action: " << action_string;
[email protected]5ccaa412009-11-13 22:00:1667 }
68}
69
[email protected]29cf16772010-04-21 15:13:4770void ExternalMetrics::RecordAction(const char* action) {
71 std::string action_string(action);
[email protected]cca169b52010-10-08 22:15:5572 BrowserThread::PostTask(
73 BrowserThread::UI, FROM_HERE,
[email protected]d8e1cf9c2011-11-03 19:37:5774 base::Bind(&ExternalMetrics::RecordActionUI, this, action_string));
[email protected]29cf16772010-04-21 15:13:4775}
76
[email protected]c1834a92011-01-21 18:21:0377void ExternalMetrics::RecordCrashUI(const std::string& crash_kind) {
78 if (g_browser_process && g_browser_process->metrics_service()) {
79 g_browser_process->metrics_service()->LogChromeOSCrash(crash_kind);
80 }
81}
82
83void ExternalMetrics::RecordCrash(const std::string& crash_kind) {
84 BrowserThread::PostTask(
85 BrowserThread::UI, FROM_HERE,
[email protected]d8e1cf9c2011-11-03 19:37:5786 base::Bind(&ExternalMetrics::RecordCrashUI, this, crash_kind));
[email protected]c1834a92011-01-21 18:21:0387}
88
[email protected]29cf16772010-04-21 15:13:4789void ExternalMetrics::RecordHistogram(const char* histogram_data) {
90 int sample, min, max, nbuckets;
91 char name[128]; // length must be consistent with sscanf format below.
92 int n = sscanf(histogram_data, "%127s %d %d %d %d",
93 name, &sample, &min, &max, &nbuckets);
94 if (n != 5) {
95 LOG(ERROR) << "bad histogram request: " << histogram_data;
96 return;
97 }
[email protected]e37f2c02010-04-21 20:36:4798 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
99 // instance and thus only work if |name| is constant.
[email protected]81ce9f3b2011-04-05 04:48:53100 base::Histogram* counter = base::Histogram::FactoryGet(
[email protected]835d7c82010-10-14 04:38:38101 name, min, max, nbuckets, base::Histogram::kUmaTargetedHistogramFlag);
[email protected]e37f2c02010-04-21 20:36:47102 counter->Add(sample);
[email protected]29cf16772010-04-21 15:13:47103}
104
105void ExternalMetrics::RecordLinearHistogram(const char* histogram_data) {
106 int sample, max;
107 char name[128]; // length must be consistent with sscanf format below.
108 int n = sscanf(histogram_data, "%127s %d %d", name, &sample, &max);
109 if (n != 3) {
110 LOG(ERROR) << "bad linear histogram request: " << histogram_data;
111 return;
112 }
[email protected]e37f2c02010-04-21 20:36:47113 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
114 // instance and thus only work if |name| is constant.
[email protected]81ce9f3b2011-04-05 04:48:53115 base::Histogram* counter = base::LinearHistogram::FactoryGet(
[email protected]835d7c82010-10-14 04:38:38116 name, 1, max, max + 1, base::Histogram::kUmaTargetedHistogramFlag);
[email protected]e37f2c02010-04-21 20:36:47117 counter->Add(sample);
[email protected]29cf16772010-04-21 15:13:47118}
119
[email protected]5ccaa412009-11-13 22:00:16120void ExternalMetrics::CollectEvents() {
[email protected]29cf16772010-04-21 15:13:47121 const char* event_file_path = "/var/log/metrics/uma-events";
[email protected]5ccaa412009-11-13 22:00:16122 struct stat stat_buf;
123 int result;
[email protected]29cf16772010-04-21 15:13:47124 if (!test_path_.empty()) {
125 event_file_path = test_path_.value().c_str();
126 }
[email protected]5ccaa412009-11-13 22:00:16127 result = stat(event_file_path, &stat_buf);
128 if (result < 0) {
129 if (errno != ENOENT) {
[email protected]29cf16772010-04-21 15:13:47130 PLOG(ERROR) << event_file_path << ": bad metrics file stat";
[email protected]5ccaa412009-11-13 22:00:16131 }
132 // Nothing to collect---try later.
133 return;
134 }
135 if (stat_buf.st_size == 0) {
136 // Also nothing to collect.
137 return;
138 }
139 int fd = open(event_file_path, O_RDWR);
140 if (fd < 0) {
141 PLOG(ERROR) << event_file_path << ": cannot open";
142 return;
143 }
144 result = flock(fd, LOCK_EX);
145 if (result < 0) {
146 PLOG(ERROR) << event_file_path << ": cannot lock";
[email protected]29cf16772010-04-21 15:13:47147 close(fd);
[email protected]5ccaa412009-11-13 22:00:16148 return;
149 }
150 // This processes all messages in the log. Each message starts with a 4-byte
151 // field containing the length of the entire message. The length is followed
152 // by a name-value pair of null-terminated strings. When all messages are
153 // read and processed, or an error occurs, truncate the file to zero size.
154 for (;;) {
155 int32 message_size;
156 result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
157 if (result < 0) {
158 PLOG(ERROR) << "reading metrics message header";
159 break;
160 }
161 if (result == 0) { // normal EOF
162 break;
163 }
164 if (result < static_cast<int>(sizeof(message_size))) {
165 LOG(ERROR) << "bad read size " << result <<
166 ", expecting " << sizeof(message_size);
167 break;
168 }
169 // kMetricsMessageMaxLength applies to the entire message: the 4-byte
170 // length field and the two null-terminated strings.
171 if (message_size < 2 + static_cast<int>(sizeof(message_size)) ||
172 message_size > static_cast<int>(kMetricsMessageMaxLength)) {
173 LOG(ERROR) << "bad message size " << message_size;
174 break;
175 }
176 message_size -= sizeof(message_size); // already read this much
177 uint8 buffer[kMetricsMessageMaxLength];
178 result = HANDLE_EINTR(read(fd, buffer, message_size));
179 if (result < 0) {
180 PLOG(ERROR) << "reading metrics message body";
181 break;
182 }
183 if (result < message_size) {
184 LOG(ERROR) << "message too short: length " << result <<
185 ", expected " << message_size;
186 break;
187 }
188 // The buffer should now contain a pair of null-terminated strings.
189 uint8* p = reinterpret_cast<uint8*>(memchr(buffer, '\0', message_size));
190 uint8* q = NULL;
191 if (p != NULL) {
192 q = reinterpret_cast<uint8*>(
193 memchr(p + 1, '\0', message_size - (p + 1 - buffer)));
194 }
195 if (q == NULL) {
196 LOG(ERROR) << "bad name-value pair for metrics";
197 break;
198 } else {
199 char* name = reinterpret_cast<char*>(buffer);
200 char* value = reinterpret_cast<char*>(p + 1);
[email protected]29cf16772010-04-21 15:13:47201 if (test_recorder_ != NULL) {
202 test_recorder_(name, value);
[email protected]c1834a92011-01-21 18:21:03203 } else if (strcmp(name, "crash") == 0) {
204 RecordCrash(value);
[email protected]29cf16772010-04-21 15:13:47205 } else if (strcmp(name, "histogram") == 0) {
206 RecordHistogram(value);
207 } else if (strcmp(name, "linearhistogram") == 0) {
208 RecordLinearHistogram(value);
209 } else if (strcmp(name, "useraction") == 0) {
210 RecordAction(value);
211 } else {
212 LOG(ERROR) << "invalid event type: " << name;
213 }
[email protected]5ccaa412009-11-13 22:00:16214 }
215 }
216
217 result = ftruncate(fd, 0);
218 if (result < 0) {
219 PLOG(ERROR) << "truncate metrics log";
220 }
221 result = flock(fd, LOCK_UN);
222 if (result < 0) {
223 PLOG(ERROR) << "unlock metrics log";
224 }
225 result = close(fd);
226 if (result < 0) {
227 PLOG(ERROR) << "close metrics log";
228 }
229}
230
231void ExternalMetrics::CollectEventsAndReschedule() {
[email protected]d2b6af672010-07-01 20:38:54232 PerfTimer timer;
[email protected]5ccaa412009-11-13 22:00:16233 CollectEvents();
[email protected]d2b6af672010-07-01 20:38:54234 UMA_HISTOGRAM_TIMES("UMA.CollectExternalEventsTime", timer.Elapsed());
[email protected]5ccaa412009-11-13 22:00:16235 ScheduleCollector();
236}
237
238void ExternalMetrics::ScheduleCollector() {
239 bool result;
[email protected]cca169b52010-10-08 22:15:55240 result = BrowserThread::PostDelayedTask(
[email protected]d8e1cf9c2011-11-03 19:37:57241 BrowserThread::FILE, FROM_HERE,
242 base::Bind(&chromeos::ExternalMetrics::CollectEventsAndReschedule, this),
[email protected]df32e89c2012-05-17 17:47:34243 base::TimeDelta::FromSeconds(kExternalMetricsCollectionIntervalSeconds));
[email protected]5ccaa412009-11-13 22:00:16244 DCHECK(result);
245}
246
247} // namespace chromeos