blob: 88f7d443a2c09e7e354df2d74b98120f1f037aa5 [file] [log] [blame]
Yao Chenb3be9ea2018-05-07 16:57:13 -07001/*
2 * Copyright (C) 2018, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "statsd_writer.h"
17
Howard Rod1730092018-10-22 17:37:42 -070018#include <cutils/fs.h>
Yao Chenb3be9ea2018-05-07 16:57:13 -070019#include <cutils/sockets.h>
Howard Rod1730092018-10-22 17:37:42 -070020#include <cutils/threads.h>
Yao Chenb3be9ea2018-05-07 16:57:13 -070021#include <errno.h>
22#include <fcntl.h>
23#include <inttypes.h>
24#include <poll.h>
25#include <private/android_filesystem_config.h>
26#include <private/android_logger.h>
27#include <stdarg.h>
28#include <stdatomic.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <sys/un.h>
35#include <time.h>
36#include <unistd.h>
37
38/* branchless on many architectures. */
39#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
40
Howard Rod1730092018-10-22 17:37:42 -070041#ifndef htole32
42#if __BYTE_ORDER == __LITTLE_ENDIAN
43#define htole32(x) (x)
44#else
45#define htole32(x) __bswap_32(x)
46#endif
47#endif
48
Yao Chenb3be9ea2018-05-07 16:57:13 -070049static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
Yao Chen63010542018-08-20 16:15:33 -070050static atomic_int dropped = 0;
Yao Chenb3be9ea2018-05-07 16:57:13 -070051
52void statsd_writer_init_lock() {
53 /*
54 * If we trigger a signal handler in the middle of locked activity and the
55 * signal handler logs a message, we could get into a deadlock state.
56 */
57 pthread_mutex_lock(&log_init_lock);
58}
59
60int statd_writer_trylock() {
61 return pthread_mutex_trylock(&log_init_lock);
62}
63
64void statsd_writer_init_unlock() {
65 pthread_mutex_unlock(&log_init_lock);
66}
67
68static int statsdAvailable();
69static int statsdOpen();
70static void statsdClose();
71static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
Yao Chen63010542018-08-20 16:15:33 -070072static void statsdNoteDrop();
Yao Chenb3be9ea2018-05-07 16:57:13 -070073
74struct android_log_transport_write statsdLoggerWrite = {
Yao Chen63010542018-08-20 16:15:33 -070075 .name = "statsd",
76 .sock = -EBADF,
77 .available = statsdAvailable,
78 .open = statsdOpen,
79 .close = statsdClose,
80 .write = statsdWrite,
81 .noteDrop = statsdNoteDrop,
Yao Chenb3be9ea2018-05-07 16:57:13 -070082};
83
84/* log_init_lock assumed */
85static int statsdOpen() {
86 int i, ret = 0;
87
88 i = atomic_load(&statsdLoggerWrite.sock);
89 if (i < 0) {
Howard Rod1730092018-10-22 17:37:42 -070090 int flags = SOCK_DGRAM;
91#ifdef SOCK_CLOEXEC
92 flags |= SOCK_CLOEXEC;
93#endif
94#ifdef SOCK_NONBLOCK
95 flags |= SOCK_NONBLOCK;
96#endif
97 int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
Yao Chenb3be9ea2018-05-07 16:57:13 -070098 if (sock < 0) {
99 ret = -errno;
100 } else {
101 struct sockaddr_un un;
102 memset(&un, 0, sizeof(struct sockaddr_un));
103 un.sun_family = AF_UNIX;
104 strcpy(un.sun_path, "/dev/socket/statsdw");
105
106 if (TEMP_FAILURE_RETRY(
107 connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
108 ret = -errno;
109 switch (ret) {
110 case -ENOTCONN:
111 case -ECONNREFUSED:
112 case -ENOENT:
113 i = atomic_exchange(&statsdLoggerWrite.sock, ret);
114 /* FALLTHRU */
115 default:
116 break;
117 }
118 close(sock);
119 } else {
120 ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
121 if ((ret >= 0) && (ret != sock)) {
122 close(ret);
123 }
124 ret = 0;
125 }
126 }
127 }
128
129 return ret;
130}
131
132static void __statsdClose(int negative_errno) {
133 int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
134 if (sock >= 0) {
135 close(sock);
136 }
137}
138
139static void statsdClose() {
140 __statsdClose(-EBADF);
141}
142
143static int statsdAvailable() {
144 if (atomic_load(&statsdLoggerWrite.sock) < 0) {
145 if (access("/dev/socket/statsdw", W_OK) == 0) {
146 return 0;
147 }
148 return -EBADF;
149 }
150 return 1;
151}
152
Yao Chen63010542018-08-20 16:15:33 -0700153static void statsdNoteDrop() {
154 atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
155}
156
Yao Chenb3be9ea2018-05-07 16:57:13 -0700157static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
158 ssize_t ret;
159 int sock;
160 static const unsigned headerLength = 1;
161 struct iovec newVec[nr + headerLength];
162 android_log_header_t header;
163 size_t i, payloadSize;
Yao Chenb3be9ea2018-05-07 16:57:13 -0700164
165 sock = atomic_load(&statsdLoggerWrite.sock);
166 if (sock < 0) switch (sock) {
167 case -ENOTCONN:
168 case -ECONNREFUSED:
169 case -ENOENT:
170 break;
171 default:
172 return -EBADF;
173 }
174 /*
175 * struct {
176 * // what we provide to socket
177 * android_log_header_t header;
178 * // caller provides
179 * union {
180 * struct {
181 * char prio;
182 * char payload[];
183 * } string;
184 * struct {
185 * uint32_t tag
186 * char payload[];
187 * } binary;
188 * };
189 * };
190 */
191
192 header.tid = gettid();
193 header.realtime.tv_sec = ts->tv_sec;
194 header.realtime.tv_nsec = ts->tv_nsec;
195
196 newVec[0].iov_base = (unsigned char*)&header;
197 newVec[0].iov_len = sizeof(header);
198
199 // If we dropped events before, try to tell statsd.
200 if (sock >= 0) {
201 int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
202 if (snapshot) {
203 android_log_event_int_t buffer;
204 header.id = LOG_ID_STATS;
205 buffer.header.tag = htole32(LIBLOG_LOG_TAG);
206 buffer.payload.type = EVENT_TYPE_INT;
207 buffer.payload.data = htole32(snapshot);
208
209 newVec[headerLength].iov_base = &buffer;
210 newVec[headerLength].iov_len = sizeof(buffer);
211
212 ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
213 if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
214 atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
215 }
216 }
217 }
218
219 header.id = LOG_ID_STATS;
220
221 for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
222 newVec[i].iov_base = vec[i - headerLength].iov_base;
223 payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
224
225 if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
226 newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
227 if (newVec[i].iov_len) {
228 ++i;
229 }
230 break;
231 }
232 }
233
234 /*
235 * The write below could be lost, but will never block.
236 *
237 * ENOTCONN occurs if statsd has died.
238 * ENOENT occurs if statsd is not running and socket is missing.
239 * ECONNREFUSED occurs if we can not reconnect to statsd.
240 * EAGAIN occurs if statsd is overloaded.
241 */
242 if (sock < 0) {
243 ret = sock;
244 } else {
245 ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
246 if (ret < 0) {
247 ret = -errno;
248 }
249 }
250 switch (ret) {
251 case -ENOTCONN:
252 case -ECONNREFUSED:
253 case -ENOENT:
254 if (statd_writer_trylock()) {
255 return ret; /* in a signal handler? try again when less stressed
256 */
257 }
258 __statsdClose(ret);
259 ret = statsdOpen();
260 statsd_writer_init_unlock();
261
262 if (ret < 0) {
263 return ret;
264 }
265
266 ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
267 if (ret < 0) {
268 ret = -errno;
269 }
270 /* FALLTHRU */
271 default:
272 break;
273 }
274
275 if (ret > (ssize_t)sizeof(header)) {
276 ret -= sizeof(header);
Yao Chenb3be9ea2018-05-07 16:57:13 -0700277 }
278
279 return ret;
280}