~ubuntu-branches/debian/jessie/systemd/jessie

« back to all changes in this revision

Viewing changes to .pc/v44..upstream-fixes_44-11/src/cgtop.c

  • Committer: Package Import Robot
  • Author(s): Michael Biebl
  • Date: 2013-03-13 08:03:06 UTC
  • Revision ID: package-import@ubuntu.com-20130313080306-d0mkta856x23o4k4
Tags: 44-11
* Team upload.
* Run debian-enable-units.service after sysinit.target to ensure our tmp
  files aren't nuked by systemd-tmpfiles.
* The mountoverflowtmp SysV init script no longer exists so remove that
  from remount-rootfs.service to avoid an unnecessary diff to upstream.
* Do not fail on purge if /var/lib/systemd is empty and has been removed
  by dpkg.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
9
  under the terms of the GNU General Public License as published by
 
10
  the Free Software Foundation; either version 2 of the License, or
 
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
 
16
  General Public License for more details.
 
17
 
 
18
  You should have received a copy of the GNU General Public License
 
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>
 
25
#include <unistd.h>
 
26
#include <alloca.h>
 
27
#include <getopt.h>
 
28
 
 
29
#include "util.h"
 
30
#include "hashmap.h"
 
31
#include "cgroup-util.h"
 
32
 
 
33
typedef struct Group {
 
34
        char *path;
 
35
 
 
36
        bool n_tasks_valid:1;
 
37
        bool cpu_valid:1;
 
38
        bool memory_valid:1;
 
39
        bool io_valid:1;
 
40
 
 
41
        unsigned n_tasks;
 
42
 
 
43
        unsigned cpu_iteration;
 
44
        uint64_t cpu_usage;
 
45
        struct timespec cpu_timestamp;
 
46
        double cpu_fraction;
 
47
 
 
48
        uint64_t memory;
 
49
 
 
50
        unsigned io_iteration;
 
51
        uint64_t io_input, io_output;
 
52
        struct timespec io_timestamp;
 
53
        uint64_t io_input_bps, io_output_bps;
 
54
} Group;
 
55
 
 
56
static unsigned arg_depth = 2;
 
57
static usec_t arg_delay = 1*USEC_PER_SEC;
 
58
 
 
59
static enum {
 
60
        ORDER_PATH,
 
61
        ORDER_TASKS,
 
62
        ORDER_CPU,
 
63
        ORDER_MEMORY,
 
64
        ORDER_IO
 
65
} arg_order = ORDER_CPU;
 
66
 
 
67
static void group_free(Group *g) {
 
68
        assert(g);
 
69
 
 
70
        free(g->path);
 
71
        free(g);
 
72
}
 
73
 
 
74
static void group_hashmap_clear(Hashmap *h) {
 
75
        Group *g;
 
76
 
 
77
        while ((g = hashmap_steal_first(h)))
 
78
                group_free(g);
 
79
}
 
80
 
 
81
static void group_hashmap_free(Hashmap *h) {
 
82
        group_hashmap_clear(h);
 
83
        hashmap_free(h);
 
84
}
 
85
 
 
86
static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
 
87
        Group *g;
 
88
        int r;
 
89
        FILE *f;
 
90
        pid_t pid;
 
91
        unsigned n;
 
92
 
 
93
        assert(controller);
 
94
        assert(path);
 
95
        assert(a);
 
96
 
 
97
        g = hashmap_get(a, path);
 
98
        if (!g) {
 
99
                g = hashmap_get(b, path);
 
100
                if (!g) {
 
101
                        g = new0(Group, 1);
 
102
                        if (!g)
 
103
                                return -ENOMEM;
 
104
 
 
105
                        g->path = strdup(path);
 
106
                        if (!g->path) {
 
107
                                group_free(g);
 
108
                                return -ENOMEM;
 
109
                        }
 
110
 
 
111
                        r = hashmap_put(a, g->path, g);
 
112
                        if (r < 0) {
 
113
                                group_free(g);
 
114
                                return r;
 
115
                        }
 
116
                } else {
 
117
                        assert_se(hashmap_move_one(a, b, path) == 0);
 
118
                        g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
 
119
                }
 
120
        }
 
