]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cgtop/cgtop.c
set: return NULL on destructors
[thirdparty/systemd.git] / src / cgtop / cgtop.c
CommitLineData
8f2d43a0
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
8f2d43a0
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
8f2d43a0 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8f2d43a0
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <string.h>
24#include <stdlib.h>
1e913bcb 25#include <stdint.h>
8f2d43a0
LP
26#include <unistd.h>
27#include <alloca.h>
28#include <getopt.h>
97b845b0 29#include <signal.h>
8f2d43a0 30
9eb977db 31#include "path-util.h"
288a74cc 32#include "terminal-util.h"
41ba8b6e 33#include "process-util.h"
8f2d43a0
LP
34#include "util.h"
35#include "hashmap.h"
36#include "cgroup-util.h"
0d7e32fa 37#include "build.h"
a5c32cff 38#include "fileio.h"
8f2d43a0
LP
39
40typedef struct Group {
41 char *path;
42
43 bool n_tasks_valid:1;
44 bool cpu_valid:1;
45 bool memory_valid:1;
46 bool io_valid:1;
47
48 unsigned n_tasks;
49
50 unsigned cpu_iteration;
45d7a8bb
LP
51 nsec_t cpu_usage;
52 nsec_t cpu_timestamp;
8f2d43a0
LP
53 double cpu_fraction;
54
55 uint64_t memory;
56
57 unsigned io_iteration;
58 uint64_t io_input, io_output;
45d7a8bb 59 nsec_t io_timestamp;
8f2d43a0
LP
60 uint64_t io_input_bps, io_output_bps;
61} Group;
62
30edf116 63static unsigned arg_depth = 3;
45d7a8bb 64static unsigned arg_iterations = (unsigned) -1;
e66bb58b 65static bool arg_batch = false;
a2c9f631 66static bool arg_raw = false;
8f2d43a0 67static usec_t arg_delay = 1*USEC_PER_SEC;
41ba8b6e 68static bool arg_kernel_threads = false;
3cb5beea 69static bool arg_recursive = true;
8f2d43a0
LP
70
71static enum {
72 ORDER_PATH,
73 ORDER_TASKS,
74 ORDER_CPU,
75 ORDER_MEMORY,
76 ORDER_IO
77} arg_order = ORDER_CPU;
78
1e913bcb
UTL
79static enum {
80 CPU_PERCENT,
81 CPU_TIME,
82} arg_cpu_type = CPU_PERCENT;
83
8f2d43a0
LP
84static void group_free(Group *g) {
85 assert(g);
86
87 free(g->path);
88 free(g);
89}
90
91static void group_hashmap_clear(Hashmap *h) {
92 Group *g;
93
94 while ((g = hashmap_steal_first(h)))
95 group_free(g);
96}
97
98static void group_hashmap_free(Hashmap *h) {
99 group_hashmap_clear(h);
100 hashmap_free(h);
101}
102
a2c9f631
CD
103static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t t) {
104 if (!is_valid)
105 return "-";
106 if (arg_raw) {
107 snprintf(buf, l, "%jd", t);
108 return buf;
109 }
110 return format_bytes(buf, l, t);
111}
112
3cb5beea
LP
113static int process(
114 const char *controller,
115 const char *path,
116 Hashmap *a,
117 Hashmap *b,
118 unsigned iteration,
119 Group **ret) {
120
8f2d43a0
LP
121 Group *g;
122 int r;
8f2d43a0
LP
123
124 assert(controller);
125 assert(path);
126 assert(a);
127
128 g = hashmap_get(a, path);
129 if (!g) {
130 g = hashmap_get(b, path);
131 if (!g) {
132 g = new0(Group, 1);
133 if (!g)
134 return -ENOMEM;
135
136 g->path = strdup(path);
137 if (!g->path) {
138 group_free(g);
139 return -ENOMEM;
140 }
141
142 r = hashmap_put(a, g->path, g);
143 if (r < 0) {
144 group_free(g);
145 return r;
146 }
147 } else {
2d5c93c7
MS
148 r = hashmap_move_one(a, b, path);
149 if (r < 0)
150 return r;
45d7a8bb 151
8f2d43a0
LP
152 g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
153 }
154 }
155
45d7a8bb
LP
156 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
157 _cleanup_fclose_ FILE *f = NULL;
158 pid_t pid;
8f2d43a0 159
45d7a8bb
LP
160 r = cg_enumerate_processes(controller, path, &f);
161 if (r == -ENOENT)
162 return 0;
163 if (r < 0)
164 return r;
8f2d43a0 165
45d7a8bb 166 g->n_tasks = 0;
41ba8b6e
LP
167 while (cg_read_pid(f, &pid) > 0) {
168
169 if (!arg_kernel_threads && is_kernel_thread(pid) > 0)
170 continue;
171
45d7a8bb 172 g->n_tasks++;
41ba8b6e 173 }
8f2d43a0 174
45d7a8bb
LP
175 if (g->n_tasks > 0)
176 g->n_tasks_valid = true;
8f2d43a0 177
45d7a8bb
LP
178 } else if (streq(controller, "cpuacct")) {
179 _cleanup_free_ char *p = NULL, *v = NULL;
8f2d43a0 180 uint64_t new_usage;
45d7a8bb 181 nsec_t timestamp;
8f2d43a0
LP
182
183 r = cg_get_path(controller, path, "cpuacct.usage", &p);
184 if (r < 0)
185 return r;
186
187 r = read_one_line_file(p, &v);
45d7a8bb
LP
188 if (r == -ENOENT)
189 return 0;
8f2d43a0
LP
190 if (r < 0)
191 return r;
192
193 r = safe_atou64(v, &new_usage);
8f2d43a0
LP
194 if (r < 0)
195 return r;
196
45d7a8bb 197 timestamp = now_nsec(CLOCK_MONOTONIC);
8f2d43a0 198
45d7a8bb
LP
199 if (g->cpu_iteration == iteration - 1 &&
200 (nsec_t) new_usage > g->cpu_usage) {
8f2d43a0 201
45d7a8bb 202 nsec_t x, y;
8f2d43a0 203
45d7a8bb
LP
204 x = timestamp - g->cpu_timestamp;
205 if (x < 1)
206 x = 1;
8f2d43a0 207
45d7a8bb
LP
208 y = (nsec_t) new_usage - g->cpu_usage;
209 g->cpu_fraction = (double) y / (double) x;
210 g->cpu_valid = true;
8f2d43a0
LP
211 }
212
45d7a8bb
LP
213 g->cpu_usage = (nsec_t) new_usage;
214 g->cpu_timestamp = timestamp;
8f2d43a0
LP
215 g->cpu_iteration = iteration;
216
217 } else if (streq(controller, "memory")) {
45d7a8bb 218 _cleanup_free_ char *p = NULL, *v = NULL;
8f2d43a0
LP
219
220 r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
221 if (r < 0)
222 return r;
223
224 r = read_one_line_file(p, &v);
45d7a8bb
LP
225 if (r == -ENOENT)
226 return 0;
8f2d43a0
LP
227 if (r < 0)
228 return r;
229
230 r = safe_atou64(v, &g->memory);
8f2d43a0
LP
231 if (r < 0)
232 return r;
233
234 if (g->memory > 0)
235 g->memory_valid = true;
236
237 } else if (streq(controller, "blkio")) {
45d7a8bb
LP
238 _cleanup_fclose_ FILE *f = NULL;
239 _cleanup_free_ char *p = NULL;
8f2d43a0 240 uint64_t wr = 0, rd = 0;
45d7a8bb 241 nsec_t timestamp;
8f2d43a0
LP
242
243 r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
244 if (r < 0)
245 return r;
246
247 f = fopen(p, "re");
45d7a8bb
LP
248 if (!f) {
249 if (errno == ENOENT)
250 return 0;
8f2d43a0 251 return -errno;
45d7a8bb 252 }
8f2d43a0
LP
253
254 for (;;) {
255 char line[LINE_MAX], *l;
256 uint64_t k, *q;
257
258 if (!fgets(line, sizeof(line), f))
259 break;
260
261 l = strstrip(line);
262 l += strcspn(l, WHITESPACE);
263 l += strspn(l, WHITESPACE);
264
265 if (first_word(l, "Read")) {
266 l += 4;
267 q = &rd;
268 } else if (first_word(l, "Write")) {
269 l += 5;
270 q = &wr;
271 } else
272 continue;
273
274 l += strspn(l, WHITESPACE);
275 r = safe_atou64(l, &k);
276 if (r < 0)
277 continue;
278
279 *q += k;
280 }
281
45d7a8bb 282 timestamp = now_nsec(CLOCK_MONOTONIC);
8f2d43a0
LP
283
284 if (g->io_iteration == iteration - 1) {
285 uint64_t x, yr, yw;
286
45d7a8bb
LP
287 x = (uint64_t) (timestamp - g->io_timestamp);
288 if (x < 1)
289 x = 1;
8f2d43a0 290
45d7a8bb
LP
291 if (rd > g->io_input)
292 yr = rd - g->io_input;
293 else
294 yr = 0;
295
296 if (wr > g->io_output)
297 yw = wr - g->io_output;
298 else
299 yw = 0;
8f2d43a0 300
45d7a8bb 301 if (yr > 0 || yw > 0) {
8f2d43a0
LP
302 g->io_input_bps = (yr * 1000000000ULL) / x;
303 g->io_output_bps = (yw * 1000000000ULL) / x;
304 g->io_valid = true;
8f2d43a0
LP
305 }
306 }
307
308 g->io_input = rd;
309 g->io_output = wr;
45d7a8bb 310 g->io_timestamp = timestamp;
8f2d43a0
LP
311 g->io_iteration = iteration;
312 }
313
3cb5beea
LP
314 if (ret)
315 *ret = g;
316
8f2d43a0
LP
317 return 0;
318}
319
320static int refresh_one(
321 const char *controller,
322 const char *path,
323 Hashmap *a,
324 Hashmap *b,
325 unsigned iteration,
3cb5beea
LP
326 unsigned depth,
327 Group **ret) {
8f2d43a0 328
45d7a8bb 329 _cleanup_closedir_ DIR *d = NULL;
3cb5beea 330 Group *ours;
8f2d43a0
LP
331 int r;
332
333 assert(controller);
334 assert(path);
335 assert(a);
336
337 if (depth > arg_depth)
338 return 0;
339
3cb5beea 340 r = process(controller, path, a, b, iteration, &ours);
8f2d43a0
LP
341 if (r < 0)
342 return r;
343
344 r = cg_enumerate_subgroups(controller, path, &d);
45d7a8bb
LP
345 if (r == -ENOENT)
346 return 0;
347 if (r < 0)
8f2d43a0 348 return r;
8f2d43a0
LP
349
350 for (;;) {
45d7a8bb 351 _cleanup_free_ char *fn = NULL, *p = NULL;
3cb5beea 352 Group *child = NULL;
8f2d43a0
LP
353
354 r = cg_read_subgroup(d, &fn);
3cb5beea 355 if (r < 0)
45d7a8bb 356 return r;
3cb5beea
LP
357 if (r == 0)
358 break;
8f2d43a0 359
b7def684 360 p = strjoin(path, "/", fn, NULL);
45d7a8bb
LP
361 if (!p)
362 return -ENOMEM;
8f2d43a0
LP
363
364 path_kill_slashes(p);
365
3cb5beea 366 r = refresh_one(controller, p, a, b, iteration, depth + 1, &child);
8f2d43a0 367 if (r < 0)
45d7a8bb 368 return r;
3cb5beea
LP
369
370 if (arg_recursive &&
371 child &&
372 child->n_tasks_valid &&
373 streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
374
375 /* Recursively sum up processes */
376
377 if (ours->n_tasks_valid)
378 ours->n_tasks += child->n_tasks;
379 else {
380 ours->n_tasks = child->n_tasks;
381 ours->n_tasks_valid = true;
382 }
383 }
8f2d43a0
LP
384 }
385
3cb5beea
LP
386 if (ret)
387 *ret = ours;
388
389 return 1;
8f2d43a0
LP
390}
391
03af6492 392static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) {
8f2d43a0
LP
393 int r;
394
395 assert(a);
396
3cb5beea 397 r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL);
8f2d43a0 398 if (r < 0)
45d7a8bb 399 return r;
3cb5beea 400 r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL);
8f2d43a0 401 if (r < 0)
45d7a8bb 402 return r;
3cb5beea 403 r = refresh_one("memory", root, a, b, iteration, 0, NULL);
8f2d43a0 404 if (r < 0)
45d7a8bb 405 return r;
3cb5beea 406 r = refresh_one("blkio", root, a, b, iteration, 0, NULL);
63210a15 407 if (r < 0)
45d7a8bb
LP
408 return r;
409
63210a15 410 return 0;
8f2d43a0
LP
411}
412
413static int group_compare(const void*a, const void *b) {
414 const Group *x = *(Group**)a, *y = *(Group**)b;
415
3cb5beea 416 if (arg_order != ORDER_TASKS || arg_recursive) {
45d7a8bb 417 /* Let's make sure that the parent is always before
3cb5beea
LP
418 * the child. Except when ordering by tasks and
419 * recursive summing is off, since that is actually
420 * not accumulative for all children. */
45d7a8bb
LP
421
422 if (path_startswith(y->path, x->path))
423 return -1;
424 if (path_startswith(x->path, y->path))
425 return 1;
426 }
427
428 switch (arg_order) {
429
430 case ORDER_PATH:
431 break;
8f2d43a0 432
45d7a8bb 433 case ORDER_CPU:
1e913bcb
UTL
434 if (arg_cpu_type == CPU_PERCENT) {
435 if (x->cpu_valid && y->cpu_valid) {
436 if (x->cpu_fraction > y->cpu_fraction)
437 return -1;
438 else if (x->cpu_fraction < y->cpu_fraction)
439 return 1;
440 } else if (x->cpu_valid)
8f2d43a0 441 return -1;
1e913bcb 442 else if (y->cpu_valid)
8f2d43a0 443 return 1;
1e913bcb
UTL
444 } else {
445 if (x->cpu_usage > y->cpu_usage)
446 return -1;
447 else if (x->cpu_usage < y->cpu_usage)
448 return 1;
449 }
8f2d43a0 450
45d7a8bb 451 break;
8f2d43a0 452
45d7a8bb 453 case ORDER_TASKS:
8f2d43a0
LP
454 if (x->n_tasks_valid && y->n_tasks_valid) {
455 if (x->n_tasks > y->n_tasks)
456 return -1;
457 else if (x->n_tasks < y->n_tasks)
458 return 1;
459 } else if (x->n_tasks_valid)
460 return -1;
461 else if (y->n_tasks_valid)
462 return 1;
8f2d43a0 463
45d7a8bb
LP
464 break;
465
466 case ORDER_MEMORY:
8f2d43a0
LP
467 if (x->memory_valid && y->memory_valid) {
468 if (x->memory > y->memory)
469 return -1;
470 else if (x->memory < y->memory)
471 return 1;
472 } else if (x->memory_valid)
473 return -1;
474 else if (y->memory_valid)
475 return 1;
8f2d43a0 476
45d7a8bb
LP
477 break;
478
479 case ORDER_IO:
8f2d43a0
LP
480 if (x->io_valid && y->io_valid) {
481 if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
482 return -1;
483 else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
484 return 1;
485 } else if (x->io_valid)
486 return -1;
487 else if (y->io_valid)
488 return 1;
489 }
490
45d7a8bb 491 return path_compare(x->path, y->path);
8f2d43a0
LP
492}
493
1e913bcb
UTL
494#define ON ANSI_HIGHLIGHT_ON
495#define OFF ANSI_HIGHLIGHT_OFF
496
dcd71990 497static void display(Hashmap *a) {
8f2d43a0
LP
498 Iterator i;
499 Group *g;
500 Group **array;
1e913bcb 501 signed path_columns;
510c4a0f 502 unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 3; /* 3 for ellipsize() to work properly */
62b95b8b 503 char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
8f2d43a0
LP
504
505 assert(a);
506
507 /* Set cursor to top left corner and clear screen */
1e913bcb
UTL
508 if (on_tty())
509 fputs("\033[H"
510 "\033[2J", stdout);
8f2d43a0
LP
511
512 array = alloca(sizeof(Group*) * hashmap_size(a));
513
514 HASHMAP_FOREACH(g, a, i)
515 if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
516 array[n++] = g;
517
7ff7394d 518 qsort_safe(array, n, sizeof(Group*), group_compare);
8f2d43a0 519
1e913bcb
UTL
520 /* Find the longest names in one run */
521 for (j = 0; j < n; j++) {
522 unsigned cputlen, pathtlen;
62b95b8b 523
45d7a8bb 524 format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
62b95b8b 525 cputlen = strlen(buffer);
1e913bcb 526 maxtcpu = MAX(maxtcpu, cputlen);
45d7a8bb 527
1e913bcb
UTL
528 pathtlen = strlen(array[j]->path);
529 maxtpath = MAX(maxtpath, pathtlen);
530 }
531
532 if (arg_cpu_type == CPU_PERCENT)
62b95b8b 533 snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
1e913bcb 534 else
62b95b8b 535 snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
1e913bcb 536
ed757c0c
LP
537 rows = lines();
538 if (rows <= 10)
539 rows = 10;
8f2d43a0 540
1e913bcb 541 if (on_tty()) {
62b95b8b 542 path_columns = columns() - 36 - strlen(buffer);
1e913bcb
UTL
543 if (path_columns < 10)
544 path_columns = 10;
545
546 printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
45d7a8bb 547 arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group",
1e913bcb
UTL
548 arg_order == ORDER_PATH ? OFF : "",
549 arg_order == ORDER_TASKS ? ON : "", "Tasks",
550 arg_order == ORDER_TASKS ? OFF : "",
62b95b8b 551 arg_order == ORDER_CPU ? ON : "", buffer,
1e913bcb
UTL
552 arg_order == ORDER_CPU ? OFF : "",
553 arg_order == ORDER_MEMORY ? ON : "", "Memory",
554 arg_order == ORDER_MEMORY ? OFF : "",
555 arg_order == ORDER_IO ? ON : "", "Input/s",
556 arg_order == ORDER_IO ? OFF : "",
557 arg_order == ORDER_IO ? ON : "", "Output/s",
558 arg_order == ORDER_IO ? OFF : "");
559 } else
560 path_columns = maxtpath;
8f2d43a0
LP
561
562 for (j = 0; j < n; j++) {
45d7a8bb 563 _cleanup_free_ char *p = NULL;
8f2d43a0 564
1e913bcb 565 if (on_tty() && j + 5 > rows)
8f2d43a0
LP
566 break;
567
568 g = array[j];
569
11f96fac 570 p = ellipsize(g->path, path_columns, 33);
45d7a8bb 571 printf("%-*s", path_columns, p ?: g->path);
8f2d43a0
LP
572
573 if (g->n_tasks_valid)
574 printf(" %7u", g->n_tasks);
575 else
576 fputs(" -", stdout);
577
62b95b8b 578 if (arg_cpu_type == CPU_PERCENT) {
1e913bcb
UTL
579 if (g->cpu_valid)
580 printf(" %6.1f", g->cpu_fraction*100);
581 else
582 fputs(" -", stdout);
62b95b8b 583 } else
45d7a8bb 584 printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
8f2d43a0 585
a2c9f631
CD
586 printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory));
587 printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps));
588 printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_output_bps));
8f2d43a0
LP
589
590 putchar('\n');
591 }
8f2d43a0
LP
592}
593
601185b4 594static void help(void) {
8f2d43a0
LP
595 printf("%s [OPTIONS...]\n\n"
596 "Show top control groups by their resource usage.\n\n"
597 " -h --help Show this help\n"
45d7a8bb
LP
598 " --version Show package version\n"
599 " -p --order=path Order by path\n"
600 " -t --order=tasks Order by number of tasks\n"
601 " -c --order=cpu Order by CPU load (default)\n"
602 " -m --order=memory Order by memory load\n"
603 " -i --order=io Order by IO load\n"
a2c9f631 604 " -r --raw Provide raw (not human-readable) numbers\n"
45d7a8bb
LP
605 " --cpu=percentage Show CPU usage as percentage (default)\n"
606 " --cpu=time Show CPU usage as time\n"
41ba8b6e 607 " -k Include kernel threads in task count\n"
3cb5beea 608 " --recursive=BOOL Sum up task count recursively\n"
1e913bcb 609 " -d --delay=DELAY Delay between updates\n"
a152771a 610 " -n --iterations=N Run for N iterations before exiting\n"
e66bb58b 611 " -b --batch Run in batch mode, accepting no input\n"
601185b4
ZJS
612 " --depth=DEPTH Maximum traversal depth (default: %u)\n"
613 , program_invocation_short_name, arg_depth);
0d7e32fa
ZJS
614}
615
8f2d43a0
LP
616static int parse_argv(int argc, char *argv[]) {
617
618 enum {
0d7e32fa
ZJS
619 ARG_VERSION = 0x100,
620 ARG_DEPTH,
45d7a8bb
LP
621 ARG_CPU_TYPE,
622 ARG_ORDER,
3cb5beea 623 ARG_RECURSIVE,
8f2d43a0
LP
624 };
625
626 static const struct option options[] = {
3cb5beea
LP
627 { "help", no_argument, NULL, 'h' },
628 { "version", no_argument, NULL, ARG_VERSION },
629 { "delay", required_argument, NULL, 'd' },
630 { "iterations", required_argument, NULL, 'n' },
631 { "batch", no_argument, NULL, 'b' },
632 { "raw", no_argument, NULL, 'r' },
633 { "depth", required_argument, NULL, ARG_DEPTH },
634 { "cpu", optional_argument, NULL, ARG_CPU_TYPE },
635 { "order", required_argument, NULL, ARG_ORDER },
636 { "recursive", required_argument, NULL, ARG_RECURSIVE },
eb9da376 637 {}
8f2d43a0
LP
638 };
639
3cb5beea 640 int c, r;
8f2d43a0
LP
641
642 assert(argc >= 1);
643 assert(argv);
644
41ba8b6e 645 while ((c = getopt_long(argc, argv, "hptcmin:brd:k", options, NULL)) >= 0)
8f2d43a0
LP
646
647 switch (c) {
648
649 case 'h':
601185b4
ZJS
650 help();
651 return 0;
8f2d43a0 652
0d7e32fa 653 case ARG_VERSION:
eb9da376
LP
654 puts(PACKAGE_STRING);
655 puts(SYSTEMD_FEATURES);
0d7e32fa
ZJS
656 return 0;
657
1e913bcb
UTL
658 case ARG_CPU_TYPE:
659 if (optarg) {
45d7a8bb 660 if (streq(optarg, "time"))
1e913bcb 661 arg_cpu_type = CPU_TIME;
45d7a8bb 662 else if (streq(optarg, "percentage"))
1e913bcb 663 arg_cpu_type = CPU_PERCENT;
45d7a8bb
LP
664 else {
665 log_error("Unknown argument to --cpu=: %s", optarg);
1e913bcb 666 return -EINVAL;
45d7a8bb
LP
667 }
668 } else
669 arg_cpu_type = CPU_TIME;
670
1e913bcb
UTL
671 break;
672
8f2d43a0
LP
673 case ARG_DEPTH:
674 r = safe_atou(optarg, &arg_depth);
675 if (r < 0) {
676 log_error("Failed to parse depth parameter.");
677 return -EINVAL;
678 }
679
680 break;
681
682 case 'd':
7f602784 683 r = parse_sec(optarg, &arg_delay);
8f2d43a0
LP
684 if (r < 0 || arg_delay <= 0) {
685 log_error("Failed to parse delay parameter.");
686 return -EINVAL;
687 }
688
689 break;
690
a152771a
DS
691 case 'n':
692 r = safe_atou(optarg, &arg_iterations);
693 if (r < 0) {
694 log_error("Failed to parse iterations parameter.");
695 return -EINVAL;
696 }
697
698 break;
699
e66bb58b
DS
700 case 'b':
701 arg_batch = true;
702 break;
703
a2c9f631
CD
704 case 'r':
705 arg_raw = true;
706 break;
707
8f2d43a0
LP
708 case 'p':
709 arg_order = ORDER_PATH;
710 break;
711
712 case 't':
713 arg_order = ORDER_TASKS;
714 break;
715
716 case 'c':
717 arg_order = ORDER_CPU;
718 break;
719
720 case 'm':
721 arg_order = ORDER_MEMORY;
722 break;
723
724 case 'i':
725 arg_order = ORDER_IO;
726 break;
727
45d7a8bb
LP
728 case ARG_ORDER:
729 if (streq(optarg, "path"))
730 arg_order = ORDER_PATH;
731 else if (streq(optarg, "tasks"))
732 arg_order = ORDER_TASKS;
733 else if (streq(optarg, "cpu"))
734 arg_order = ORDER_CPU;
735 else if (streq(optarg, "memory"))
736 arg_order = ORDER_MEMORY;
737 else if (streq(optarg, "io"))
738 arg_order = ORDER_IO;
739 else {
740 log_error("Invalid argument to --order=: %s", optarg);
741 return -EINVAL;
742 }
743 break;
744
41ba8b6e
LP
745 case 'k':
746 arg_kernel_threads = true;
747 break;
748
3cb5beea
LP
749 case ARG_RECURSIVE:
750 r = parse_boolean(optarg);
751 if (r < 0) {
752 log_error("Failed to parse --recursive= argument: %s", optarg);
753 return r;
754 }
755
756 arg_recursive = r;
757 break;
758
8f2d43a0
LP
759 case '?':
760 return -EINVAL;
761
762 default:
eb9da376 763 assert_not_reached("Unhandled option");
8f2d43a0 764 }
8f2d43a0
LP
765
766 if (optind < argc) {
767 log_error("Too many arguments.");
768 return -EINVAL;
769 }
770
771 return 1;
772}
773
774int main(int argc, char *argv[]) {
775 int r;
776 Hashmap *a = NULL, *b = NULL;
777 unsigned iteration = 0;
778 usec_t last_refresh = 0;
779 bool quit = false, immediate_refresh = false;
03af6492 780 _cleanup_free_ char *root = NULL;
8f2d43a0
LP
781
782 log_parse_environment();
783 log_open();
784
785 r = parse_argv(argc, argv);
786 if (r <= 0)
787 goto finish;
788
03af6492
LP
789 r = cg_get_root_path(&root);
790 if (r < 0) {
791 log_error_errno(r, "Failed to get root control group path: %m");
792 goto finish;
793 }
794
d5099efc
MS
795 a = hashmap_new(&string_hash_ops);
796 b = hashmap_new(&string_hash_ops);
8f2d43a0 797 if (!a || !b) {
0d0f0c50 798 r = log_oom();
8f2d43a0
LP
799 goto finish;
800 }
801
ed757c0c 802 signal(SIGWINCH, columns_lines_cache_reset);
28917d7d 803
45d7a8bb 804 if (arg_iterations == (unsigned) -1)
780fe62e 805 arg_iterations = on_tty() ? 0 : 1;
1e913bcb 806
8f2d43a0
LP
807 while (!quit) {
808 Hashmap *c;
809 usec_t t;
810 char key;
811 char h[FORMAT_TIMESPAN_MAX];
812
813 t = now(CLOCK_MONOTONIC);
814
815 if (t >= last_refresh + arg_delay || immediate_refresh) {
816
03af6492 817 r = refresh(root, a, b, iteration++);
dcd71990
LP
818 if (r < 0) {
819 log_error_errno(r, "Failed to refresh: %m");
8f2d43a0 820 goto finish;
dcd71990 821 }
8f2d43a0
LP
822
823 group_hashmap_clear(b);
824
825 c = a;
826 a = b;
827 b = c;
828
829 last_refresh = t;
830 immediate_refresh = false;
831 }
832
dcd71990 833 display(b);
8f2d43a0 834
a152771a
DS
835 if (arg_iterations && iteration >= arg_iterations)
836 break;
837
dcc7aacd
CD
838 if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */
839 fputs("\n", stdout);
840 fflush(stdout);
841
45d7a8bb 842 if (arg_batch)
dcd71990 843 (void) usleep(last_refresh + arg_delay - t);
45d7a8bb
LP
844 else {
845 r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
e66bb58b
DS
846 if (r == -ETIMEDOUT)
847 continue;
848 if (r < 0) {
da927ba9 849 log_error_errno(r, "Couldn't read key: %m");
e66bb58b
DS
850 goto finish;
851 }
8f2d43a0
LP
852 }
853
dcc7aacd
CD
854 if (on_tty()) { /* TTY: Clear any user keystroke */
855 fputs("\r \r", stdout);
856 fflush(stdout);
857 }
8f2d43a0 858
e66bb58b
DS
859 if (arg_batch)
860 continue;
861
8f2d43a0
LP
862 switch (key) {
863
864 case ' ':
865 immediate_refresh = true;
866 break;
867
868 case 'q':
869 quit = true;
870 break;
871
872 case 'p':
873 arg_order = ORDER_PATH;
874 break;
875
876 case 't':
877 arg_order = ORDER_TASKS;
878 break;
879
880 case 'c':
881 arg_order = ORDER_CPU;
882 break;
883
884 case 'm':
885 arg_order = ORDER_MEMORY;
886 break;
887
888 case 'i':
889 arg_order = ORDER_IO;
890 break;
891
2bfc1eda
ZJS
892 case '%':
893 arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
894 break;
895
7fcfb7ee
LP
896 case 'k':
897 arg_kernel_threads = !arg_kernel_threads;
898 fprintf(stdout, "\nCounting kernel threads: %s.", yes_no(arg_kernel_threads));
899 fflush(stdout);
900 sleep(1);
901 break;
902
903 case 'r':
904 arg_recursive = !arg_recursive;
905 fprintf(stdout, "\nRecursive task counting: %s", yes_no(arg_recursive));
906 fflush(stdout);
907 sleep(1);
908 break;
909
8f2d43a0
LP
910 case '+':
911 if (arg_delay < USEC_PER_SEC)
912 arg_delay += USEC_PER_MSEC*250;
913 else
914 arg_delay += USEC_PER_SEC;
915
2fa4092c 916 fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
8f2d43a0
LP
917 fflush(stdout);
918 sleep(1);
919 break;
920
921 case '-':
922 if (arg_delay <= USEC_PER_MSEC*500)
923 arg_delay = USEC_PER_MSEC*250;
924 else if (arg_delay < USEC_PER_MSEC*1250)
925 arg_delay -= USEC_PER_MSEC*250;
926 else
927 arg_delay -= USEC_PER_SEC;
928
2fa4092c 929 fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
8f2d43a0
LP
930 fflush(stdout);
931 sleep(1);
932 break;
933
934 case '?':
935 case 'h':
936 fprintf(stdout,
c851f34b 937 "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n"
7fcfb7ee
LP
938 "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n"
939 "\t<" ON "k" OFF "> Count kernel threads; <" ON "r" OFF "> Count recursively; <" ON "q" OFF "> Quit");
8f2d43a0
LP
940 fflush(stdout);
941 sleep(3);
942 break;
943
944 default:
45d7a8bb
LP
945 if (key < ' ')
946 fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key);
947 else
948 fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
8f2d43a0
LP
949 fflush(stdout);
950 sleep(1);
951 break;
952 }
953 }
954
8f2d43a0
LP
955 r = 0;
956
957finish:
958 group_hashmap_free(a);
959 group_hashmap_free(b);
960
dcd71990 961 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
8f2d43a0 962}