blob: 98415a839c2d07bed4d6d0e24c1f9e5467e9fb4d [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>
Darin Petkov4fcb2ac2010-04-15 16:40:23 -07009
10#include <cstdarg>
11#include <cstdio>
12#include <cstring>
Darin Petkov65b01462010-04-14 13:32:20 -070013
14#define READ_WRITE_ALL_FILE_FLAGS \
15 (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
16
Darin Petkovc2526a12010-04-21 14:24:04 -070017static const char kAutotestPath[] =
18 "/var/log/metrics/autotest-events";
19static const char kChromePath[] =
20 "/var/log/metrics/uma-events";
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070021static const int32_t kBufferSize = 1024;
Darin Petkov65b01462010-04-14 13:32:20 -070022
23using namespace std;
24
25// TODO(sosa@chromium.org) - use Chromium logger instead of stderr
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070026static void PrintError(const char *message, const char *file,
27 int code) {
Darin Petkov65b01462010-04-14 13:32:20 -070028 const char *kProgramName = "metrics_library";
29 if (code == 0) {
30 fprintf(stderr, "%s: %s\n", kProgramName, message);
31 } else if (file == NULL) {
32 fprintf(stderr, "%s: ", kProgramName);
33 perror(message);
34 } else {
35 fprintf(stderr, "%s: %s: ", kProgramName, file);
36 perror(message);
37 }
38}
39
Darin Petkovc2526a12010-04-21 14:24:04 -070040// Sends message of size |length| to Chrome and returns true on success.
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070041static bool SendMessageToChrome(int32_t length, const char *message) {
Darin Petkov65b01462010-04-14 13:32:20 -070042 int chrome_fd = open(kChromePath,
43 O_WRONLY | O_APPEND | O_CREAT,
44 READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070045 // If we failed to open it, return.
Darin Petkov65b01462010-04-14 13:32:20 -070046 if (chrome_fd < 0) {
47 PrintError("open", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070048 return false;
Darin Petkov65b01462010-04-14 13:32:20 -070049 }
50
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070051 // Need to chmod because open flags are anded with umask. Ignore the
52 // exit code -- a chronos process may fail chmoding because the file
53 // has been created by a root process but that should be OK.
54 fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov65b01462010-04-14 13:32:20 -070055
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070056 // Grab an exclusive lock to protect Chrome from truncating
57 // underneath us. Keep the file locked as briefly as possible.
Darin Petkov65b01462010-04-14 13:32:20 -070058 if (flock(chrome_fd, LOCK_EX) < 0) {
59 PrintError("flock", kChromePath, errno);
60 close(chrome_fd);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070061 return false;
Darin Petkov65b01462010-04-14 13:32:20 -070062 }
63
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070064 bool success = true;
65 if (write(chrome_fd, message, length) != length) {
Darin Petkov65b01462010-04-14 13:32:20 -070066 PrintError("write", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070067 success = false;
68 }
Darin Petkov65b01462010-04-14 13:32:20 -070069
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070070 // Release the file lock and close file.
71 if (flock(chrome_fd, LOCK_UN) < 0) {
Darin Petkov65b01462010-04-14 13:32:20 -070072 PrintError("unlock", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070073 success = false;
74 }
Darin Petkov65b01462010-04-14 13:32:20 -070075 close(chrome_fd);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070076 return success;
77}
78
Darin Petkovc2526a12010-04-21 14:24:04 -070079// Formats a name/value message for Chrome in |buffer| and returns the
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070080// length of the message or a negative value on error.
81//
82// Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 |
83//
Darin Petkovc2526a12010-04-21 14:24:04 -070084// The arbitrary |format| argument covers the non-LENGTH portion of the
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070085// message. The caller is responsible to store the \0 character
86// between NAME and VALUE (e.g. "%s%c%d", name, '\0', value).
87static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer,
88 const char *format, ...) {
89 int32_t message_length;
90 size_t len_size = sizeof(message_length);
91
92 // Format the non-LENGTH contents in the buffer by leaving space for
93 // LENGTH at the start of the buffer.
94 va_list args;
95 va_start(args, format);
96 message_length = vsnprintf(&buffer[len_size], buffer_size - len_size,
97 format, args);
98 va_end(args);
99
100 if (message_length < 0) {
101 PrintError("chrome message format error", NULL, 0);
102 return -1;
103 }
104
105 // +1 to account for the trailing \0.
106 message_length += len_size + 1;
107 if (message_length > buffer_size) {
108 PrintError("chrome message too long", NULL, 0);
109 return -1;
110 }
111
112 // Prepend LENGTH to the message.
113 memcpy(buffer, &message_length, len_size);
114 return message_length;
115}
116
Darin Petkovfc91b422010-05-12 13:05:45 -0700117void MetricsLibrary::Init() {
118}
119
Darin Petkovc2526a12010-04-21 14:24:04 -0700120// static
121bool MetricsLibrary::SendToAutotest(const string& name, int value) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700122 FILE *autotest_file = fopen(kAutotestPath, "a+");
123 if (autotest_file == NULL) {
124 PrintError("fopen", kAutotestPath, errno);
125 return false;
126 }
127
128 fprintf(autotest_file, "%s=%d\n", name.c_str(), value);
129 fclose(autotest_file);
130 return true;
131}
132
Darin Petkov21cd2c52010-05-12 15:26:16 -0700133bool MetricsLibrary::SendToUMA(const string& name, int sample,
134 int min, int max, int nbuckets) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700135 // Format the message.
136 char message[kBufferSize];
137 int32_t message_length =
138 FormatChromeMessage(kBufferSize, message,
Darin Petkovc2526a12010-04-21 14:24:04 -0700139 "histogram%c%s %d %d %d %d", '\0',
140 name.c_str(), sample, min, max, nbuckets);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700141
142 if (message_length < 0)
143 return false;
144
145 // Send the message.
146 return SendMessageToChrome(message_length, message);
Darin Petkov65b01462010-04-14 13:32:20 -0700147}
Darin Petkov5b7dce12010-04-21 15:45:10 -0700148
Darin Petkov21cd2c52010-05-12 15:26:16 -0700149bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
150 int max) {
Darin Petkov5b7dce12010-04-21 15:45:10 -0700151 // Format the message.
152 char message[kBufferSize];
153 int32_t message_length =
154 FormatChromeMessage(kBufferSize, message,
155 "linearhistogram%c%s %d %d", '\0',
156 name.c_str(), sample, max);
157
158 if (message_length < 0)
159 return false;
160
161 // Send the message.
162 return SendMessageToChrome(message_length, message);
163}