121
 
 
122
        /* Regardless which controller, let's find the maximum number
 
123
         * of processes in any of it */
 
124
 
 
125
        r = cg_enumerate_tasks(controller, path, &f);
 
126
        if (r < 0)
 
127
                return r;
 
128
 
 
129
        n = 0;
 
130
        while (cg_read_pid(f, &pid) > 0)
 
131
                n++;
 
132
        fclose(f);
 
133
 
 
134
        if (n > 0) {
 
135
                if (g->n_tasks_valid)
 
136
                        g->n_tasks = MAX(g->n_tasks, n);
 
137
                else
 
138
                        g->n_tasks = n;
 
139
 
 
140
                g->n_tasks_valid = true;
 
141
        }
 
142
 
 
143
        if (streq(controller, "cpuacct")) {
 
144
                uint64_t new_usage;
 
145
                char *p, *v;
 
146
                struct timespec ts;
 
147
 
 
148
                r = cg_get_path(controller, path, "cpuacct.usage", &p);
 
149
                if (r < 0)
 
150
                        return r;
 
151
 
 
152
                r = read_one_line_file(p, &v);
 
153
                free(p);
 
154
                if (r < 0)
 
155
                        return r;
 
156
 
 
157
                r = safe_atou64(v, &new_usage);
 
158
                free(v);
 
159
                if (r < 0)
 
160
                        return r;
 
161
 
 
162
                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
 
163
 
 
164
                if (g->cpu_iteration == iteration - 1) {
 
165
                        uint64_t x, y;
 
166
 
 
167
                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
 
168
                                ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
 
169
 
 
170
                        y = new_usage - g->cpu_usage;
 
171
 
 
172
                        if (y > 0) {
 
173
                                g->cpu_fraction = (double) y / (double) x;
 
174
                                g->cpu_valid = true;
 
175
                        }
 
176
                }
 
177
 
 
178
                g->cpu_usage = new_usage;
 
179
                g->cpu_timestamp = ts;
 
180
                g->cpu_iteration = iteration;
 
181
 
 
182
        } else if (streq(controller, "memory")) {
 
183
                char *p, *v;
 
184
 
 
185
                r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
 
186
                if (r < 0)
 
187
                        return r;
 
188
 
 
189
                r = read_one_line_file(p, &v);
 
190
                free(p);
 
191
                if (r < 0)
 
192
                        return r;
 
193
 
 
194
                r = safe_atou64(v, &g->memory);
 
195
                free(v);
 
196
                if (r < 0)
 
197
                        return r;
 
198
 
 
199
                if (g->memory > 0)
 
200
                        g->memory_valid = true;
 
201
 
 
202
        } else if (streq(controller, "blkio")) {
 
203
                char *p;
 
204
                uint64_t wr = 0, rd = 0;
 
205
                struct timespec ts;
 
206
 
 
207
                r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
 
208
                if (r < 0)
 
209
                        return r;
 
210
 
 
211
                f = fopen(p, "re");
 
212
                free(p);
 
213
 
 
214
                if (!f)
 
215
                        return -errno;
 
216
 
 
217
                for (;;) {
 
218
                        char line[LINE_MAX], *l;
 
219
                        uint64_t k, *q;
 
220
 
 
221
                        if (!fgets(line, sizeof(line), f))
 
222
                                break;
 
223
 
 
224
                        l = strstrip(line);
 
225
                        l += strcspn(l, WHITESPACE);
 
226
                        l += strspn(l, WHITESPACE);
 
227
 
 
228
                        if (first_word(l, "Read")) {
 
229
                                l += 4;
 
230
                                q = &rd;
 
231
                        } else if (first_word(l, "Write")) {
 
232
                                l += 5;
 
233
                                q = &wr;
 
234
                        } else
 
235
                                continue;
 
236
 
 
237
                        l += strspn(l, WHITESPACE);
 
238
                        r = safe_atou64(l, &k);
 
239
                        if (r < 0)
 
240
                                continue;
 
241
 
 
242
                        *q += k;
 
243
                }
 
244
 
 
245
                fclose(f);
 
246
 
 
247
                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
 
248
 
 
249
                if (g->io_iteration == iteration - 1) {
 
250
                        uint64_t x, yr, yw;
 
251
 
 
252
                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
 
253
                                ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
 
254
 
 
255
                        yr = rd - g->io_input;
 
256
                        yw = wr - g->io_output;
 
257
 
 
258
                        if (yr > 0 || yw > 0) {
 
259
                                g->io_input_bps = (yr * 1000000000ULL) / x;
 
260
                                g->io_output_bps = (yw * 1000000000ULL) / x;
 
261
                                g->io_valid = true;
 
262
 
 
263
                        }
 
264
                }
 
265
 
 
266
                g->io_input = rd;
 
267
                g->io_output = wr;
 
268
                g->io_timestamp = ts;
 
269
                g->io_iteration = iteration;
 
270
        }
 
