blob: 3e3987d75e144598e4bf3d8bb83b8db7c92a99fa [file] [log] [blame]
Darin Petkov65b01462010-04-14 13:32:20 -07001// Copyright (c) 2010 The Chromium OS 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
Darin Petkov65b01462010-04-14 13:32:20 -07005#include "metrics_library.h"
6
7#include <errno.h>
8#include <sys/file.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -07009#include <sys/stat.h>
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070010
11#include <cstdarg>
12#include <cstdio>
13#include <cstring>
Darin Petkov65b01462010-04-14 13:32:20 -070014
15#define READ_WRITE_ALL_FILE_FLAGS \
16 (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
17
Ken Mixter4c5daa42010-08-26 18:35:06 -070018static const char kAutotestPath[] = "/var/log/metrics/autotest-events";
19static const char kUMAEventsPath[] = "/var/log/metrics/uma-events";
20static const char kConsentFile[] = "/home/chronos/Consent To Send Stats";
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070021static const int32_t kBufferSize = 1024;
Darin Petkov65b01462010-04-14 13:32:20 -070022
Ken Mixter4c5daa42010-08-26 18:35:06 -070023time_t MetricsLibrary::cached_enabled_time_ = 0;
24bool MetricsLibrary::cached_enabled_ = false;
25
David James3b3add52010-06-04 15:01:19 -070026using std::string;
Darin Petkov65b01462010-04-14 13:32:20 -070027
28// TODO(sosa@chromium.org) - use Chromium logger instead of stderr
Darin Petkov11b8eb32010-05-18 11:00:59 -070029static void PrintError(const char* message, const char* file,
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070030 int code) {
David James3b3add52010-06-04 15:01:19 -070031 static const char kProgramName[] = "libmetrics";
Darin Petkov65b01462010-04-14 13:32:20 -070032 if (code == 0) {
33 fprintf(stderr, "%s: %s\n", kProgramName, message);
34 } else if (file == NULL) {
35 fprintf(stderr, "%s: ", kProgramName);
36 perror(message);
37 } else {
38 fprintf(stderr, "%s: %s: ", kProgramName, file);
39 perror(message);
40 }
41}
42
Darin Petkov11b8eb32010-05-18 11:00:59 -070043MetricsLibrary::MetricsLibrary()
Ken Mixter4c5daa42010-08-26 18:35:06 -070044 : uma_events_file_(NULL),
45 consent_file_(kConsentFile) {}
46
47bool MetricsLibrary::AreMetricsEnabled() {
48 static struct stat stat_buffer;
49 time_t this_check_time = time(NULL);
50
51 if (this_check_time != cached_enabled_time_) {
52 cached_enabled_time_ = this_check_time;
53 cached_enabled_ = (stat(consent_file_, &stat_buffer) >= 0);
54 }
55 return cached_enabled_;
56}
Darin Petkov11b8eb32010-05-18 11:00:59 -070057
58bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) {
Ken Mixter4c5daa42010-08-26 18:35:06 -070059 if (!AreMetricsEnabled())
60 return true;
61
Darin Petkov11b8eb32010-05-18 11:00:59 -070062 int chrome_fd = open(uma_events_file_,
Darin Petkov65b01462010-04-14 13:32:20 -070063 O_WRONLY | O_APPEND | O_CREAT,
64 READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070065 // If we failed to open it, return.
Darin Petkov65b01462010-04-14 13:32:20 -070066 if (chrome_fd < 0) {
Darin Petkov11b8eb32010-05-18 11:00:59 -070067 PrintError("open", uma_events_file_, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070068 return false;
Darin Petkov65b01462010-04-14 13:32:20 -070069 }
70
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070071 // Need to chmod because open flags are anded with umask. Ignore the
72 // exit code -- a chronos process may fail chmoding because the file
73 // has been created by a root process but that should be OK.
74 fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov65b01462010-04-14 13:32:20 -070075
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070076 // Grab an exclusive lock to protect Chrome from truncating
77 // underneath us. Keep the file locked as briefly as possible.
Darin Petkov65b01462010-04-14 13:32:20 -070078 if (flock(chrome_fd, LOCK_EX) < 0) {
Darin Petkov11b8eb32010-05-18 11:00:59 -070079 PrintError("flock", uma_events_file_, errno);
Darin Petkov65b01462010-04-14 13:32:20 -070080 close(chrome_fd);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070081 return false;
Darin Petkov65b01462010-04-14 13:32:20 -070082 }
83
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070084 bool success = true;
85 if (write(chrome_fd, message, length) != length) {
Darin Petkov11b8eb32010-05-18 11:00:59 -070086 PrintError("write", uma_events_file_, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070087 success = false;
88 }
Darin Petkov65b01462010-04-14 13:32:20 -070089
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070090 // Release the file lock and close file.
91 if (flock(chrome_fd, LOCK_UN) < 0) {
Darin Petkov11b8eb32010-05-18 11:00:59 -070092 PrintError("unlock", uma_events_file_, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070093 success = false;
94 }
Darin Petkov65b01462010-04-14 13:32:20 -070095 close(chrome_fd);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070096 return success;
97}
98
Darin Petkov11b8eb32010-05-18 11:00:59 -070099int32_t MetricsLibrary::FormatChromeMessage(int32_t buffer_size, char* buffer,
100 const char* format, ...) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700101 int32_t message_length;
102 size_t len_size = sizeof(message_length);
103
104 // Format the non-LENGTH contents in the buffer by leaving space for
105 // LENGTH at the start of the buffer.
106 va_list args;
107 va_start(args, format);
108 message_length = vsnprintf(&buffer[len_size], buffer_size - len_size,
109 format, args);
110 va_end(args);
111
112 if (message_length < 0) {
113 PrintError("chrome message format error", NULL, 0);
114 return -1;
115 }
116
117 // +1 to account for the trailing \0.
118 message_length += len_size + 1;
119 if (message_length > buffer_size) {
120 PrintError("chrome message too long", NULL, 0);
121 return -1;
122 }
123
124 // Prepend LENGTH to the message.
125 memcpy(buffer, &message_length, len_size);
126 return message_length;
127}
128
Darin Petkovfc91b422010-05-12 13:05:45 -0700129void MetricsLibrary::Init() {
Darin Petkov11b8eb32010-05-18 11:00:59 -0700130 uma_events_file_ = kUMAEventsPath;
Darin Petkovfc91b422010-05-12 13:05:45 -0700131}
132
Darin Petkovc2526a12010-04-21 14:24:04 -0700133bool MetricsLibrary::SendToAutotest(const string& name, int value) {
David James3b3add52010-06-04 15:01:19 -0700134 FILE* autotest_file = fopen(kAutotestPath, "a+");
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700135 if (autotest_file == NULL) {
136 PrintError("fopen", kAutotestPath, errno);
137 return false;
138 }
139
140 fprintf(autotest_file, "%s=%d\n", name.c_str(), value);
141 fclose(autotest_file);
142 return true;
143}
144
Darin Petkov21cd2c52010-05-12 15:26:16 -0700145bool MetricsLibrary::SendToUMA(const string& name, int sample,
146 int min, int max, int nbuckets) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700147 // Format the message.
148 char message[kBufferSize];
149 int32_t message_length =
150 FormatChromeMessage(kBufferSize, message,
Darin Petkovc2526a12010-04-21 14:24:04 -0700151 "histogram%c%s %d %d %d %d", '\0',
152 name.c_str(), sample, min, max, nbuckets);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700153
154 if (message_length < 0)
155 return false;
156
157 // Send the message.
158 return SendMessageToChrome(message_length, message);
Darin Petkov65b01462010-04-14 13:32:20 -0700159}
Darin Petkov5b7dce12010-04-21 15:45:10 -0700160
Darin Petkov21cd2c52010-05-12 15:26:16 -0700161bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
162 int max) {
Darin Petkov5b7dce12010-04-21 15:45:10 -0700163 // Format the message.
164 char message[kBufferSize];
165 int32_t message_length =
166 FormatChromeMessage(kBufferSize, message,
167 "linearhistogram%c%s %d %d", '\0',
168 name.c_str(), sample, max);
169
170 if (message_length < 0)
171 return false;
172
173 // Send the message.
174 return SendMessageToChrome(message_length, message);
175}