]>
Commit | Line | Data |
---|---|---|
7dc6f6e2 DE |
1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * Copyright © 2014 Oracle. | |
5 | * | |
6 | * Authors: | |
7 | * Dwight Engen <dwight.engen@oracle.com> | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | */ | |
23 | ||
adc09141 | 24 | #define _GNU_SOURCE |
9dba726e | 25 | #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ |
7dc6f6e2 | 26 | #include <errno.h> |
9dba726e | 27 | #include <inttypes.h> |
7dc6f6e2 DE |
28 | #include <signal.h> |
29 | #include <stdbool.h> | |
adc09141 | 30 | #include <stdint.h> |
7dc6f6e2 DE |
31 | #include <stdio.h> |
32 | #include <stdlib.h> | |
adc09141 | 33 | #include <string.h> |
7dc6f6e2 DE |
34 | #include <termios.h> |
35 | #include <unistd.h> | |
36 | #include <sys/epoll.h> | |
37 | #include <sys/ioctl.h> | |
adc09141 CB |
38 | #include <sys/time.h> |
39 | ||
7dc6f6e2 DE |
40 | #include <lxc/lxccontainer.h> |
41 | ||
42 | #include "arguments.h" | |
adc09141 | 43 | #include "tool_utils.h" |
7dc6f6e2 | 44 | |
7dc6f6e2 DE |
45 | #define USER_HZ 100 |
46 | #define ESC "\033" | |
47 | #define TERMCLEAR ESC "[H" ESC "[J" | |
48 | #define TERMNORM ESC "[0m" | |
49 | #define TERMBOLD ESC "[1m" | |
50 | #define TERMRVRS ESC "[7m" | |
51 | ||
7c9d733f | 52 | struct blkio_stats { |
53 | uint64_t read; | |
54 | uint64_t write; | |
55 | uint64_t total; | |
56 | }; | |
57 | ||
7dc6f6e2 DE |
58 | struct stats { |
59 | uint64_t mem_used; | |
60 | uint64_t mem_limit; | |
6b6e702a NA |
61 | uint64_t memsw_used; |
62 | uint64_t memsw_limit; | |
7dc6f6e2 DE |
63 | uint64_t kmem_used; |
64 | uint64_t kmem_limit; | |
65 | uint64_t cpu_use_nanos; | |
66 | uint64_t cpu_use_user; | |
67 | uint64_t cpu_use_sys; | |
7c9d733f | 68 | struct blkio_stats io_service_bytes; |
69 | struct blkio_stats io_serviced; | |
7dc6f6e2 DE |
70 | }; |
71 | ||
72 | struct ct { | |
73 | struct lxc_container *c; | |
74 | struct stats *stats; | |
75 | }; | |
76 | ||
b420058d | 77 | static int batch = 0; |
78 | static int delay_set = 0; | |
7dc6f6e2 DE |
79 | static int delay = 3; |
80 | static char sort_by = 'n'; | |
81 | static int sort_reverse = 0; | |
7dc6f6e2 DE |
82 | static struct termios oldtios; |
83 | static struct ct *ct = NULL; | |
84 | static int ct_alloc_cnt = 0; | |
85 | ||
86 | static int my_parser(struct lxc_arguments* args, int c, char* arg) | |
87 | { | |
88 | switch (c) { | |
45419e74 | 89 | case 'd': |
b420058d | 90 | delay_set = 1; |
45419e74 CB |
91 | if (lxc_safe_int(arg, &delay) < 0) |
92 | return -1; | |
93 | break; | |
b420058d | 94 | case 'b': |
95 | batch=1; | |
96 | break; | |
45419e74 CB |
97 | case 's': |
98 | sort_by = arg[0]; | |
99 | break; | |
100 | case 'r': | |
101 | sort_reverse = 1; | |
102 | break; | |
7dc6f6e2 DE |
103 | } |
104 | return 0; | |
105 | } | |
106 | ||
107 | static const struct option my_longopts[] = { | |
108 | {"delay", required_argument, 0, 'd'}, | |
b420058d | 109 | {"batch", no_argument, 0, 'b'}, |
7dc6f6e2 DE |
110 | {"sort", required_argument, 0, 's'}, |
111 | {"reverse", no_argument, 0, 'r'}, | |
112 | LXC_COMMON_OPTIONS | |
113 | }; | |
114 | ||
115 | static struct lxc_arguments my_args = { | |
116 | .progname = "lxc-top", | |
117 | .help = "\ | |
8564baf9 | 118 | \n\ |
7dc6f6e2 DE |
119 | \n\ |
120 | lxc-top monitors the state of the active containers\n\ | |
121 | \n\ | |
122 | Options :\n\ | |
123 | -d, --delay delay in seconds between refreshes (default: 3.0)\n\ | |
b420058d | 124 | -b, --batch output designed to capture to a file\n\ |
7dc6f6e2 DE |
125 | -s, --sort sort by [n,c,b,m] (default: n) where\n\ |
126 | n = Name\n\ | |
127 | c = CPU use\n\ | |
128 | b = Block I/O use\n\ | |
129 | m = Memory use\n\ | |
6b6e702a | 130 | s = Memory + Swap use\n\ |
7dc6f6e2 DE |
131 | k = Kernel memory use\n\ |
132 | -r, --reverse sort in reverse (descending) order\n", | |
133 | .name = ".*", | |
134 | .options = my_longopts, | |
135 | .parser = my_parser, | |
136 | .checker = NULL, | |
137 | .lxcpath_additional = -1, | |
138 | }; | |
139 | ||
140 | static void stdin_tios_restore(void) | |
141 | { | |
142 | tcsetattr(0, TCSAFLUSH, &oldtios); | |
4db667c4 | 143 | fprintf(stderr, "\n"); |
7dc6f6e2 DE |
144 | } |
145 | ||
146 | static int stdin_tios_setup(void) | |
147 | { | |
148 | struct termios newtios; | |
149 | ||
150 | if (!isatty(0)) { | |
8052112c | 151 | fprintf(stderr, "stdin is not a tty\n"); |
7dc6f6e2 DE |
152 | return -1; |
153 | } | |
154 | ||
155 | if (tcgetattr(0, &oldtios)) { | |
8052112c | 156 | fprintf(stderr, "failed to get current terminal settings\n"); |
7dc6f6e2 DE |
157 | return -1; |
158 | } | |
159 | ||
160 | newtios = oldtios; | |
161 | ||
162 | /* turn off echo and line buffering */ | |
163 | newtios.c_iflag &= ~IGNBRK; | |
164 | newtios.c_iflag &= BRKINT; | |
165 | newtios.c_lflag &= ~(ECHO|ICANON); | |
166 | newtios.c_cc[VMIN] = 1; | |
167 | newtios.c_cc[VTIME] = 0; | |
168 | ||
169 | if (tcsetattr(0, TCSAFLUSH, &newtios)) { | |
8052112c | 170 | fprintf(stderr, "failed to set new terminal settings\n"); |
7dc6f6e2 DE |
171 | return -1; |
172 | } | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | static int stdin_tios_rows(void) | |
178 | { | |
179 | struct winsize wsz; | |
180 | if (isatty(0) && ioctl(0, TIOCGWINSZ, &wsz) == 0) | |
181 | return wsz.ws_row; | |
182 | return 25; | |
183 | } | |
184 | ||
7dc6f6e2 DE |
185 | static void sig_handler(int sig) |
186 | { | |
187 | exit(EXIT_SUCCESS); | |
188 | } | |
189 | ||
190 | static void size_humanize(unsigned long long val, char *buf, size_t bufsz) | |
191 | { | |
de429548 CB |
192 | int ret; |
193 | ||
7dc6f6e2 | 194 | if (val > 1 << 30) { |
de429548 | 195 | ret = snprintf(buf, bufsz, "%u.%2.2u GiB", |
6fc7d8b6 | 196 | (unsigned int)(val >> 30), |
197 | (unsigned int)(val & ((1 << 30) - 1)) / 10737419); | |
7dc6f6e2 | 198 | } else if (val > 1 << 20) { |
6fc7d8b6 | 199 | unsigned int x = val + 5243; /* for rounding */ |
de429548 | 200 | ret = snprintf(buf, bufsz, "%u.%2.2u MiB", |
7dc6f6e2 DE |
201 | x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); |
202 | } else if (val > 1 << 10) { | |
6fc7d8b6 | 203 | unsigned int x = val + 5; /* for rounding */ |
de429548 | 204 | ret = snprintf(buf, bufsz, "%u.%2.2u KiB", |
7dc6f6e2 DE |
205 | x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); |
206 | } else { | |
de429548 | 207 | ret = snprintf(buf, bufsz, "%3u.00 ", (unsigned int)val); |
7dc6f6e2 | 208 | } |
de429548 CB |
209 | |
210 | if (ret < 0 || (size_t)ret >= bufsz) | |
211 | fprintf(stderr, "Failed to create string\n"); | |
7dc6f6e2 DE |
212 | } |
213 | ||
214 | static uint64_t stat_get_int(struct lxc_container *c, const char *item) | |
215 | { | |
216 | char buf[80]; | |
217 | int len; | |
218 | uint64_t val; | |
219 | ||
220 | len = c->get_cgroup_item(c, item, buf, sizeof(buf)); | |
221 | if (len <= 0) { | |
8052112c | 222 | fprintf(stderr, "unable to read cgroup item %s\n", item); |
7dc6f6e2 DE |
223 | return 0; |
224 | } | |
225 | ||
226 | val = strtoull(buf, NULL, 0); | |
227 | return val; | |
228 | } | |
229 | ||
230 | static uint64_t stat_match_get_int(struct lxc_container *c, const char *item, | |
231 | const char *match, int column) | |
232 | { | |
233 | char buf[4096]; | |
234 | int i,j,len; | |
235 | uint64_t val = 0; | |
236 | char **lines, **cols; | |
237 | size_t matchlen; | |
238 | ||
239 | len = c->get_cgroup_item(c, item, buf, sizeof(buf)); | |
240 | if (len <= 0) { | |
8052112c | 241 | fprintf(stderr, "unable to read cgroup item %s\n", item); |
7dc6f6e2 DE |
242 | goto out; |
243 | } | |
244 | ||
245 | lines = lxc_string_split_and_trim(buf, '\n'); | |
246 | if (!lines) | |
247 | goto out; | |
248 | ||
249 | matchlen = strlen(match); | |
250 | for (i = 0; lines[i]; i++) { | |
251 | if (strncmp(lines[i], match, matchlen) == 0) { | |
252 | cols = lxc_string_split_and_trim(lines[i], ' '); | |
253 | if (!cols) | |
254 | goto err1; | |
255 | for (j = 0; cols[j]; j++) { | |
256 | if (j == column) { | |
257 | val = strtoull(cols[j], NULL, 0); | |
258 | break; | |
259 | } | |
260 | } | |
261 | lxc_free_array((void **)cols, free); | |
262 | break; | |
263 | } | |
264 | } | |
265 | err1: | |
266 | lxc_free_array((void **)lines, free); | |
267 | out: | |
268 | return val; | |
269 | } | |
270 | ||
7c9d733f | 271 | /* |
272 | examples: | |
273 | blkio.throttle.io_serviced | |
274 | 8:0 Read 4259 | |
275 | 8:0 Write 835 | |
276 | 8:0 Sync 292 | |
277 | 8:0 Async 4802 | |
278 | 8:0 Total 5094 | |
279 | Total 5094 | |
280 | ||
281 | blkio.throttle.io_service_bytes | |
282 | 8:0 Read 110309376 | |
283 | 8:0 Write 39018496 | |
284 | 8:0 Sync 2818048 | |
285 | 8:0 Async 146509824 | |
286 | 8:0 Total 149327872 | |
287 | Total 149327872 | |
288 | */ | |
289 | static void stat_get_blk_stats(struct lxc_container *c, const char *item, | |
290 | struct blkio_stats *stats) { | |
291 | char buf[4096]; | |
292 | int i, len; | |
293 | char **lines, **cols; | |
294 | ||
295 | len = c->get_cgroup_item(c, item, buf, sizeof(buf)); | |
adc09141 | 296 | if (len <= 0 || (size_t)len >= sizeof(buf)) { |
8052112c | 297 | fprintf(stderr, "unable to read cgroup item %s\n", item); |
7c9d733f | 298 | return; |
299 | } | |
300 | ||
301 | lines = lxc_string_split_and_trim(buf, '\n'); | |
302 | if (!lines) | |
303 | return; | |
304 | ||
305 | memset(stats, 0, sizeof(struct blkio_stats)); | |
306 | for (i = 0; lines[i]; i++) { | |
307 | cols = lxc_string_split_and_trim(lines[i], ' '); | |
308 | if (!cols) | |
309 | goto out; | |
310 | if (strcmp(cols[1], "Read") == 0) | |
311 | stats->read += strtoull(cols[2], NULL, 0); | |
312 | else if (strcmp(cols[1], "Write") == 0) | |
313 | stats->write += strtoull(cols[2], NULL, 0); | |
314 | if (strcmp(cols[0], "Total") == 0) | |
315 | stats->total = strtoull(cols[1], NULL, 0); | |
316 | ||
317 | lxc_free_array((void **)cols, free); | |
318 | } | |
319 | out: | |
320 | lxc_free_array((void **)lines, free); | |
321 | return; | |
322 | } | |
323 | ||
7dc6f6e2 DE |
324 | static void stats_get(struct lxc_container *c, struct ct *ct, struct stats *total) |
325 | { | |
326 | ct->c = c; | |
327 | ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes"); | |
328 | ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes"); | |
6b6e702a NA |
329 | ct->stats->memsw_used = stat_get_int(c, "memory.memsw.usage_in_bytes"); |
330 | ct->stats->memsw_limit = stat_get_int(c, "memory.memsw.limit_in_bytes"); | |
7dc6f6e2 DE |
331 | ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes"); |
332 | ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes"); | |
333 | ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage"); | |
334 | ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1); | |
335 | ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1); | |
7c9d733f | 336 | stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes); |
337 | stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced); | |
7dc6f6e2 DE |
338 | |
339 | if (total) { | |
340 | total->mem_used = total->mem_used + ct->stats->mem_used; | |
341 | total->mem_limit = total->mem_limit + ct->stats->mem_limit; | |
6b6e702a NA |
342 | total->memsw_used = total->memsw_used + ct->stats->memsw_used; |
343 | total->memsw_limit = total->memsw_limit + ct->stats->memsw_limit; | |
7dc6f6e2 DE |
344 | total->kmem_used = total->kmem_used + ct->stats->kmem_used; |
345 | total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit; | |
346 | total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos; | |
347 | total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user; | |
348 | total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys; | |
7c9d733f | 349 | total->io_service_bytes.total += ct->stats->io_service_bytes.total; |
350 | total->io_service_bytes.read += ct->stats->io_service_bytes.read; | |
351 | total->io_service_bytes.write += ct->stats->io_service_bytes.write; | |
7dc6f6e2 DE |
352 | } |
353 | } | |
354 | ||
355 | static void stats_print_header(struct stats *stats) | |
356 | { | |
357 | printf(TERMRVRS TERMBOLD); | |
7c9d733f | 358 | printf("%-18s %12s %12s %12s %36s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem"); |
6b6e702a NA |
359 | if (stats->memsw_used > 0) |
360 | printf(" %10s", "MemSw"); | |
7dc6f6e2 DE |
361 | if (stats->kmem_used > 0) |
362 | printf(" %10s", "KMem"); | |
363 | printf("\n"); | |
364 | ||
7c9d733f | 365 | printf("%-18s %12s %12s %12s %36s %10s", "Name", "Used", "Sys", "User", "Total(Read/Write)", "Used"); |
6b6e702a NA |
366 | if (stats->memsw_used > 0) |
367 | printf(" %10s", "Used"); | |
7dc6f6e2 DE |
368 | if (stats->kmem_used > 0) |
369 | printf(" %10s", "Used"); | |
370 | printf("\n"); | |
371 | printf(TERMNORM); | |
372 | } | |
373 | ||
374 | static void stats_print(const char *name, const struct stats *stats, | |
375 | const struct stats *total) | |
376 | { | |
7c9d733f | 377 | char iosb_str[63]; |
378 | char iosb_total_str[20]; | |
379 | char iosb_read_str[20]; | |
380 | char iosb_write_str[20]; | |
7dc6f6e2 | 381 | char mem_used_str[20]; |
6b6e702a | 382 | char memsw_used_str[20]; |
7dc6f6e2 | 383 | char kmem_used_str[20]; |
b420058d | 384 | struct timeval time_val; |
385 | unsigned long long time_ms; | |
7c9d733f | 386 | int ret; |
9dba726e | 387 | |
b420058d | 388 | if (!batch) { |
7c9d733f | 389 | size_humanize(stats->io_service_bytes.total, iosb_total_str, sizeof(iosb_total_str)); |
390 | size_humanize(stats->io_service_bytes.read, iosb_read_str, sizeof(iosb_read_str)); | |
391 | size_humanize(stats->io_service_bytes.write, iosb_write_str, sizeof(iosb_write_str)); | |
b420058d | 392 | size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str)); |
9dba726e | 393 | |
7c9d733f | 394 | ret = snprintf(iosb_str, sizeof(iosb_str), "%s(%s/%s)", iosb_total_str, iosb_read_str, iosb_write_str); |
adc09141 | 395 | if (ret < 0 || (size_t)ret >= sizeof(iosb_str)) |
8052112c | 396 | printf("snprintf'd too many characters: %d\n", ret); |
7c9d733f | 397 | |
398 | printf("%-18.18s %12.2f %12.2f %12.2f %36s %10s", | |
b420058d | 399 | name, |
400 | (float)stats->cpu_use_nanos / 1000000000, | |
401 | (float)stats->cpu_use_sys / USER_HZ, | |
402 | (float)stats->cpu_use_user / USER_HZ, | |
7c9d733f | 403 | iosb_str, |
b420058d | 404 | mem_used_str); |
6b6e702a NA |
405 | |
406 | if (total->memsw_used > 0) { | |
407 | size_humanize(stats->memsw_used, memsw_used_str, sizeof(memsw_used_str)); | |
408 | printf(" %10s", memsw_used_str); | |
409 | } | |
b420058d | 410 | if (total->kmem_used > 0) { |
411 | size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str)); | |
412 | printf(" %10s", kmem_used_str); | |
413 | } | |
414 | } else { | |
adc09141 | 415 | (void)gettimeofday(&time_val, NULL); |
b420058d | 416 | time_ms = (unsigned long long) (time_val.tv_sec) * 1000 + (unsigned long long) (time_val.tv_usec) / 1000; |
9dba726e | 417 | printf("%" PRIu64 ",%s,%" PRIu64 ",%" PRIu64 ",%" PRIu64 |
1b67456c | 418 | ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64, |
9dba726e CB |
419 | (uint64_t)time_ms, name, (uint64_t)stats->cpu_use_nanos, |
420 | (uint64_t)stats->cpu_use_sys, | |
7c9d733f | 421 | (uint64_t)stats->cpu_use_user, (uint64_t)stats->io_service_bytes.total, |
422 | (uint64_t)stats->io_serviced.total, (uint64_t)stats->mem_used, | |
1b67456c | 423 | (uint64_t)stats->memsw_used, (uint64_t)stats->kmem_used); |
7dc6f6e2 | 424 | } |
9dba726e | 425 | |
7dc6f6e2 DE |
426 | } |
427 | ||
428 | static int cmp_name(const void *sct1, const void *sct2) | |
429 | { | |
430 | const struct ct *ct1 = sct1; | |
431 | const struct ct *ct2 = sct2; | |
432 | ||
433 | if (sort_reverse) | |
434 | return strcmp(ct2->c->name, ct1->c->name); | |
435 | return strcmp(ct1->c->name, ct2->c->name); | |
436 | } | |
437 | ||
438 | static int cmp_cpuuse(const void *sct1, const void *sct2) | |
439 | { | |
440 | const struct ct *ct1 = sct1; | |
441 | const struct ct *ct2 = sct2; | |
442 | ||
443 | if (sort_reverse) | |
444 | return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos; | |
445 | return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos; | |
446 | } | |
447 | ||
448 | static int cmp_blkio(const void *sct1, const void *sct2) | |
449 | { | |
450 | const struct ct *ct1 = sct1; | |
451 | const struct ct *ct2 = sct2; | |
452 | ||
453 | if (sort_reverse) | |
7c9d733f | 454 | return ct2->stats->io_service_bytes.total < ct1->stats->io_service_bytes.total; |
455 | return ct1->stats->io_service_bytes.total < ct2->stats->io_service_bytes.total; | |
7dc6f6e2 DE |
456 | } |
457 | ||
458 | static int cmp_memory(const void *sct1, const void *sct2) | |
459 | { | |
460 | const struct ct *ct1 = sct1; | |
461 | const struct ct *ct2 = sct2; | |
462 | ||
463 | if (sort_reverse) | |
464 | return ct2->stats->mem_used < ct1->stats->mem_used; | |
465 | return ct1->stats->mem_used < ct2->stats->mem_used; | |
466 | } | |
467 | ||
6b6e702a NA |
468 | static int cmp_memorysw(const void *sct1, const void *sct2) |
469 | { | |
470 | const struct ct *ct1 = sct1; | |
471 | const struct ct *ct2 = sct2; | |
472 | ||
473 | if (sort_reverse) | |
474 | return ct2->stats->memsw_used < ct1->stats->memsw_used; | |
475 | return ct1->stats->memsw_used < ct2->stats->memsw_used; | |
476 | } | |
477 | ||
7dc6f6e2 DE |
478 | static int cmp_kmemory(const void *sct1, const void *sct2) |
479 | { | |
480 | const struct ct *ct1 = sct1; | |
481 | const struct ct *ct2 = sct2; | |
482 | ||
483 | if (sort_reverse) | |
484 | return ct2->stats->kmem_used < ct1->stats->kmem_used; | |
485 | return ct1->stats->kmem_used < ct2->stats->kmem_used; | |
486 | } | |
487 | ||
488 | static void ct_sort(int active) | |
489 | { | |
490 | int (*cmp_func)(const void *, const void *); | |
491 | ||
492 | switch(sort_by) { | |
493 | default: | |
494 | case 'n': cmp_func = cmp_name; break; | |
495 | case 'c': cmp_func = cmp_cpuuse; break; | |
496 | case 'b': cmp_func = cmp_blkio; break; | |
497 | case 'm': cmp_func = cmp_memory; break; | |
6b6e702a | 498 | case 's': cmp_func = cmp_memorysw; break; |
7dc6f6e2 DE |
499 | case 'k': cmp_func = cmp_kmemory; break; |
500 | } | |
501 | qsort(ct, active, sizeof(*ct), (int (*)(const void *,const void *))cmp_func); | |
502 | } | |
503 | ||
504 | static void ct_free(void) | |
505 | { | |
506 | int i; | |
507 | ||
508 | for (i = 0; i < ct_alloc_cnt; i++) { | |
509 | if (ct[i].c) { | |
510 | lxc_container_put(ct[i].c); | |
511 | ct[i].c = NULL; | |
512 | } | |
f10fad2f ME |
513 | free(ct[i].stats); |
514 | ct[i].stats = NULL; | |
7dc6f6e2 DE |
515 | } |
516 | } | |
517 | ||
518 | static void ct_realloc(int active_cnt) | |
519 | { | |
520 | int i; | |
521 | ||
522 | if (active_cnt > ct_alloc_cnt) { | |
523 | ct_free(); | |
524 | ct = realloc(ct, sizeof(*ct) * active_cnt); | |
525 | if (!ct) { | |
8052112c | 526 | fprintf(stderr, "cannot alloc mem\n"); |
7dc6f6e2 DE |
527 | exit(EXIT_FAILURE); |
528 | } | |
529 | for (i = 0; i < active_cnt; i++) { | |
530 | ct[i].stats = malloc(sizeof(*ct[0].stats)); | |
531 | if (!ct[i].stats) { | |
8052112c | 532 | fprintf(stderr, "cannot alloc mem\n"); |
7dc6f6e2 DE |
533 | exit(EXIT_FAILURE); |
534 | } | |
535 | } | |
536 | ct_alloc_cnt = active_cnt; | |
537 | } | |
538 | } | |
539 | ||
adc09141 CB |
540 | #define LXC_MAINLOOP_CONTINUE 0 |
541 | #define LXC_MAINLOOP_CLOSE 1 | |
542 | ||
543 | struct lxc_epoll_descr { | |
544 | int epfd; | |
545 | struct lxc_list handlers; | |
546 | }; | |
547 | ||
548 | typedef int (*lxc_mainloop_callback_t)(int fd, uint32_t event, void *data, | |
549 | struct lxc_epoll_descr *descr); | |
550 | ||
551 | struct mainloop_handler { | |
552 | lxc_mainloop_callback_t callback; | |
553 | int fd; | |
554 | void *data; | |
555 | }; | |
556 | ||
557 | #define MAX_EVENTS 10 | |
558 | ||
559 | int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms) | |
560 | { | |
561 | int i, nfds, ret; | |
562 | struct mainloop_handler *handler; | |
563 | struct epoll_event events[MAX_EVENTS]; | |
564 | ||
565 | for (;;) { | |
566 | nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, timeout_ms); | |
567 | if (nfds < 0) { | |
568 | if (errno == EINTR) | |
569 | continue; | |
570 | ||
571 | return -1; | |
572 | } | |
573 | ||
574 | for (i = 0; i < nfds; i++) { | |
575 | handler = events[i].data.ptr; | |
576 | ||
577 | /* If the handler returns a positive value, exit the | |
578 | * mainloop. | |
579 | */ | |
580 | ret = handler->callback(handler->fd, events[i].events, | |
581 | handler->data, descr); | |
582 | if (ret == LXC_MAINLOOP_CLOSE) | |
583 | return 0; | |
584 | } | |
585 | ||
586 | if (nfds == 0) | |
587 | return 0; | |
588 | ||
589 | if (lxc_list_empty(&descr->handlers)) | |
590 | return 0; | |
591 | } | |
592 | } | |
593 | ||
594 | int lxc_mainloop_open(struct lxc_epoll_descr *descr) | |
595 | { | |
596 | /* hint value passed to epoll create */ | |
597 | descr->epfd = epoll_create1(EPOLL_CLOEXEC); | |
598 | if (descr->epfd < 0) | |
599 | return -1; | |
600 | ||
601 | lxc_list_init(&descr->handlers); | |
602 | return 0; | |
603 | } | |
604 | ||
605 | int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd, | |
606 | lxc_mainloop_callback_t callback, void *data) | |
607 | { | |
608 | struct epoll_event ev; | |
609 | struct mainloop_handler *handler; | |
610 | struct lxc_list *item; | |
611 | ||
612 | handler = malloc(sizeof(*handler)); | |
613 | if (!handler) | |
614 | return -1; | |
615 | ||
616 | handler->callback = callback; | |
617 | handler->fd = fd; | |
618 | handler->data = data; | |
619 | ||
620 | ev.events = EPOLLIN; | |
621 | ev.data.ptr = handler; | |
622 | ||
623 | if (epoll_ctl(descr->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) | |
624 | goto out_free_handler; | |
625 | ||
626 | item = malloc(sizeof(*item)); | |
627 | if (!item) | |
628 | goto out_free_handler; | |
629 | ||
630 | item->elem = handler; | |
631 | lxc_list_add(&descr->handlers, item); | |
632 | return 0; | |
633 | ||
634 | out_free_handler: | |
635 | free(handler); | |
636 | return -1; | |
637 | } | |
638 | ||
639 | int lxc_mainloop_close(struct lxc_epoll_descr *descr) | |
640 | { | |
641 | struct lxc_list *iterator, *next; | |
642 | ||
643 | iterator = descr->handlers.next; | |
644 | while (iterator != &descr->handlers) { | |
645 | next = iterator->next; | |
646 | ||
647 | lxc_list_del(iterator); | |
648 | free(iterator->elem); | |
649 | free(iterator); | |
650 | iterator = next; | |
651 | } | |
652 | ||
653 | if (descr->epfd >= 0) | |
654 | return close(descr->epfd); | |
655 | ||
656 | return 0; | |
657 | } | |
658 | ||
659 | static int stdin_handler(int fd, uint32_t events, void *data, | |
660 | struct lxc_epoll_descr *descr) | |
661 | { | |
662 | char *in_char = data; | |
663 | ||
664 | if (events & EPOLLIN) { | |
665 | int rc; | |
666 | ||
667 | rc = read(fd, in_char, sizeof(*in_char)); | |
668 | if (rc <= 0) | |
669 | *in_char = '\0'; | |
670 | } | |
671 | ||
672 | if (events & EPOLLHUP) | |
673 | *in_char = 'q'; | |
b2a48508 | 674 | return LXC_MAINLOOP_CLOSE; |
adc09141 CB |
675 | } |
676 | ||
7dc6f6e2 DE |
677 | int main(int argc, char *argv[]) |
678 | { | |
679 | struct lxc_epoll_descr descr; | |
680 | int ret, ct_print_cnt; | |
681 | char in_char; | |
9dba726e | 682 | |
7dc6f6e2 DE |
683 | ret = EXIT_FAILURE; |
684 | if (lxc_arguments_parse(&my_args, argc, argv)) | |
685 | goto out; | |
686 | ||
687 | ct_print_cnt = stdin_tios_rows() - 3; /* 3 -> header and total */ | |
688 | if (stdin_tios_setup() < 0) { | |
8052112c | 689 | fprintf(stderr, "failed to setup terminal\n"); |
7dc6f6e2 DE |
690 | goto out; |
691 | } | |
692 | ||
693 | /* ensure the terminal gets restored */ | |
694 | atexit(stdin_tios_restore); | |
695 | signal(SIGINT, sig_handler); | |
696 | signal(SIGQUIT, sig_handler); | |
697 | ||
698 | if (lxc_mainloop_open(&descr)) { | |
8052112c | 699 | fprintf(stderr, "failed to create mainloop\n"); |
7dc6f6e2 DE |
700 | goto out; |
701 | } | |
702 | ||
703 | ret = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &in_char); | |
704 | if (ret) { | |
8052112c | 705 | fprintf(stderr, "failed to add stdin handler\n"); |
7dc6f6e2 DE |
706 | ret = EXIT_FAILURE; |
707 | goto err1; | |
708 | } | |
709 | ||
b420058d | 710 | if (batch && !delay_set) { |
711 | delay = 300; | |
712 | } | |
713 | if (batch) { | |
6b6e702a | 714 | printf("time_ms,container,cpu_nanos,cpu_sys_userhz,cpu_user_userhz,blkio_bytes,blkio_iops,mem_used_bytes,memsw_used_bytes,kernel_mem_used_bytes\n"); |
b420058d | 715 | } |
9dba726e | 716 | |
7dc6f6e2 DE |
717 | for(;;) { |
718 | struct lxc_container **active; | |
719 | int i, active_cnt; | |
720 | struct stats total; | |
721 | char total_name[30]; | |
722 | ||
723 | active_cnt = list_active_containers(my_args.lxcpath[0], NULL, &active); | |
724 | ct_realloc(active_cnt); | |
725 | ||
726 | memset(&total, 0, sizeof(total)); | |
727 | for (i = 0; i < active_cnt; i++) | |
728 | stats_get(active[i], &ct[i], &total); | |
729 | ||
730 | ct_sort(active_cnt); | |
731 | ||
b420058d | 732 | if (!batch) { |
733 | printf(TERMCLEAR); | |
734 | stats_print_header(&total); | |
735 | } | |
7dc6f6e2 DE |
736 | for (i = 0; i < active_cnt && i < ct_print_cnt; i++) { |
737 | stats_print(ct[i].c->name, ct[i].stats, &total); | |
738 | printf("\n"); | |
739 | } | |
b420058d | 740 | if (!batch) { |
741 | sprintf(total_name, "TOTAL %d of %d", i, active_cnt); | |
742 | stats_print(total_name, &total, &total); | |
743 | } | |
7dc6f6e2 | 744 | fflush(stdout); |
9dba726e | 745 | |
7dc6f6e2 DE |
746 | for (i = 0; i < active_cnt; i++) { |
747 | lxc_container_put(ct[i].c); | |
748 | ct[i].c = NULL; | |
749 | } | |
9dba726e | 750 | |
7dc6f6e2 | 751 | in_char = '\0'; |
b420058d | 752 | if (!batch) { |
753 | ret = lxc_mainloop(&descr, 1000 * delay); | |
754 | if (ret != 0 || in_char == 'q') | |
755 | break; | |
756 | switch(in_char) { | |
757 | case 'r': | |
7dc6f6e2 | 758 | sort_reverse ^= 1; |
b420058d | 759 | break; |
760 | case 'n': | |
761 | case 'c': | |
762 | case 'b': | |
763 | case 'm': | |
6b6e702a | 764 | case 's': |
b420058d | 765 | case 'k': |
766 | if (sort_by == in_char) | |
767 | sort_reverse ^= 1; | |
768 | else | |
769 | sort_reverse = 0; | |
770 | sort_by = in_char; | |
771 | } | |
772 | } else { | |
773 | sleep(delay); | |
7dc6f6e2 DE |
774 | } |
775 | } | |
776 | ret = EXIT_SUCCESS; | |
9dba726e | 777 | |
7dc6f6e2 DE |
778 | err1: |
779 | lxc_mainloop_close(&descr); | |
780 | out: | |
9fd8b8a7 | 781 | exit(ret); |
7dc6f6e2 | 782 | } |