271
 
 
272
        return 0;
 
273
}
 
274
 
 
275
static int refresh_one(
 
276
                const char *controller,
 
277
                const char *path,
 
278
                Hashmap *a,
 
279
                Hashmap *b,
 
280
                unsigned iteration,
 
281
                unsigned depth) {
 
282
 
 
283
        DIR *d = NULL;
 
284
        int r;
 
285
 
 
286
        assert(controller);
 
287
        assert(path);
 
288
        assert(a);
 
289
 
 
290
        if (depth > arg_depth)
 
291
                return 0;
 
292
 
 
293
        r = process(controller, path, a, b, iteration);
 
294
        if (r < 0)
 
295
                return r;
 
296
 
 
297
        r = cg_enumerate_subgroups(controller, path, &d);
 
298
        if (r < 0) {
 
299
                if (r == ENOENT)
 
300
                        return 0;
 
301
 
 
302
                return r;
 
303
        }
 
304
 
 
305
        for (;;) {
 
306
                char *fn, *p;
 
307
 
 
308
                r = cg_read_subgroup(d, &fn);
 
309
                if (r <= 0)
 
310
                        goto finish;
 
311
 
 
312
                p = join(path, "/", fn, NULL);
 
313
                free(fn);
 
314
 
 
315
                if (!p) {
 
316
                        r = -ENOMEM;
 
317
                        goto finish;
 
318
                }
 
319
 
 
320
                path_kill_slashes(p);
 
321
 
 
322
                r = refresh_one(controller, p, a, b, iteration, depth + 1);
 
323
                free(p);
 
324
 
 
325
                if (r < 0)
 
326
                        goto finish;
 
327
        }
 
328
 
 
329
finish:
 
330
        if (d)
 
331
                closedir(d);
 
332
 
 
333
        return r;
 
334
}
 
335
 
 
336
static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
 
337
        int r;
 
338
 
 
339
        assert(a);
 
340
 
 
341
        r = refresh_one("name=systemd", "/", a, b, iteration, 0);
 
342
        if (r < 0)
 
343
                return r;
 
344
 
 
345
        r = refresh_one("cpuacct", "/", a, b, iteration, 0);
 
346
        if (r < 0)
 
347
                return r;
 
348
 
 
349
        r = refresh_one("memory", "/", a, b, iteration, 0);
 
350
        if (r < 0)
 
351
                return r;
 
352
 
 
353
        return refresh_one("blkio", "/", a, b, iteration, 0);
 
354
}
 
