blob: 34adc13f44a35d5163708924e4a14e92a466ee27 [file] [log] [blame]
[email protected]81ce9f3b2011-04-05 04:48:531// Copyright (c) 2011 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
16#include "base/basictypes.h"
17#include "base/eintr_wrapper.h"
[email protected]835d7c82010-10-14 04:38:3818#include "base/metrics/histogram.h"
[email protected]d2b6af672010-07-01 20:38:5419#include "base/perftimer.h"
[email protected]5ccaa412009-11-13 22:00:1620#include "base/time.h"
[email protected]c1834a92011-01-21 18:21:0321#include "chrome/browser/browser_process.h"
[email protected]c1834a92011-01-21 18:21:0322#include "chrome/browser/metrics/metrics_service.h"
[email protected]afd1e522011-04-27 23:29:5923#include "content/browser/user_metrics.h"
[email protected]c38831a12011-10-28 12:44:4924#include "content/public/browser/browser_thread.h"
[email protected]5ccaa412009-11-13 22:00:1625
26namespace chromeos {
27
28// The interval between external metrics collections, in milliseconds.
29static const int kExternalMetricsCollectionIntervalMs = 30 * 1000;
30
[email protected]196ff472011-01-21 19:25:2731ExternalMetrics::ExternalMetrics()
32 : test_recorder_(NULL) {
[email protected]5ccaa412009-11-13 22:00:1633}
34
[email protected]41baad02011-05-15 20:37:4635ExternalMetrics::~ExternalMetrics() {}
36
[email protected]29cf16772010-04-21 15:13:4737void ExternalMetrics::Start() {
[email protected]196ff472011-01-21 19:25:2738 // Register user actions external to the browser.
39 // chrome/tools/extract_actions.py won't understand these lines, so all of
40 // these are explicitly added in that script.
41 // TODO(derat): We shouldn't need to verify actions before reporting them;
42 // remove all of this once http://crosbug.com/11125 is fixed.
43 valid_user_actions_.insert("Accel_NextWindow_Tab");
44 valid_user_actions_.insert("Accel_PrevWindow_Tab");
45 valid_user_actions_.insert("Accel_NextWindow_F5");
46 valid_user_actions_.insert("Accel_PrevWindow_F5");
47 valid_user_actions_.insert("Accel_BrightnessDown_F6");
48 valid_user_actions_.insert("Accel_BrightnessUp_F7");
[email protected]54e9f752011-09-19 22:38:1449 valid_user_actions_.insert("Cryptohome.PKCS11InitFail");
[email protected]1078339c2011-09-16 18:30:0150 valid_user_actions_.insert("Updater.ServerCertificateChanged");
51 valid_user_actions_.insert("Updater.ServerCertificateFailed");
[email protected]196ff472011-01-21 19:25:2752
[email protected]5ccaa412009-11-13 22:00:1653 ScheduleCollector();
54}
55
[email protected]29cf16772010-04-21 15:13:4756void ExternalMetrics::RecordActionUI(std::string action_string) {
[email protected]196ff472011-01-21 19:25:2757 if (valid_user_actions_.count(action_string)) {
58 UserMetrics::RecordComputedAction(action_string);
[email protected]5ccaa412009-11-13 22:00:1659 } else {
[email protected]196ff472011-01-21 19:25:2760 LOG(ERROR) << "undefined UMA action: " << action_string;
[email protected]5ccaa412009-11-13 22:00:1661 }
62}
63
[email protected]29cf16772010-04-21 15:13:4764void ExternalMetrics::RecordAction(const char* action) {
65 std::string action_string(action);
[email protected]cca169b52010-10-08 22:15:5566 BrowserThread::PostTask(
67 BrowserThread::UI, FROM_HERE,
[email protected]196ff472011-01-21 19:25:2768 NewRunnableMethod(this, &ExternalMetrics::RecordActionUI, action_string));
[email protected]29cf16772010-04-21 15:13:4769}
70
[email protected]c1834a92011-01-21 18:21:0371void ExternalMetrics::RecordCrashUI(const std::string& crash_kind) {
72 if (g_browser_process && g_browser_process->metrics_service()) {
73 g_browser_process->metrics_service()->LogChromeOSCrash(crash_kind);
74 }
75}
76
77void ExternalMetrics::RecordCrash(const std::string& crash_kind) {
78 BrowserThread::PostTask(
79 BrowserThread::UI, FROM_HERE,
80 NewRunnableMethod(this, &ExternalMetrics::RecordCrashUI, crash_kind));
81}
82
[email protected]29cf16772010-04-21 15:13:4783void ExternalMetrics::RecordHistogram(const char* histogram_data) {
84 int sample, min, max, nbuckets;
85 char name[128]; // length must be consistent with sscanf format below.
86 int n = sscanf(histogram_data, "%127s %d %d %d %d",
87 name, &sample, &min, &max, &nbuckets);
88 if (n != 5) {
89 LOG(ERROR) << "bad histogram request: " << histogram_data;
90 return;
91 }
[email protected]e37f2c02010-04-21 20:36:4792 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
93 // instance and thus only work if |name| is constant.
[email protected]81ce9f3b2011-04-05 04:48:5394 base::Histogram* counter = base::Histogram::FactoryGet(
[email protected]835d7c82010-10-14 04:38:3895 name, min, max, nbuckets, base::Histogram::kUmaTargetedHistogramFlag);
[email protected]e37f2c02010-04-21 20:36:4796 counter->Add(sample);
[email protected]29cf16772010-04-21 15:13:4797}
98
99void ExternalMetrics::RecordLinearHistogram(const char* histogram_data) {
100 int sample, max;
101 char name[128]; // length must be consistent with sscanf format below.
102 int n = sscanf(histogram_data, "%127s %d %d", name, &sample, &max);
103 if (n != 3) {
104 LOG(ERROR) << "bad linear histogram request: " << histogram_data;
105 return;
106 }
[email protected]e37f2c02010-04-21 20:36:47107 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
108 // instance and thus only work if |name| is constant.
[email protected]81ce9f3b2011-04-05 04:48:53109 base::Histogram* counter = base::LinearHistogram::FactoryGet(
[email protected]835d7c82010-10-14 04:38:38110 name, 1, max, max + 1, base::Histogram::kUmaTargetedHistogramFlag);
[email protected]e37f2c02010-04-21 20:36:47111 counter->Add(sample);
[email protected]29cf16772010-04-21 15:13:47112}
113
[email protected]5ccaa412009-11-13 22:00:16114void ExternalMetrics::CollectEvents() {
[email protected]29cf16772010-04-21 15:13:47115 const char* event_file_path = "/var/log/metrics/uma-events";
[email protected]5ccaa412009-11-13 22:00:16116 struct stat stat_buf;
117 int result;
[email protected]29cf16772010-04-21 15:13:47118 if (!test_path_.empty()) {
119 event_file_path = test_path_.value().c_str();
120 }
[email protected]5ccaa412009-11-13 22:00:16121 result = stat(event_file_path, &stat_buf);
122 if (result < 0) {
123 if (errno != ENOENT) {
[email protected]29cf16772010-04-21 15:13:47124 PLOG(ERROR) << event_file_path << ": bad metrics file stat";
[email protected]5ccaa412009-11-13 22:00:16125 }
126 // Nothing to collect---try later.
127 return;
128 }
129 if (stat_buf.st_size == 0) {
130 // Also nothing to collect.
131 return;
132 }
133 int fd = open(event_file_path, O_RDWR);
134 if (fd < 0) {
135 PLOG(ERROR) << event_file_path << ": cannot open";
136 return;
137 }
138 result = flock(fd, LOCK_EX);
139 if (result < 0) {
140 PLOG(ERROR) << event_file_path << ": cannot lock";
[email protected]29cf16772010-04-21 15:13:47141 close(fd);
[email protected]5ccaa412009-11-13 22:00:16142 return;
143 }
144 // This processes all messages in the log. Each message starts with a 4-byte
145 // field containing the length of the entire message. The length is followed
146 // by a name-value pair of null-terminated strings. When all messages are
147 // read and processed, or an error occurs, truncate the file to zero size.
148 for (;;) {
149 int32 message_size;
150 result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
151 if (result < 0) {
152 PLOG(ERROR) << "reading metrics message header";
153 break;
154 }
155 if (result == 0) { // normal EOF
156 break;
157 }
158 if (result < static_cast<int>(sizeof(message_size))) {
159 LOG(ERROR) << "bad read size " << result <<
160 ", expecting " << sizeof(message_size);
161 break;
162 }
163 // kMetricsMessageMaxLength applies to the entire message: the 4-byte
164 // length field and the two null-terminated strings.
165 if (message_size < 2 + static_cast<int>(sizeof(message_size)) ||
166 message_size > static_cast<int>(kMetricsMessageMaxLength)) {
167 LOG(ERROR) << "bad message size " << message_size;
168 break;
169 }
170 message_size -= sizeof(message_size); // already read this much
171 uint8 buffer[kMetricsMessageMaxLength];
172 result = HANDLE_EINTR(read(fd, buffer, message_size));
173 if (result < 0) {
174 PLOG(ERROR) << "reading metrics message body";
175 break;
176 }
177 if (result < message_size) {
178 LOG(ERROR) << "message too short: length " << result <<
179 ", expected " << message_size;
180 break;
181 }
182 // The buffer should now contain a pair of null-terminated strings.
183 uint8* p = reinterpret_cast<uint8*>(memchr(buffer, '\0', message_size));
184 uint8* q = NULL;
185 if (p != NULL) {
186 q = reinterpret_cast<uint8*>(
187 memchr(p + 1, '\0', message_size - (p + 1 - buffer)));
188 }
189 if (q == NULL) {
190 LOG(ERROR) << "bad name-value pair for metrics";
191 break;
192 } else {
193 char* name = reinterpret_cast<char*>(buffer);
194 char* value = reinterpret_cast<char*>(p + 1);
[email protected]29cf16772010-04-21 15:13:47195 if (test_recorder_ != NULL) {
196 test_recorder_(name, value);
[email protected]c1834a92011-01-21 18:21:03197 } else if (strcmp(name, "crash") == 0) {
198 RecordCrash(value);
[email protected]29cf16772010-04-21 15:13:47199 } else if (strcmp(name, "histogram") == 0) {
200 RecordHistogram(value);
201 } else if (strcmp(name, "linearhistogram") == 0) {
202 RecordLinearHistogram(value);
203 } else if (strcmp(name, "useraction") == 0) {
204 RecordAction(value);
205 } else {
206 LOG(ERROR) << "invalid event type: " << name;
207 }
[email protected]5ccaa412009-11-13 22:00:16208 }
209 }
210
211 result = ftruncate(fd, 0);
212 if (result < 0) {
213 PLOG(ERROR) << "truncate metrics log";
214 }
215 result = flock(fd, LOCK_UN);
216 if (result < 0) {
217 PLOG(ERROR) << "unlock metrics log";
218 }
219 result = close(fd);
220 if (result < 0) {
221 PLOG(ERROR) << "close metrics log";
222 }
223}
224
225void ExternalMetrics::CollectEventsAndReschedule() {
[email protected]d2b6af672010-07-01 20:38:54226 PerfTimer timer;
[email protected]5ccaa412009-11-13 22:00:16227 CollectEvents();
[email protected]d2b6af672010-07-01 20:38:54228 UMA_HISTOGRAM_TIMES("UMA.CollectExternalEventsTime", timer.Elapsed());
[email protected]5ccaa412009-11-13 22:00:16229 ScheduleCollector();
230}
231
232void ExternalMetrics::ScheduleCollector() {
233 bool result;
[email protected]cca169b52010-10-08 22:15:55234 result = BrowserThread::PostDelayedTask(
235 BrowserThread::FILE, FROM_HERE, NewRunnableMethod(
[email protected]29cf16772010-04-21 15:13:47236 this, &chromeos::ExternalMetrics::CollectEventsAndReschedule),
[email protected]5ccaa412009-11-13 22:00:16237 kExternalMetricsCollectionIntervalMs);
238 DCHECK(result);
239}
240
241} // namespace chromeos