blob: 95e78b2275b39515b8f54ef493296098d4af8c6d [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 Petkovc2526a12010-04-21 14:24:04 -0700124// static
125bool MetricsLibrary::SendToAutotest(const string& name, int value) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700126 FILE *autotest_file = fopen(kAutotestPath, "a+");
127 if (autotest_file == NULL) {
128 PrintError("fopen", kAutotestPath, errno);
129 return false;
130 }
131
132 fprintf(autotest_file, "%s=%d\n", name.c_str(), value);
133 fclose(autotest_file);
134 return true;
135}
136
Darin Petkovc2526a12010-04-21 14:24:04 -0700137// static
138bool MetricsLibrary::SendToChrome(const string& name, int sample,
139 int min, int max, int nbuckets) {
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700140 // Format the message.
141 char message[kBufferSize];
142 int32_t message_length =
143 FormatChromeMessage(kBufferSize, message,
Darin Petkovc2526a12010-04-21 14:24:04 -0700144 "histogram%c%s %d %d %d %d", '\0',
145 name.c_str(), sample, min, max, nbuckets);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -0700146
147 if (message_length < 0)
148 return false;
149
150 // Send the message.
151 return SendMessageToChrome(message_length, message);
Darin Petkov65b01462010-04-14 13:32:20 -0700152}
Darin Petkov5b7dce12010-04-21 15:45:10 -0700153
154//static
155bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample,
156 int max) {
157 // Format the message.
158 char message[kBufferSize];
159 int32_t message_length =
160 FormatChromeMessage(kBufferSize, message,
161 "linearhistogram%c%s %d %d", '\0',
162 name.c_str(), sample, max);
163
164 if (message_length < 0)
165 return false;
166
167 // Send the message.
168 return SendMessageToChrome(message_length, message);
169}