355
 
 
356
static int group_compare(const void*a, const void *b) {
 
357
        const Group *x = *(Group**)a, *y = *(Group**)b;
 
358
 
 
359
        if (path_startswith(y->path, x->path))
 
360
                return -1;
 
361
        if (path_startswith(x->path, y->path))
 
362
                return 1;
 
363
 
 
364
        if (arg_order == ORDER_CPU) {
 
365
                if (x->cpu_valid && y->cpu_valid) {
 
366
 
 
367
                        if (x->cpu_fraction > y->cpu_fraction)
 
368
                                return -1;
 
369
                        else if (x->cpu_fraction < y->cpu_fraction)
 
370
                                return 1;
 
371
                } else if (x->cpu_valid)
 
372
                        return -1;
 
373
                else if (y->cpu_valid)
 
374
                        return 1;
 
375
        }
 
376
 
 
377
        if (arg_order == ORDER_TASKS) {
 
378
 
 
379
                if (x->n_tasks_valid && y->n_tasks_valid) {
 
380
                        if (x->n_tasks > y->n_tasks)
 
381
                                return -1;
 
382
                        else if (x->n_tasks < y->n_tasks)
 
383
                                return 1;
 
384
                } else if (x->n_tasks_valid)
 
385
                        return -1;
 
386
                else if (y->n_tasks_valid)
 
387
                        return 1;
 
388
        }
 
389
 
 
390
        if (arg_order == ORDER_MEMORY) {
 
391
                if (x->memory_valid && y->memory_valid) {
 
392
                        if (x->memory > y->memory)
 
393
                                return -1;
 
394
                        else if (x->memory < y->memory)
 
395
                                return 1;
 
396
                } else if (x->memory_valid)
 
397
                        return -1;
 
398
                else if (y->memory_valid)
 
399
                        return 1;
 
400
        }
 
401
 
 
402
        if (arg_order == ORDER_IO) {
 
403
                if (x->io_valid && y->io_valid) {
 
404
                        if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
 
405
                                return -1;
 
406
                        else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
 
407
                                return 1;
 
408
                } else if (x->io_valid)
 
409
                        return -1;
 
410
                else if (y->io_valid)
 
411
                        return 1;
 
412
        }
 
413
 
 
414
        return strcmp(x->path, y->path);
 
415
}
 
416
 
 
417
static int display(Hashmap *a) {
 
418
        Iterator i;
 
419
        Group *g;
 
420
        Group **array;
 
421
        unsigned rows, n = 0, j;
 
422
 
 
423
        assert(a);
 
424
 
 
425
        /* Set cursor to top left corner and clear screen */
 
426
        fputs("\033[H"
 
427
              "\033[2J", stdout);
 
428
 
 
429
        array = alloca(sizeof(Group*) * hashmap_size(a));
 
430
 
 
431
        HASHMAP_FOREACH(g, a, i)
 
432
                if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
 
433
                        array[n++] = g;
 
434
 
 
435
        qsort(array, n, sizeof(Group*), group_compare);
 
436
 
 
437
        rows = fd_lines(STDOUT_FILENO);
 
438
        if (rows <= 0)
 
439
                rows = 25;
 
440
 
 
441
        printf("%s%-37s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
 
442
               arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_ON : "", "Path",     arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_OFF : "",
 
443
               arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_ON : "", "Tasks",    arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_OFF : "",
 
444
               arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_ON : "", "%CPU",     arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_OFF : "",
 
445
               arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory",   arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "",
 
446
               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "", "Input/s",  arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF : "",
 
447
               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "", "Output/s", arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF : "");
 
448
 
 
449
        for (j = 0; j < n; j++) {
 
450
                char *p;
 
451
                char m[FORMAT_BYTES_MAX];
 
452
 
 
453
                if (j + 5 > rows)
 
454
                        break;
 
455
 
 
456
                g = array[j];
 
457
 
 
458
                p = ellipsize(g->path, 37, 33);
 
459
                printf("%-37s", p ? p : g->path);
 
460
                free(p);
 
461
 
 
462
                if (g->n_tasks_valid)
 
463
                        printf(" %7u", g->n_tasks);
 
464
                else
 
465
                        fputs("       -", stdout);
 
466
 
 
467
                if (g->cpu_valid)
 
468
                        printf(" %6.1f", g->cpu_fraction*100);
 
469
                else
 
470
                        fputs("      -", stdout);
 
471
 
 
472
                if (g->memory_valid)
 
473
                        printf(" %8s", format_bytes(m, sizeof(m), g->memory));
 
474
                else
 
475
                        fputs("        -", stdout);
 
476
 
 
477
                if (g->io_valid) {
 
478
                        printf(" %8s",
 
479
                               format_bytes(m, sizeof(m), g->io_input_bps));
 
480
                        printf(" %8s",
 
481
                               format_bytes(m, sizeof(m), g->io_output_bps));
 
482
                } else
 
483
                        fputs("        -        -", stdout);
 
484
 
 
485
                putchar('\n');
 
486
        }
 
487
 
 
488
        return 0;
 
489
}
 
