1/*
2 This file is part of Mtproto-proxy Library.
3
4 Mtproto-proxy Library is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 Mtproto-proxy Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright 2012-2013 Vkontakte Ltd
18 2012-2013 Anton Maydell
19
20 Copyright 2014-2017 Telegram Messenger Inc
21 2014-2017 Anton Maydell
22*/
23
24#define _FILE_OFFSET_BITS 64
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdarg.h>
30#include <ctype.h>
31#include <assert.h>
32#include <errno.h>
33#include <unistd.h>
34#include <fcntl.h>
35#include "kprintf.h"
36#include "precise-time.h"
37#include "server-functions.h"
38#include "common/common-stats.h"
39#include "net/net-connections.h"
40
41static int read_whole_file (char *filename, void *output, int olen) {
42 int fd = open (filename, O_RDONLY), n = -1;
43 if (fd < 0) {
44 vkprintf (1, "%s: open (\"%s\", O_RDONLY) failed. %m\n", __func__, filename);
45 return -1;
46 }
47 do {
48 n = read (fd, output, olen);
49 if (n < 0) {
50 if (errno == EINTR) {
51 continue;
52 }
53 vkprintf (1, "%s: read from %s failed. %m\n", __func__, filename);
54 }
55 break;
56 } while (1);
57 while (close (fd) < 0 && errno == EINTR) {}
58 if (n < 0) {
59 return -1;
60 }
61 if (n >= olen) {
62 vkprintf (1, "%s: output buffer is too small (%d bytes).\n", __func__, olen);
63 return -1;
64 }
65 unsigned char *p = output;
66 p[n] = 0;
67 return n;
68}
69
70static int parse_statm (const char *buf, long long *a, int m) {
71 static long long page_size = -1;
72 if (page_size < 0) {
73 page_size = sysconf (_SC_PAGESIZE);
74 assert (page_size > 0);
75 }
76 int i;
77 if (m > 7) {
78 m = 7;
79 }
80 const char *p = buf;
81 char *q;
82 errno = 0;
83 for (i = 0; i < m; i++) {
84 a[i] = strtoll (p, &q, 10);
85 if (p == q || errno) {
86 return -1;
87 }
88 a[i] *= page_size;
89 p = q;
90 }
91 return 0;
92}
93
94int am_get_memory_usage (pid_t pid, long long *a, int m) {
95 char proc_filename[32];
96 char buf[4096];
97 assert (snprintf (proc_filename, sizeof (proc_filename), "/proc/%d/statm", (int) pid) < sizeof (proc_filename));
98 if (read_whole_file (proc_filename, buf, sizeof (buf)) < 0) {
99 return -1;
100 }
101 return parse_statm (buf, a, m);
102}
103
104int am_get_memory_stats (am_memory_stat_t *S, int flags) {
105 if (!flags) {
106 return -1;
107 }
108 long long a[6];
109
110 if (flags & AM_GET_MEMORY_USAGE_SELF) {
111 if (am_get_memory_usage (getpid (), a, 6) < 0) {
112 return -1;
113 }
114 S->vm_size = a[0];
115 S->vm_rss = a[1];
116 S->vm_data = a[5];
117 }
118
119 if (flags & AM_GET_MEMORY_USAGE_OVERALL) {
120 char buf[16384], *p;
121 if (read_whole_file ("/proc/meminfo", buf, sizeof (buf)) < 0) {
122 return -1;
123 }
124 vkprintf (4, "/proc/meminfo: %s\n", buf);
125 char suffix[32];
126 long long value;
127 int r = 0;
128 for (p = strtok (buf, "\n"); r != 15 && p != NULL; p = strtok (NULL, "\n")) {
129 switch (*p++) {
130 case 'C':
131 if (!memcmp (p, "ached:", 6)) {
132 if (sscanf (p + 6, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
133 S->mem_cached = value << 10;
134 r |= 8;
135 }
136 }
137 break;
138 case 'M':
139 if (!memcmp (p, "emFree:", 7)) {
140 if (sscanf (p + 7, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
141 S->mem_free = value << 10;
142 r |= 1;
143 }
144 }
145 break;
146 case 'S':
147 if (!memcmp (p, "wapTotal:", 9)) {
148 if (sscanf (p + 9, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
149 S->swap_total = value << 10;
150 r |= 2;
151 }
152 } else if (!memcmp (p, "wapFree:", 8)) {
153 if (sscanf (p + 8, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
154 S->swap_free = value << 10;
155 r |= 4;
156 }
157 }
158 break;
159 }
160 }
161 if (r != 15) {
162 return -1;
163 }
164 S->swap_used = S->swap_total - S->swap_free;
165 }
166 return 0;
167}
168
169struct stat_fun_en {
170 stat_fun_t func;
171 struct stat_fun_en *next;
172};
173struct stat_fun_en *stat_func_first = NULL;
174
175int sb_register_stat_fun (stat_fun_t func) {
176 struct stat_fun_en *last = NULL, *p;
177 for (p = stat_func_first; p; p = p->next) {
178 last = p;
179 if (p->func == func) {
180 return 0;
181 }
182 }
183 p = malloc (sizeof (*p));
184 p->func = func;
185 p->next = NULL;
186 if (last) {
187 last->next = p;
188 } else {
189 stat_func_first = p;
190 }
191 return 1;
192}
193
194/************************ stats buffer functions **********************************/
195void sb_init (stats_buffer_t *sb, char *buff, int size) {
196 sb->buff = buff;
197 sb->pos = 0;
198 sb->size = size;
199 sb->flags = 0;
200}
201
202void sb_alloc (stats_buffer_t *sb, int size) {
203 if (size < 16) {
204 size = 16;
205 }
206 sb->buff = malloc (size);
207 assert (sb->buff);
208 sb->pos = 0;
209 sb->size = size;
210 sb->flags = 1;
211}
212
213void sb_release (stats_buffer_t *sb) {
214 if (sb->flags & 1) {
215 free (sb->buff);
216 }
217 sb->buff = NULL;
218}
219
220static void sb_truncate (stats_buffer_t *sb) {
221 sb->buff[sb->size - 1] = 0;
222 sb->pos = sb->size - 2;
223 while (sb->pos >= 0 && sb->buff[sb->pos] != '\n') {
224 sb->buff[sb->pos--] = 0;
225 }
226 sb->pos++;
227}
228
229static int sb_full (stats_buffer_t *sb) {
230 return (sb->pos == sb->size - 1 && sb->buff[sb->pos]) || sb->pos >= sb->size;
231}
232
233void sb_prepare (stats_buffer_t *sb) {
234 sb->pos = prepare_stats (sb->buff, sb->size);
235 if (sb_full (sb)) {
236 sb_truncate (sb);
237 return;
238 }
239 struct stat_fun_en *p;
240 for (p = stat_func_first; p; p = p->next) {
241 p->func (sb);
242 if (sb_full (sb)) {
243 sb_truncate (sb);
244 return;
245 }
246 }
247}
248
249void sb_printf (stats_buffer_t *sb, const char *format, ...) {
250 if (sb->pos >= sb->size) { return; }
251 const int old_pos = sb->pos;
252 va_list ap;
253 va_start (ap, format);
254 sb->pos += vsnprintf (sb->buff + old_pos, sb->size - old_pos, format, ap);
255 va_end (ap);
256 if (sb->pos >= sb->size) {
257 if (sb->flags & 1) {
258 sb->size = 2 * sb->pos;
259 sb->buff = realloc (sb->buff, sb->size);
260 assert (sb->buff);
261 va_start (ap, format);
262 sb->pos = old_pos + vsnprintf (sb->buff + old_pos, sb->size - old_pos, format, ap);
263 va_end (ap);
264 assert (sb->pos < sb->size);
265 } else {
266 sb_truncate (sb);
267 }
268 }
269}
270/************************************************************************************/
271
272void sb_memory (stats_buffer_t *sb, int flags) {
273 am_memory_stat_t S;
274 if (!am_get_memory_stats (&S, flags & AM_GET_MEMORY_USAGE_SELF)) {
275 sb_printf (sb,
276 "vmsize_bytes\t%lld\n"
277 "vmrss_bytes\t%lld\n"
278 "vmdata_bytes\t%lld\n",
279 S.vm_size, S.vm_rss, S.vm_data);
280 }
281
282 if (!am_get_memory_stats (&S, flags & AM_GET_MEMORY_USAGE_OVERALL)) {
283 sb_printf (sb,
284 "memfree_bytes\t%lld\n"
285 "memcached_bytes\t%lld\n"
286 "swap_used_bytes\t%lld\n"
287 "swap_total_bytes\t%lld\n",
288 S.mem_free, S.mem_cached, S.swap_used, S.swap_total);
289 }
290}
291
292void sb_print_queries (stats_buffer_t *sb, const char *const desc, long long q) {
293 sb_printf (sb, "%s\t%lld\nqps_%s\t%.3lf\n", desc, q, desc, safe_div (q, now - start_time));
294}
295
296int sb_sum_i (void **base, int len, int offset) {
297 int res = 0;
298 int i;
299 for (i = 0; i < len; i++) if (base[i]) {
300 res += *(int *)((base[i]) + offset);
301 }
302 return res;
303}
304
305long long sb_sum_ll (void **base, int len, int offset) {
306 long long res = 0;
307 int i;
308 for (i = 0; i < len; i++) if (base[i]) {
309 res += *(long long *)((base[i]) + offset);
310 }
311 return res;
312}
313
314double sb_sum_f (void **base, int len, int offset) {
315 double res = 0;
316 int i;
317 for (i = 0; i < len; i++) if (base[i]) {
318 res += *(double *)((base[i]) + offset);
319 }
320 return res;
321}
322
323void sbp_print_date (stats_buffer_t *sb, const char *key, time_t unix_time) {
324 struct tm b;
325 struct tm *t = gmtime_r (&unix_time, &b);
326 if (t) {
327 char s[256];
328 size_t l = strftime (s, sizeof (s), "%c", t);
329 if (l > 0) {
330 sb_printf (sb, "%s\t%s\n", key, s);
331 }
332 }
333}
334