1 /*
2 * Copyright (C) 2019 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
17 #include "stats_buffer_writer.h"
18
19 #include <com_android_os_statsd_flags.h>
20 #include <errno.h>
21 #include <sys/time.h>
22 #include <sys/uio.h>
23
24 #include "logging_rate_limiter.h"
25 #include "stats_buffer_writer_impl.h"
26 #include "stats_buffer_writer_queue.h"
27 #include "statsd_writer.h"
28
29 static const uint32_t kStatsEventTag = 1937006964;
30
31 extern struct android_log_transport_write statsdLoggerWrite;
32
33 namespace flags = com::android::os::statsd::flags;
34
35 static int __write_to_statsd_init(struct iovec* vec, size_t nr);
36 static int (*__write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
37
38 /**
39 * @brief Logs the error code associated with atom loss
40 *
41 * @param error To distinguish source of error, the errno code values must be negative,
42 * while the libstatssocket internal error codes are positive
43 */
note_log_drop(int error,int atomId)44 void note_log_drop(int error, int atomId) {
45 statsdLoggerWrite.noteDrop(error, atomId);
46 }
47
stats_log_close()48 void stats_log_close() {
49 statsd_writer_init_lock();
50 __write_to_statsd = __write_to_statsd_init;
51 if (statsdLoggerWrite.close) {
52 (*statsdLoggerWrite.close)();
53 }
54 statsd_writer_init_unlock();
55 }
56
stats_log_is_closed()57 int stats_log_is_closed() {
58 return statsdLoggerWrite.isClosed && (*statsdLoggerWrite.isClosed)();
59 }
60
can_log_atom(uint32_t atomId)61 bool can_log_atom(uint32_t atomId) {
62 // Below values should be justified with experiments, as of now idea is to
63 // allow to fill 10% of socket buffer at max (max_dgram_qlen == 2400) within 100ms.
64 // This allows to fill entire buffer within a second.
65 // Higher frequency considered as abnormality
66 constexpr int32_t kLogFrequencyThreshold = 240;
67 constexpr int32_t kLoggingFrequencyWindowMs = 100;
68
69 static LoggingRateLimiter<RealTimeClock> rateLimiter(kLogFrequencyThreshold,
70 kLoggingFrequencyWindowMs);
71 return rateLimiter.canLogAtom(atomId);
72 }
73
write_buffer_to_statsd(void * buffer,size_t size,uint32_t atomId)74 int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
75 constexpr int kQueueOverflowErrorCode = 1;
76 constexpr int kLoggingRateLimitExceededErrorCode = 2;
77
78 if (should_write_via_queue(atomId)) {
79 const bool ret =
80 write_buffer_to_statsd_queue(static_cast<const uint8_t*>(buffer), size, atomId);
81 if (!ret) {
82 // to account on the loss, note atom drop with predefined internal error code
83 note_log_drop(kQueueOverflowErrorCode, atomId);
84 }
85 return ret;
86 }
87
88 if (flags::logging_rate_limit_enabled() && !can_log_atom(atomId)) {
89 note_log_drop(kLoggingRateLimitExceededErrorCode, atomId);
90 return 0;
91 }
92
93 return write_buffer_to_statsd_impl(buffer, size, atomId, /*doNoteDrop*/ true);
94 }
95
write_buffer_to_statsd_impl(void * buffer,size_t size,uint32_t atomId,bool doNoteDrop)96 int write_buffer_to_statsd_impl(void* buffer, size_t size, uint32_t atomId, bool doNoteDrop) {
97 int ret = 1;
98
99 struct iovec vecs[2];
100 vecs[0].iov_base = (void*)&kStatsEventTag;
101 vecs[0].iov_len = sizeof(kStatsEventTag);
102 vecs[1].iov_base = buffer;
103 vecs[1].iov_len = size;
104
105 ret = __write_to_statsd(vecs, 2);
106
107 if (ret < 0 && doNoteDrop) {
108 note_log_drop(ret, atomId);
109 }
110
111 return ret;
112 }
113
__write_to_stats_daemon(struct iovec * vec,size_t nr)114 static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
115 int save_errno;
116 struct timespec ts;
117 size_t len, i;
118
119 for (len = i = 0; i < nr; ++i) {
120 len += vec[i].iov_len;
121 }
122 if (!len) {
123 return -EINVAL;
124 }
125
126 save_errno = errno;
127 #if defined(__ANDROID__)
128 clock_gettime(CLOCK_REALTIME, &ts);
129 #else
130 struct timeval tv;
131 gettimeofday(&tv, NULL);
132 ts.tv_sec = tv.tv_sec;
133 ts.tv_nsec = tv.tv_usec * 1000;
134 #endif
135
136 int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
137 errno = save_errno;
138 return ret;
139 }
140
__write_to_statsd_initialize_locked()141 static int __write_to_statsd_initialize_locked() {
142 if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
143 if (statsdLoggerWrite.close) {
144 (*statsdLoggerWrite.close)();
145 return -ENODEV;
146 }
147 }
148 return 1;
149 }
150
__write_to_statsd_init(struct iovec * vec,size_t nr)151 static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
152 int ret, save_errno = errno;
153
154 statsd_writer_init_lock();
155
156 if (__write_to_statsd == __write_to_statsd_init) {
157 ret = __write_to_statsd_initialize_locked();
158 if (ret < 0) {
159 statsd_writer_init_unlock();
160 errno = save_errno;
161 return ret;
162 }
163
164 __write_to_statsd = __write_to_stats_daemon;
165 }
166
167 statsd_writer_init_unlock();
168
169 ret = __write_to_statsd(vec, nr);
170 errno = save_errno;
171 return ret;
172 }
173