490
 
 
491
static void help(void) {
 
492
 
 
493
        printf("%s [OPTIONS...]\n\n"
 
494
               "Show top control groups by their resource usage.\n\n"
 
495
               "  -h --help           Show this help\n"
 
496
               "  -p                  Order by path\n"
 
497
               "  -t                  Order by number of tasks\n"
 
498
               "  -c                  Order by CPU load\n"
 
499
               "  -m                  Order by memory load\n"
 
500
               "  -i                  Order by IO load\n"
 
501
               "  -d --delay=DELAY    Specify delay\n"
 
502
               "     --depth=DEPTH    Maximum traversal depth (default: 2)\n",
 
503
               program_invocation_short_name);
 
504
}
 
505
 
 
506
static int parse_argv(int argc, char *argv[]) {
 
507
 
 
508
        enum {
 
509
                ARG_DEPTH = 0x100
 
510
        };
 
511
 
 
512
        static const struct option options[] = {
 
513
                { "help",  no_argument,       NULL, 'h'       },
 
514
                { "delay", required_argument, NULL, 'd'       },
 
515
                { "depth", required_argument, NULL, ARG_DEPTH },
 
516
                { NULL,    0,                 NULL, 0         }
 
517
        };
 
518
 
 
519
        int c;
 
520
        int r;
 
521
 
 
522
        assert(argc >= 1);
 
523
        assert(argv);
 
524
 
 
525
        while ((c = getopt_long(argc, argv, "hptcmid:", options, NULL)) >= 0) {
 
526
 
 
527
                switch (c) {
 
528
 
 
529
                case 'h':
 
530
                        help();
 
531
                        return 0;
 
532
 
 
533
                case ARG_DEPTH:
 
534
                        r = safe_atou(optarg, &arg_depth);
 
535
                        if (r < 0) {
 
536
                                log_error("Failed to parse depth parameter.");
 
537
                                return -EINVAL;
 
538
                        }
 
539
 
 
540
                        break;
 
541
 
 
542
                case 'd':
 
543
                        r = parse_usec(optarg, &arg_delay);
 
544
                        if (r < 0 || arg_delay <= 0) {
 
545
                                log_error("Failed to parse delay parameter.");
 
546
                                return -EINVAL;
 
547
                        }
 
548
 
 
549
                        break;
 
550
 
 
551
                case 'p':
 
552
                        arg_order = ORDER_PATH;
 
553
                        break;
 
554
 
 
555
                case 't':
 
556
                        arg_order = ORDER_TASKS;
 
557
                        break;
 
558
 
 
559
                case 'c':
 
560
                        arg_order = ORDER_CPU;
 
561
                        break;
 
562
 
 
563
                case 'm':
 
564
                        arg_order = ORDER_MEMORY;
 
565
                        break;
 
566
 
 
567
                case 'i':
 
568
                        arg_order = ORDER_IO;
 
569
                        break;
 
570
 
 
571
                case '?':
 
572
                        return -EINVAL;
 
573
 
 
574
                default:
 
575
                        log_error("Unknown option code %c", c);
 
576
                        return -EINVAL;
 
577
                }
 
578
        }
 
579
 
 
580
        if (optind < argc) {
 
581
                log_error("Too many arguments.");
 
582
                return -EINVAL;
 
583
        }
 
584
 
 
585
        return 1;
 
586
}
 
