blob: 2accb1ae830ecbb7ad0b5a73ec9e273c4d170c2a [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
5/*
6 * metrics_library.cc
7 *
8 * Created on: Dec 1, 2009
9 * Author: sosa
10 */
11
12#include "metrics_library.h"
13
14#include <errno.h>
15#include <sys/file.h>
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070016
17#include <cstdarg>
18#include <cstdio>
19#include <cstring>
Darin Petkov65b01462010-04-14 13:32:20 -070020
21#define READ_WRITE_ALL_FILE_FLAGS \
22 (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
23
Darin Petkovc2526a12010-04-21 14:24:04 -070024static const char kAutotestPath[] =
25 "/var/log/metrics/autotest-events";
26static const char kChromePath[] =
27 "/var/log/metrics/uma-events";
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070028static const int32_t kBufferSize = 1024;
Darin Petkov65b01462010-04-14 13:32:20 -070029
30using namespace std;
31
32// TODO(sosa@chromium.org) - use Chromium logger instead of stderr
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070033static void PrintError(const char *message, const char *file,
34 int code) {
Darin Petkov65b01462010-04-14 13:32:20 -070035 const char *kProgramName = "metrics_library";
36 if (code == 0) {
37 fprintf(stderr, "%s: %s\n", kProgramName, message);
38 } else if (file == NULL) {
39 fprintf(stderr, "%s: ", kProgramName);
40 perror(message);
41 } else {
42 fprintf(stderr, "%s: %s: ", kProgramName, file);
43 perror(message);
44 }
45}
46
Darin Petkovc2526a12010-04-21 14:24:04 -070047// Sends message of size |length| to Chrome and returns true on success.
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070048static bool SendMessageToChrome(int32_t length, const char *message) {
Darin Petkov65b01462010-04-14 13:32:20 -070049 int chrome_fd = open(kChromePath,
50 O_WRONLY | O_APPEND | O_CREAT,
51 READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070052 // If we failed to open it, return.
Darin Petkov65b01462010-04-14 13:32:20 -070053 if (chrome_fd < 0) {
54 PrintError("open", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070055 return false;
Darin Petkov65b01462010-04-14 13:32:20 -070056 }
57
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070058 // Need to chmod because open flags are anded with umask. Ignore the
59 // exit code -- a chronos process may fail chmoding because the file
60 // has been created by a root process but that should be OK.
61 fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov65b01462010-04-14 13:32:20 -070062
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070063 // Grab an exclusive lock to protect Chrome from truncating
64 // underneath us. Keep the file locked as briefly as possible.
Darin Petkov65b01462010-04-14 13:32:20 -070065 if (flock(chrome_fd, LOCK_EX) < 0) {
66 PrintError("flock", kChromePath, errno);
67 close(chrome_fd);
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 bool success = true;
72 if (write(chrome_fd, message, length) != length) {
Darin Petkov65b01462010-04-14 13:32:20 -070073 PrintError("write", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070074 success = false;
75 }
Darin Petkov65b01462010-04-14 13:32:20 -070076
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070077 // Release the file lock and close file.
78 if (flock(chrome_fd, LOCK_UN) < 0) {
Darin Petkov65b01462010-04-14 13:32:20 -070079 PrintError("unlock", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070080 success = false;
81 }
Darin Petkov65b01462010-04-14 13:32:20 -070082 close(chrome_fd);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070083 return success;
84}
85
Darin Petkovc2526a12010-04-21 14:24:04 -070086// Formats a name/value message for Chrome in |buffer| and returns the
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070087// length of the message or a negative value on error.
88//
89// Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 |
90//
Darin Petkovc2526a12010-04-21 14:24:04 -070091// The arbitrary |format| argument covers the non-LENGTH portion of the
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070092// message. The caller is responsible to store the \0 character
93// between NAME and VALUE (e.g. "%s%c%d", name, '\0', value).
94static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer,
95 const char *format, ...) {
96 int32_t message_length;
97 size_t len_size = sizeof(message_length);
98
99 // Format the non-LENGTH contents in the buffer by leaving space for
100 // LENGTH at the start of the buffer.
101 va_list args;
102 va_start(args, format);
103 message_length = vsnprintf(&buffer[len_size], buffer_size - len_size,
104 format, args);
105 va_end(args);
106
107 if (message_length < 0) {
108 PrintError("chrome message format error", NULL, 0);
109 return -1;
110 }
111
112 // +1 to account for the trailing \0.
113 message_length += len_size + 1;
114 if (message_length > buffer_size) {
115 PrintError("chrome message too long", NULL, 0);
116 return -1;
117 }
118
119 // Prepend LENGTH to the message.
120 memcpy(buffer, &message_length, len_size);
121 return message_length;
122}
123
Darin Petkovfc91b422010-05-12 13:05:45 -0700124void MetricsLibrary::Init() {
125}
126
Darin Petkovc2526a12010-04-21 14:24:04 -0700127// static
128bool MetricsLibrary::SendToAutotest(const string& name, int value) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700129 FILE *autotest_file = fopen(kAutotestPath, "a+");
130 if (autotest_file == NULL) {
131 PrintError("fopen", kAutotestPath, errno);
132 return false;
133 }
134
135 fprintf(autotest_file, "%s=%d\n", name.c_str(), value);
136 fclose(autotest_file);
137 return true;
138}
139
Darin Petkovc2526a12010-04-21 14:24:04 -0700140// static
141bool MetricsLibrary::SendToChrome(const string& name, int sample,
142 int min, int max, int nbuckets) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700143 // Format the message.
144 char message[kBufferSize];
145 int32_t message_length =
146 FormatChromeMessage(kBufferSize, message,
Darin Petkovc2526a12010-04-21 14:24:04 -0700147 "histogram%c%s %d %d %d %d", '\0',
148 name.c_str(), sample, min, max, nbuckets);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700149
150 if (message_length < 0)
151 return false;
152
153 // Send the message.
154 return SendMessageToChrome(message_length, message);
Darin Petkov65b01462010-04-14 13:32:20 -0700155}
Darin Petkov5b7dce12010-04-21 15:45:10 -0700156
Darin Petkovfc91b422010-05-12 13:05:45 -0700157bool MetricsLibrary::SendToUMA(const string& name, int sample,
158 int min, int max, int nbuckets) {
159 return SendToChrome(name, sample, min, max, nbuckets);
160}
161
Darin Petkov5b7dce12010-04-21 15:45:10 -0700162//static
163bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample,
164 int max) {
165 // Format the message.
166 char message[kBufferSize];
167 int32_t message_length =
168 FormatChromeMessage(kBufferSize, message,
169 "linearhistogram%c%s %d %d", '\0',
170 name.c_str(), sample, max);
171
172 if (message_length < 0)
173 return false;
174
175 // Send the message.
176 return SendMessageToChrome(message_length, message);
177}
Darin Petkovfc91b422010-05-12 13:05:45 -0700178
179bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
180 int max) {
181 return SendEnumToChrome(name, sample, max);
182}