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 | |
41 | static 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 | |
70 | static 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 | |
94 | int 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 | |
104 | int 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 | |
169 | struct stat_fun_en { |
170 | stat_fun_t func; |
171 | struct stat_fun_en *next; |
172 | }; |
173 | struct stat_fun_en *stat_func_first = NULL; |
174 | |
175 | int 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 **********************************/ |
195 | void 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 | |
202 | void 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 | |
213 | void sb_release (stats_buffer_t *sb) { |
214 | if (sb->flags & 1) { |
215 | free (sb->buff); |
216 | } |
217 | sb->buff = NULL; |
218 | } |
219 | |
220 | static 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 | |
229 | static int sb_full (stats_buffer_t *sb) { |
230 | return (sb->pos == sb->size - 1 && sb->buff[sb->pos]) || sb->pos >= sb->size; |
231 | } |
232 | |
233 | void 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 | |
249 | void 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 | |
272 | void 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 | |
292 | void 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 | |
296 | int 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 | |
305 | long 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 | |
314 | double 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 | |
323 | void 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 | |