587
 
 
588
int main(int argc, char *argv[]) {
 
589
        int r;
 
590
        Hashmap *a = NULL, *b = NULL;
 
591
        unsigned iteration = 0;
 
592
        usec_t last_refresh = 0;
 
593
        bool quit = false, immediate_refresh = false;
 
594
 
 
595
        log_parse_environment();
 
596
        log_open();
 
597
 
 
598
        r = parse_argv(argc, argv);
 
599
        if (r <= 0)
 
600
                goto finish;
 
601
 
 
602
        a = hashmap_new(string_hash_func, string_compare_func);
 
603
        b = hashmap_new(string_hash_func, string_compare_func);
 
604
        if (!a || !b) {
 
605
                log_error("Out of memory");
 
606
                r = -ENOMEM;
 
607
                goto finish;
 
608
        }
 
609
 
 
610
        while (!quit) {
 
611
                Hashmap *c;
 
612
                usec_t t;
 
613
                char key;
 
614
                char h[FORMAT_TIMESPAN_MAX];
 
615
 
 
616
                t = now(CLOCK_MONOTONIC);
 
617
 
 
618
                if (t >= last_refresh + arg_delay || immediate_refresh) {
 
619
 
 
620
                        r = refresh(a, b, iteration++);
 
621
                        if (r < 0)
 
622
                                goto finish;
 
623
 
 
624
                        group_hashmap_clear(b);
 
625
 
 
626
                        c = a;
 
627
                        a = b;
 
628
                        b = c;
 
629
 
 
630
                        last_refresh = t;
 
631
                        immediate_refresh = false;
 
632
                }
 
633
 
 
634
                r = display(b);
 
635
                if (r < 0)
 
636
                        goto finish;
 
637
 
 
638
                r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
 
639
                if (r == -ETIMEDOUT)
 
640
                        continue;
 
641
                if (r < 0) {
 
642
                        log_error("Couldn't read key: %s", strerror(-r));
 
643
                        goto finish;
 
644
                }
 
645
 
 
646
                fputs("\r \r", stdout);
 
647
                fflush(stdout);
 
648
 
 
649
                switch (key) {
 
650
 
 
651
                case ' ':
 
652
                        immediate_refresh = true;
 
653
                        break;
 
654
 
 
655
                case 'q':
 
656
                        quit = true;
 
657
                        break;
 
658
 
 
659
                case 'p':
 
660
                        arg_order = ORDER_PATH;
 
661
                        break;
 
662
 
 
663
                case 't':
 
664
                        arg_order = ORDER_TASKS;
 
665
                        break;
 
666
 
 
667
                case 'c':
 
668
                        arg_order = ORDER_CPU;
 
669
                        break;
 
670
 
 
671
                case 'm':
 
672
                        arg_order = ORDER_MEMORY;
 
673
                        break;
 
674
 
 
675
                case 'i':
 
676
                        arg_order = ORDER_IO;
 
677
                        break;
 
678
 
 
679
                case '+':
 
680
                        if (arg_delay < USEC_PER_SEC)
 
681
                                arg_delay += USEC_PER_MSEC*250;
 
682
                        else
 
683
                                arg_delay += USEC_PER_SEC;
 
684
 
 
685
                        fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
 
686
                        fflush(stdout);
 
687
                        sleep(1);
 
688
                        break;
 
689
 
 
690
                case '-':
 
691
                        if (arg_delay <= USEC_PER_MSEC*500)
 
692
                                arg_delay = USEC_PER_MSEC*250;
 
693
                        else if (arg_delay < USEC_PER_MSEC*1250)
 
694
                                arg_delay -= USEC_PER_MSEC*250;
 
695
                        else
 
696
                                arg_delay -= USEC_PER_SEC;
 
697
 
 
698
                        fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
 
699
                        fflush(stdout);
 
700
                        sleep(1);
 
701
                        break;
 
702
 
 
703
                case '?':
 
704
                case 'h':
 
705
                        fprintf(stdout,
 
706
                                "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n"
 
707
                                "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh");
 
708
                        fflush(stdout);
 
709
                        sleep(3);
 
710
                        break;
 
711
 
 
712
                default:
 
713
                        fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
 
714
                        fflush(stdout);
 
715
                        sleep(1);
 
716
                        break;
 
717
                }
 
718
        }
 
719
 
 
720
        log_info("Exiting.");
 
721
 
 
722
        r = 0;
 
723
 
 
724
finish:
 
725
        group_hashmap_free(a);
 
726
        group_hashmap_free(b);
 
727
 
 
728
        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 
729
}