LCOV - code coverage report
Current view: top level - cgtop - cgtop.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 28 618 4.5 %
Date: 2019-08-23 13:36:53 Functions: 4 14 28.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 7 540 1.3 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <alloca.h>
       4                 :            : #include <errno.h>
       5                 :            : #include <getopt.h>
       6                 :            : #include <signal.h>
       7                 :            : #include <stdint.h>
       8                 :            : #include <stdlib.h>
       9                 :            : #include <string.h>
      10                 :            : #include <unistd.h>
      11                 :            : 
      12                 :            : #include "sd-bus.h"
      13                 :            : 
      14                 :            : #include "alloc-util.h"
      15                 :            : #include "bus-error.h"
      16                 :            : #include "bus-util.h"
      17                 :            : #include "cgroup-show.h"
      18                 :            : #include "cgroup-util.h"
      19                 :            : #include "fd-util.h"
      20                 :            : #include "fileio.h"
      21                 :            : #include "hashmap.h"
      22                 :            : #include "main-func.h"
      23                 :            : #include "parse-util.h"
      24                 :            : #include "path-util.h"
      25                 :            : #include "pretty-print.h"
      26                 :            : #include "process-util.h"
      27                 :            : #include "procfs-util.h"
      28                 :            : #include "sort-util.h"
      29                 :            : #include "stdio-util.h"
      30                 :            : #include "strv.h"
      31                 :            : #include "terminal-util.h"
      32                 :            : #include "unit-name.h"
      33                 :            : #include "virt.h"
      34                 :            : 
      35                 :            : typedef struct Group {
      36                 :            :         char *path;
      37                 :            : 
      38                 :            :         bool n_tasks_valid:1;
      39                 :            :         bool cpu_valid:1;
      40                 :            :         bool memory_valid:1;
      41                 :            :         bool io_valid:1;
      42                 :            : 
      43                 :            :         uint64_t n_tasks;
      44                 :            : 
      45                 :            :         unsigned cpu_iteration;
      46                 :            :         nsec_t cpu_usage;
      47                 :            :         nsec_t cpu_timestamp;
      48                 :            :         double cpu_fraction;
      49                 :            : 
      50                 :            :         uint64_t memory;
      51                 :            : 
      52                 :            :         unsigned io_iteration;
      53                 :            :         uint64_t io_input, io_output;
      54                 :            :         nsec_t io_timestamp;
      55                 :            :         uint64_t io_input_bps, io_output_bps;
      56                 :            : } Group;
      57                 :            : 
      58                 :            : static unsigned arg_depth = 3;
      59                 :            : static unsigned arg_iterations = (unsigned) -1;
      60                 :            : static bool arg_batch = false;
      61                 :            : static bool arg_raw = false;
      62                 :            : static usec_t arg_delay = 1*USEC_PER_SEC;
      63                 :            : static char* arg_machine = NULL;
      64                 :            : static char* arg_root = NULL;
      65                 :            : static bool arg_recursive = true;
      66                 :            : static bool arg_recursive_unset = false;
      67                 :            : 
      68                 :            : static enum {
      69                 :            :         COUNT_PIDS,
      70                 :            :         COUNT_USERSPACE_PROCESSES,
      71                 :            :         COUNT_ALL_PROCESSES,
      72                 :            : } arg_count = COUNT_PIDS;
      73                 :            : 
      74                 :            : static enum {
      75                 :            :         ORDER_PATH,
      76                 :            :         ORDER_TASKS,
      77                 :            :         ORDER_CPU,
      78                 :            :         ORDER_MEMORY,
      79                 :            :         ORDER_IO,
      80                 :            : } arg_order = ORDER_CPU;
      81                 :            : 
      82                 :            : static enum {
      83                 :            :         CPU_PERCENT,
      84                 :            :         CPU_TIME,
      85                 :            : } arg_cpu_type = CPU_PERCENT;
      86                 :            : 
      87                 :          0 : static Group *group_free(Group *g) {
      88         [ #  # ]:          0 :         if (!g)
      89                 :          0 :                 return NULL;
      90                 :            : 
      91                 :          0 :         free(g->path);
      92                 :          0 :         return mfree(g);
      93                 :            : }
      94                 :            : 
      95                 :          0 : static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64_t t) {
      96         [ #  # ]:          0 :         if (!is_valid)
      97                 :          0 :                 return "-";
      98         [ #  # ]:          0 :         if (arg_raw) {
      99                 :          0 :                 snprintf(buf, l, "%" PRIu64, t);
     100                 :          0 :                 return buf;
     101                 :            :         }
     102                 :          0 :         return format_bytes(buf, l, t);
     103                 :            : }
     104                 :            : 
     105                 :          0 : static bool is_root_cgroup(const char *path) {
     106                 :            : 
     107                 :            :         /* Returns true if the specified path belongs to the root cgroup. The root cgroup is special on cgroup v2 as it
     108                 :            :          * carries only very few attributes in order not to export multiple truth about system state as most
     109                 :            :          * information is available elsewhere in /proc anyway. We need to be able to deal with that, and need to get
     110                 :            :          * our data from different sources in that case.
     111                 :            :          *
     112                 :            :          * There's one extra complication in all of this, though 😣: if the path to the cgroup indicates we are in the
     113                 :            :          * root cgroup this might actually not be the case, because cgroup namespacing might be in effect
     114                 :            :          * (CLONE_NEWCGROUP). Since there's no nice way to distinguish a real cgroup root from a fake namespaced one we
     115                 :            :          * do an explicit container check here, under the assumption that CLONE_NEWCGROUP is generally used when
     116                 :            :          * container managers are used too.
     117                 :            :          *
     118                 :            :          * Note that checking for a container environment is kinda ugly, since in theory people could use cgtop from
     119                 :            :          * inside a container where cgroup namespacing is turned off to watch the host system. However, that's mostly a
     120                 :            :          * theoretic usecase, and if people actually try all they'll lose is accounting for the top-level cgroup. Which
     121                 :            :          * isn't too bad. */
     122                 :            : 
     123         [ #  # ]:          0 :         if (detect_container() > 0)
     124                 :          0 :                 return false;
     125                 :            : 
     126                 :          0 :         return empty_or_root(path);
     127                 :            : }
     128                 :            : 
     129                 :          0 : static int process(
     130                 :            :                 const char *controller,
     131                 :            :                 const char *path,
     132                 :            :                 Hashmap *a,
     133                 :            :                 Hashmap *b,
     134                 :            :                 unsigned iteration,
     135                 :            :                 Group **ret) {
     136                 :            : 
     137                 :            :         Group *g;
     138                 :            :         int r, all_unified;
     139                 :            : 
     140         [ #  # ]:          0 :         assert(controller);
     141         [ #  # ]:          0 :         assert(path);
     142         [ #  # ]:          0 :         assert(a);
     143                 :            : 
     144                 :          0 :         all_unified = cg_all_unified();
     145         [ #  # ]:          0 :         if (all_unified < 0)
     146                 :          0 :                 return all_unified;
     147                 :            : 
     148                 :          0 :         g = hashmap_get(a, path);
     149         [ #  # ]:          0 :         if (!g) {
     150                 :          0 :                 g = hashmap_get(b, path);
     151         [ #  # ]:          0 :                 if (!g) {
     152                 :          0 :                         g = new0(Group, 1);
     153         [ #  # ]:          0 :                         if (!g)
     154                 :          0 :                                 return -ENOMEM;
     155                 :            : 
     156                 :          0 :                         g->path = strdup(path);
     157         [ #  # ]:          0 :                         if (!g->path) {
     158                 :          0 :                                 group_free(g);
     159                 :          0 :                                 return -ENOMEM;
     160                 :            :                         }
     161                 :            : 
     162                 :          0 :                         r = hashmap_put(a, g->path, g);
     163         [ #  # ]:          0 :                         if (r < 0) {
     164                 :          0 :                                 group_free(g);
     165                 :          0 :                                 return r;
     166                 :            :                         }
     167                 :            :                 } else {
     168                 :          0 :                         r = hashmap_move_one(a, b, path);
     169         [ #  # ]:          0 :                         if (r < 0)
     170                 :          0 :                                 return r;
     171                 :            : 
     172                 :          0 :                         g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
     173                 :            :                 }
     174                 :            :         }
     175                 :            : 
     176         [ #  # ]:          0 :         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) &&
     177   [ #  #  #  # ]:          0 :             IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) {
     178         [ #  # ]:          0 :                 _cleanup_fclose_ FILE *f = NULL;
     179                 :            :                 pid_t pid;
     180                 :            : 
     181                 :          0 :                 r = cg_enumerate_processes(controller, path, &f);
     182         [ #  # ]:          0 :                 if (r == -ENOENT)
     183                 :          0 :                         return 0;
     184         [ #  # ]:          0 :                 if (r < 0)
     185                 :          0 :                         return r;
     186                 :            : 
     187                 :          0 :                 g->n_tasks = 0;
     188         [ #  # ]:          0 :                 while (cg_read_pid(f, &pid) > 0) {
     189                 :            : 
     190   [ #  #  #  # ]:          0 :                         if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0)
     191                 :          0 :                                 continue;
     192                 :            : 
     193                 :          0 :                         g->n_tasks++;
     194                 :            :                 }
     195                 :            : 
     196         [ #  # ]:          0 :                 if (g->n_tasks > 0)
     197                 :          0 :                         g->n_tasks_valid = true;
     198                 :            : 
     199   [ #  #  #  # ]:          0 :         } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) {
     200                 :            : 
     201         [ #  # ]:          0 :                 if (is_root_cgroup(path)) {
     202                 :          0 :                         r = procfs_tasks_get_current(&g->n_tasks);
     203         [ #  # ]:          0 :                         if (r < 0)
     204                 :          0 :                                 return r;
     205                 :            :                 } else {
     206   [ #  #  #  # ]:          0 :                         _cleanup_free_ char *p = NULL, *v = NULL;
     207                 :            : 
     208                 :          0 :                         r = cg_get_path(controller, path, "pids.current", &p);
     209         [ #  # ]:          0 :                         if (r < 0)
     210                 :          0 :                                 return r;
     211                 :            : 
     212                 :          0 :                         r = read_one_line_file(p, &v);
     213         [ #  # ]:          0 :                         if (r == -ENOENT)
     214                 :          0 :                                 return 0;
     215         [ #  # ]:          0 :                         if (r < 0)
     216                 :          0 :                                 return r;
     217                 :            : 
     218                 :          0 :                         r = safe_atou64(v, &g->n_tasks);
     219         [ #  # ]:          0 :                         if (r < 0)
     220                 :          0 :                                 return r;
     221                 :            :                 }
     222                 :            : 
     223         [ #  # ]:          0 :                 if (g->n_tasks > 0)
     224                 :          0 :                         g->n_tasks_valid = true;
     225                 :            : 
     226         [ #  # ]:          0 :         } else if (streq(controller, "memory")) {
     227                 :            : 
     228         [ #  # ]:          0 :                 if (is_root_cgroup(path)) {
     229                 :          0 :                         r = procfs_memory_get_used(&g->memory);
     230         [ #  # ]:          0 :                         if (r < 0)
     231                 :          0 :                                 return r;
     232                 :            :                 } else {
     233   [ #  #  #  # ]:          0 :                         _cleanup_free_ char *p = NULL, *v = NULL;
     234                 :            : 
     235         [ #  # ]:          0 :                         if (all_unified)
     236                 :          0 :                                 r = cg_get_path(controller, path, "memory.current", &p);
     237                 :            :                         else
     238                 :          0 :                                 r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
     239         [ #  # ]:          0 :                         if (r < 0)
     240                 :          0 :                                 return r;
     241                 :            : 
     242                 :          0 :                         r = read_one_line_file(p, &v);
     243         [ #  # ]:          0 :                         if (r == -ENOENT)
     244                 :          0 :                                 return 0;
     245         [ #  # ]:          0 :                         if (r < 0)
     246                 :          0 :                                 return r;
     247                 :            : 
     248                 :          0 :                         r = safe_atou64(v, &g->memory);
     249         [ #  # ]:          0 :                         if (r < 0)
     250                 :          0 :                                 return r;
     251                 :            :                 }
     252                 :            : 
     253         [ #  # ]:          0 :                 if (g->memory > 0)
     254                 :          0 :                         g->memory_valid = true;
     255                 :            : 
     256   [ #  #  #  # ]:          0 :         } else if ((streq(controller, "io") && all_unified) ||
     257   [ #  #  #  # ]:          0 :                    (streq(controller, "blkio") && !all_unified)) {
     258         [ #  # ]:          0 :                 _cleanup_fclose_ FILE *f = NULL;
     259         [ #  # ]:          0 :                 _cleanup_free_ char *p = NULL;
     260                 :          0 :                 uint64_t wr = 0, rd = 0;
     261                 :            :                 nsec_t timestamp;
     262                 :            : 
     263         [ #  # ]:          0 :                 r = cg_get_path(controller, path, all_unified ? "io.stat" : "blkio.io_service_bytes", &p);
     264         [ #  # ]:          0 :                 if (r < 0)
     265                 :          0 :                         return r;
     266                 :            : 
     267                 :          0 :                 f = fopen(p, "re");
     268         [ #  # ]:          0 :                 if (!f) {
     269         [ #  # ]:          0 :                         if (errno == ENOENT)
     270                 :          0 :                                 return 0;
     271                 :          0 :                         return -errno;
     272                 :            :                 }
     273                 :            : 
     274                 :          0 :                 for (;;) {
     275   [ #  #  #  # ]:          0 :                         _cleanup_free_ char *line = NULL;
     276                 :            :                         uint64_t k, *q;
     277                 :            :                         char *l;
     278                 :            : 
     279                 :          0 :                         r = read_line(f, LONG_LINE_MAX, &line);
     280         [ #  # ]:          0 :                         if (r < 0)
     281                 :          0 :                                 return r;
     282         [ #  # ]:          0 :                         if (r == 0)
     283                 :          0 :                                 break;
     284                 :            : 
     285                 :            :                         /* Trim and skip the device */
     286                 :          0 :                         l = strstrip(line);
     287                 :          0 :                         l += strcspn(l, WHITESPACE);
     288                 :          0 :                         l += strspn(l, WHITESPACE);
     289                 :            : 
     290         [ #  # ]:          0 :                         if (all_unified) {
     291         [ #  # ]:          0 :                                 while (!isempty(l)) {
     292         [ #  # ]:          0 :                                         if (sscanf(l, "rbytes=%" SCNu64, &k))
     293                 :          0 :                                                 rd += k;
     294         [ #  # ]:          0 :                                         else if (sscanf(l, "wbytes=%" SCNu64, &k))
     295                 :          0 :                                                 wr += k;
     296                 :            : 
     297                 :          0 :                                         l += strcspn(l, WHITESPACE);
     298                 :          0 :                                         l += strspn(l, WHITESPACE);
     299                 :            :                                 }
     300                 :            :                         } else {
     301         [ #  # ]:          0 :                                 if (first_word(l, "Read")) {
     302                 :          0 :                                         l += 4;
     303                 :          0 :                                         q = &rd;
     304         [ #  # ]:          0 :                                 } else if (first_word(l, "Write")) {
     305                 :          0 :                                         l += 5;
     306                 :          0 :                                         q = &wr;
     307                 :            :                                 } else
     308                 :          0 :                                         continue;
     309                 :            : 
     310                 :          0 :                                 l += strspn(l, WHITESPACE);
     311                 :          0 :                                 r = safe_atou64(l, &k);
     312         [ #  # ]:          0 :                                 if (r < 0)
     313                 :          0 :                                         continue;
     314                 :            : 
     315                 :          0 :                                 *q += k;
     316                 :            :                         }
     317                 :            :                 }
     318                 :            : 
     319                 :          0 :                 timestamp = now_nsec(CLOCK_MONOTONIC);
     320                 :            : 
     321         [ #  # ]:          0 :                 if (g->io_iteration == iteration - 1) {
     322                 :            :                         uint64_t x, yr, yw;
     323                 :            : 
     324                 :          0 :                         x = (uint64_t) (timestamp - g->io_timestamp);
     325         [ #  # ]:          0 :                         if (x < 1)
     326                 :          0 :                                 x = 1;
     327                 :            : 
     328         [ #  # ]:          0 :                         if (rd > g->io_input)
     329                 :          0 :                                 yr = rd - g->io_input;
     330                 :            :                         else
     331                 :          0 :                                 yr = 0;
     332                 :            : 
     333         [ #  # ]:          0 :                         if (wr > g->io_output)
     334                 :          0 :                                 yw = wr - g->io_output;
     335                 :            :                         else
     336                 :          0 :                                 yw = 0;
     337                 :            : 
     338   [ #  #  #  # ]:          0 :                         if (yr > 0 || yw > 0) {
     339                 :          0 :                                 g->io_input_bps = (yr * 1000000000ULL) / x;
     340                 :          0 :                                 g->io_output_bps = (yw * 1000000000ULL) / x;
     341                 :          0 :                                 g->io_valid = true;
     342                 :            :                         }
     343                 :            :                 }
     344                 :            : 
     345                 :          0 :                 g->io_input = rd;
     346                 :          0 :                 g->io_output = wr;
     347                 :          0 :                 g->io_timestamp = timestamp;
     348                 :          0 :                 g->io_iteration = iteration;
     349   [ #  #  #  # ]:          0 :         } else if (STR_IN_SET(controller, "cpu", "cpuacct") || cpu_accounting_is_cheap()) {
     350   [ #  #  #  # ]:          0 :                 _cleanup_free_ char *p = NULL, *v = NULL;
     351                 :            :                 uint64_t new_usage;
     352                 :            :                 nsec_t timestamp;
     353                 :            : 
     354         [ #  # ]:          0 :                 if (is_root_cgroup(path)) {
     355                 :          0 :                         r = procfs_cpu_get_usage(&new_usage);
     356         [ #  # ]:          0 :                         if (r < 0)
     357                 :          0 :                                 return r;
     358         [ #  # ]:          0 :                 } else if (all_unified) {
     359         [ #  # ]:          0 :                         _cleanup_free_ char *val = NULL;
     360                 :            : 
     361         [ #  # ]:          0 :                         if (!streq(controller, "cpu"))
     362                 :          0 :                                 return 0;
     363                 :            : 
     364                 :          0 :                         r = cg_get_keyed_attribute("cpu", path, "cpu.stat", STRV_MAKE("usage_usec"), &val);
     365   [ #  #  #  # ]:          0 :                         if (IN_SET(r, -ENOENT, -ENXIO))
     366                 :          0 :                                 return 0;
     367         [ #  # ]:          0 :                         if (r < 0)
     368                 :          0 :                                 return r;
     369                 :            : 
     370                 :          0 :                         r = safe_atou64(val, &new_usage);
     371         [ #  # ]:          0 :                         if (r < 0)
     372                 :          0 :                                 return r;
     373                 :            : 
     374                 :          0 :                         new_usage *= NSEC_PER_USEC;
     375                 :            :                 } else {
     376         [ #  # ]:          0 :                         if (!streq(controller, "cpuacct"))
     377                 :          0 :                                 return 0;
     378                 :            : 
     379                 :          0 :                         r = cg_get_path(controller, path, "cpuacct.usage", &p);
     380         [ #  # ]:          0 :                         if (r < 0)
     381                 :          0 :                                 return r;
     382                 :            : 
     383                 :          0 :                         r = read_one_line_file(p, &v);
     384         [ #  # ]:          0 :                         if (r == -ENOENT)
     385                 :          0 :                                 return 0;
     386         [ #  # ]:          0 :                         if (r < 0)
     387                 :          0 :                                 return r;
     388                 :            : 
     389                 :          0 :                         r = safe_atou64(v, &new_usage);
     390         [ #  # ]:          0 :                         if (r < 0)
     391                 :          0 :                                 return r;
     392                 :            :                 }
     393                 :            : 
     394                 :          0 :                 timestamp = now_nsec(CLOCK_MONOTONIC);
     395                 :            : 
     396         [ #  # ]:          0 :                 if (g->cpu_iteration == iteration - 1 &&
     397         [ #  # ]:          0 :                     (nsec_t) new_usage > g->cpu_usage) {
     398                 :            : 
     399                 :            :                         nsec_t x, y;
     400                 :            : 
     401                 :          0 :                         x = timestamp - g->cpu_timestamp;
     402         [ #  # ]:          0 :                         if (x < 1)
     403                 :          0 :                                 x = 1;
     404                 :            : 
     405                 :          0 :                         y = (nsec_t) new_usage - g->cpu_usage;
     406                 :          0 :                         g->cpu_fraction = (double) y / (double) x;
     407                 :          0 :                         g->cpu_valid = true;
     408                 :            :                 }
     409                 :            : 
     410                 :          0 :                 g->cpu_usage = (nsec_t) new_usage;
     411                 :          0 :                 g->cpu_timestamp = timestamp;
     412                 :          0 :                 g->cpu_iteration = iteration;
     413                 :            : 
     414                 :            :         }
     415                 :            : 
     416         [ #  # ]:          0 :         if (ret)
     417                 :          0 :                 *ret = g;
     418                 :            : 
     419                 :          0 :         return 0;
     420                 :            : }
     421                 :            : 
     422                 :          0 : static int refresh_one(
     423                 :            :                 const char *controller,
     424                 :            :                 const char *path,
     425                 :            :                 Hashmap *a,
     426                 :            :                 Hashmap *b,
     427                 :            :                 unsigned iteration,
     428                 :            :                 unsigned depth,
     429                 :            :                 Group **ret) {
     430                 :            : 
     431                 :          0 :         _cleanup_closedir_ DIR *d = NULL;
     432                 :          0 :         Group *ours = NULL;
     433                 :            :         int r;
     434                 :            : 
     435         [ #  # ]:          0 :         assert(controller);
     436         [ #  # ]:          0 :         assert(path);
     437         [ #  # ]:          0 :         assert(a);
     438                 :            : 
     439         [ #  # ]:          0 :         if (depth > arg_depth)
     440                 :          0 :                 return 0;
     441                 :            : 
     442                 :          0 :         r = process(controller, path, a, b, iteration, &ours);
     443         [ #  # ]:          0 :         if (r < 0)
     444                 :          0 :                 return r;
     445                 :            : 
     446                 :          0 :         r = cg_enumerate_subgroups(controller, path, &d);
     447         [ #  # ]:          0 :         if (r == -ENOENT)
     448                 :          0 :                 return 0;
     449         [ #  # ]:          0 :         if (r < 0)
     450                 :          0 :                 return r;
     451                 :            : 
     452                 :          0 :         for (;;) {
     453   [ #  #  #  #  :          0 :                 _cleanup_free_ char *fn = NULL, *p = NULL;
                   #  # ]
     454                 :          0 :                 Group *child = NULL;
     455                 :            : 
     456                 :          0 :                 r = cg_read_subgroup(d, &fn);
     457         [ #  # ]:          0 :                 if (r < 0)
     458                 :          0 :                         return r;
     459         [ #  # ]:          0 :                 if (r == 0)
     460                 :          0 :                         break;
     461                 :            : 
     462                 :          0 :                 p = path_join(path, fn);
     463         [ #  # ]:          0 :                 if (!p)
     464                 :          0 :                         return -ENOMEM;
     465                 :            : 
     466                 :          0 :                 path_simplify(p, false);
     467                 :            : 
     468                 :          0 :                 r = refresh_one(controller, p, a, b, iteration, depth + 1, &child);
     469         [ #  # ]:          0 :                 if (r < 0)
     470                 :          0 :                         return r;
     471                 :            : 
     472         [ #  # ]:          0 :                 if (arg_recursive &&
     473   [ #  #  #  #  :          0 :                     IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) &&
                   #  # ]
     474                 :          0 :                     child &&
     475         [ #  # ]:          0 :                     child->n_tasks_valid &&
     476         [ #  # ]:          0 :                     streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
     477                 :            : 
     478                 :            :                         /* Recursively sum up processes */
     479                 :            : 
     480         [ #  # ]:          0 :                         if (ours->n_tasks_valid)
     481                 :          0 :                                 ours->n_tasks += child->n_tasks;
     482                 :            :                         else {
     483                 :          0 :                                 ours->n_tasks = child->n_tasks;
     484                 :          0 :                                 ours->n_tasks_valid = true;
     485                 :            :                         }
     486                 :            :                 }
     487                 :            :         }
     488                 :            : 
     489         [ #  # ]:          0 :         if (ret)
     490                 :          0 :                 *ret = ours;
     491                 :            : 
     492                 :          0 :         return 1;
     493                 :            : }
     494                 :            : 
     495                 :          0 : static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) {
     496                 :            :         const char *c;
     497                 :            :         int r;
     498                 :            : 
     499         [ #  # ]:          0 :         FOREACH_STRING(c, SYSTEMD_CGROUP_CONTROLLER, "cpu", "cpuacct", "memory", "io", "blkio", "pids") {
     500                 :          0 :                 r = refresh_one(c, root, a, b, iteration, 0, NULL);
     501         [ #  # ]:          0 :                 if (r < 0)
     502                 :          0 :                         return r;
     503                 :            :         }
     504                 :            : 
     505                 :          0 :         return 0;
     506                 :            : }
     507                 :            : 
     508                 :          0 : static int group_compare(Group * const *a, Group * const *b) {
     509                 :          0 :         const Group *x = *a, *y = *b;
     510                 :            :         int r;
     511                 :            : 
     512   [ #  #  #  # ]:          0 :         if (arg_order != ORDER_TASKS || arg_recursive) {
     513                 :            :                 /* Let's make sure that the parent is always before
     514                 :            :                  * the child. Except when ordering by tasks and
     515                 :            :                  * recursive summing is off, since that is actually
     516                 :            :                  * not accumulative for all children. */
     517                 :            : 
     518         [ #  # ]:          0 :                 if (path_startswith(empty_to_root(y->path), empty_to_root(x->path)))
     519                 :          0 :                         return -1;
     520         [ #  # ]:          0 :                 if (path_startswith(empty_to_root(x->path), empty_to_root(y->path)))
     521                 :          0 :                         return 1;
     522                 :            :         }
     523                 :            : 
     524   [ #  #  #  #  :          0 :         switch (arg_order) {
                   #  # ]
     525                 :            : 
     526                 :          0 :         case ORDER_PATH:
     527                 :          0 :                 break;
     528                 :            : 
     529                 :          0 :         case ORDER_CPU:
     530         [ #  # ]:          0 :                 if (arg_cpu_type == CPU_PERCENT) {
     531   [ #  #  #  # ]:          0 :                         if (x->cpu_valid && y->cpu_valid) {
     532         [ #  # ]:          0 :                                 r = CMP(y->cpu_fraction, x->cpu_fraction);
     533         [ #  # ]:          0 :                                 if (r != 0)
     534                 :          0 :                                         return r;
     535         [ #  # ]:          0 :                         } else if (x->cpu_valid)
     536                 :          0 :                                 return -1;
     537         [ #  # ]:          0 :                         else if (y->cpu_valid)
     538                 :          0 :                                 return 1;
     539                 :            :                 } else {
     540         [ #  # ]:          0 :                         r = CMP(y->cpu_usage, x->cpu_usage);
     541         [ #  # ]:          0 :                         if (r != 0)
     542                 :          0 :                                 return r;
     543                 :            :                 }
     544                 :            : 
     545                 :          0 :                 break;
     546                 :            : 
     547                 :          0 :         case ORDER_TASKS:
     548   [ #  #  #  # ]:          0 :                 if (x->n_tasks_valid && y->n_tasks_valid) {
     549         [ #  # ]:          0 :                         r = CMP(y->n_tasks, x->n_tasks);
     550         [ #  # ]:          0 :                         if (r != 0)
     551                 :          0 :                                 return r;
     552         [ #  # ]:          0 :                 } else if (x->n_tasks_valid)
     553                 :          0 :                         return -1;
     554         [ #  # ]:          0 :                 else if (y->n_tasks_valid)
     555                 :          0 :                         return 1;
     556                 :            : 
     557                 :          0 :                 break;
     558                 :            : 
     559                 :          0 :         case ORDER_MEMORY:
     560   [ #  #  #  # ]:          0 :                 if (x->memory_valid && y->memory_valid) {
     561         [ #  # ]:          0 :                         r = CMP(y->memory, x->memory);
     562         [ #  # ]:          0 :                         if (r != 0)
     563                 :          0 :                                 return r;
     564         [ #  # ]:          0 :                 } else if (x->memory_valid)
     565                 :          0 :                         return -1;
     566         [ #  # ]:          0 :                 else if (y->memory_valid)
     567                 :          0 :                         return 1;
     568                 :            : 
     569                 :          0 :                 break;
     570                 :            : 
     571                 :          0 :         case ORDER_IO:
     572   [ #  #  #  # ]:          0 :                 if (x->io_valid && y->io_valid) {
     573         [ #  # ]:          0 :                         r = CMP(y->io_input_bps + y->io_output_bps, x->io_input_bps + x->io_output_bps);
     574         [ #  # ]:          0 :                         if (r != 0)
     575                 :          0 :                                 return r;
     576         [ #  # ]:          0 :                 } else if (x->io_valid)
     577                 :          0 :                         return -1;
     578         [ #  # ]:          0 :                 else if (y->io_valid)
     579                 :          0 :                         return 1;
     580                 :            :         }
     581                 :            : 
     582                 :          0 :         return path_compare(x->path, y->path);
     583                 :            : }
     584                 :            : 
     585                 :          0 : static void display(Hashmap *a) {
     586                 :            :         Iterator i;
     587                 :            :         Group *g;
     588                 :            :         Group **array;
     589                 :            :         signed path_columns;
     590                 :          0 :         unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 3; /* 3 for ellipsize() to work properly */
     591                 :          0 :         char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
     592                 :            : 
     593         [ #  # ]:          0 :         assert(a);
     594                 :            : 
     595         [ #  # ]:          0 :         if (!terminal_is_dumb())
     596                 :          0 :                 fputs(ANSI_HOME_CLEAR, stdout);
     597                 :            : 
     598   [ #  #  #  # ]:          0 :         array = newa(Group*, hashmap_size(a));
     599                 :            : 
     600         [ #  # ]:          0 :         HASHMAP_FOREACH(g, a, i)
     601   [ #  #  #  #  :          0 :                 if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
             #  #  #  # ]
     602                 :          0 :                         array[n++] = g;
     603                 :            : 
     604                 :          0 :         typesafe_qsort(array, n, group_compare);
     605                 :            : 
     606                 :            :         /* Find the longest names in one run */
     607         [ #  # ]:          0 :         for (j = 0; j < n; j++) {
     608                 :            :                 unsigned cputlen, pathtlen;
     609                 :            : 
     610                 :          0 :                 format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
     611                 :          0 :                 cputlen = strlen(buffer);
     612                 :          0 :                 maxtcpu = MAX(maxtcpu, cputlen);
     613                 :            : 
     614                 :          0 :                 pathtlen = strlen(array[j]->path);
     615                 :          0 :                 maxtpath = MAX(maxtpath, pathtlen);
     616                 :            :         }
     617                 :            : 
     618         [ #  # ]:          0 :         if (arg_cpu_type == CPU_PERCENT)
     619         [ #  # ]:          0 :                 xsprintf(buffer, "%6s", "%CPU");
     620                 :            :         else
     621         [ #  # ]:          0 :                 xsprintf(buffer, "%*s", maxtcpu, "CPU Time");
     622                 :            : 
     623                 :          0 :         rows = lines();
     624         [ #  # ]:          0 :         if (rows <= 10)
     625                 :          0 :                 rows = 10;
     626                 :            : 
     627         [ #  # ]:          0 :         if (on_tty()) {
     628                 :            :                 const char *on, *off;
     629                 :            : 
     630                 :          0 :                 path_columns = columns() - 36 - strlen(buffer);
     631         [ #  # ]:          0 :                 if (path_columns < 10)
     632                 :          0 :                         path_columns = 10;
     633                 :            : 
     634                 :          0 :                 on = ansi_highlight_underline();
     635                 :          0 :                 off = ansi_underline();
     636                 :            : 
     637                 :          0 :                 printf("%s%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s%s\n",
     638                 :            :                        ansi_underline(),
     639         [ #  # ]:          0 :                        arg_order == ORDER_PATH ? on : "", path_columns, "Control Group",
     640         [ #  # ]:          0 :                        arg_order == ORDER_PATH ? off : "",
     641   [ #  #  #  #  :          0 :                        arg_order == ORDER_TASKS ? on : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+",
                   #  # ]
     642         [ #  # ]:          0 :                        arg_order == ORDER_TASKS ? off : "",
     643         [ #  # ]:          0 :                        arg_order == ORDER_CPU ? on : "", buffer,
     644         [ #  # ]:          0 :                        arg_order == ORDER_CPU ? off : "",
     645         [ #  # ]:          0 :                        arg_order == ORDER_MEMORY ? on : "", "Memory",
     646         [ #  # ]:          0 :                        arg_order == ORDER_MEMORY ? off : "",
     647         [ #  # ]:          0 :                        arg_order == ORDER_IO ? on : "", "Input/s",
     648         [ #  # ]:          0 :                        arg_order == ORDER_IO ? off : "",
     649         [ #  # ]:          0 :                        arg_order == ORDER_IO ? on : "", "Output/s",
     650         [ #  # ]:          0 :                        arg_order == ORDER_IO ? off : "",
     651                 :            :                        ansi_normal());
     652                 :            :         } else
     653                 :          0 :                 path_columns = maxtpath;
     654                 :            : 
     655         [ #  # ]:          0 :         for (j = 0; j < n; j++) {
     656         [ #  # ]:          0 :                 _cleanup_free_ char *ellipsized = NULL;
     657                 :            :                 const char *path;
     658                 :            : 
     659   [ #  #  #  # ]:          0 :                 if (on_tty() && j + 6 > rows)
     660                 :          0 :                         break;
     661                 :            : 
     662                 :          0 :                 g = array[j];
     663                 :            : 
     664                 :          0 :                 path = empty_to_root(g->path);
     665                 :          0 :                 ellipsized = ellipsize(path, path_columns, 33);
     666         [ #  # ]:          0 :                 printf("%-*s", path_columns, ellipsized ?: path);
     667                 :            : 
     668         [ #  # ]:          0 :                 if (g->n_tasks_valid)
     669                 :          0 :                         printf(" %7" PRIu64, g->n_tasks);
     670                 :            :                 else
     671                 :          0 :                         fputs("       -", stdout);
     672                 :            : 
     673         [ #  # ]:          0 :                 if (arg_cpu_type == CPU_PERCENT) {
     674         [ #  # ]:          0 :                         if (g->cpu_valid)
     675                 :          0 :                                 printf(" %6.1f", g->cpu_fraction*100);
     676                 :            :                         else
     677                 :          0 :                                 fputs("      -", stdout);
     678                 :            :                 } else
     679                 :          0 :                         printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
     680                 :            : 
     681                 :          0 :                 printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory));
     682                 :          0 :                 printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps));
     683                 :          0 :                 printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_output_bps));
     684                 :            : 
     685                 :          0 :                 putchar('\n');
     686                 :            :         }
     687                 :          0 : }
     688                 :            : 
     689                 :         12 : static int help(void) {
     690                 :         12 :         _cleanup_free_ char *link = NULL;
     691                 :            :         int r;
     692                 :            : 
     693                 :         12 :         r = terminal_urlify_man("systemd-cgtop", "1", &link);
     694         [ -  + ]:         12 :         if (r < 0)
     695                 :          0 :                 return log_oom();
     696                 :            : 
     697                 :         12 :         printf("%s [OPTIONS...] [CGROUP]\n\n"
     698                 :            :                "Show top control groups by their resource usage.\n\n"
     699                 :            :                "  -h --help           Show this help\n"
     700                 :            :                "     --version        Show package version\n"
     701                 :            :                "  -p --order=path     Order by path\n"
     702                 :            :                "  -t --order=tasks    Order by number of tasks/processes\n"
     703                 :            :                "  -c --order=cpu      Order by CPU load (default)\n"
     704                 :            :                "  -m --order=memory   Order by memory load\n"
     705                 :            :                "  -i --order=io       Order by IO load\n"
     706                 :            :                "  -r --raw            Provide raw (not human-readable) numbers\n"
     707                 :            :                "     --cpu=percentage Show CPU usage as percentage (default)\n"
     708                 :            :                "     --cpu=time       Show CPU usage as time\n"
     709                 :            :                "  -P                  Count userspace processes instead of tasks (excl. kernel)\n"
     710                 :            :                "  -k                  Count all processes instead of tasks (incl. kernel)\n"
     711                 :            :                "     --recursive=BOOL Sum up process count recursively\n"
     712                 :            :                "  -d --delay=DELAY    Delay between updates\n"
     713                 :            :                "  -n --iterations=N   Run for N iterations before exiting\n"
     714                 :            :                "  -1                  Shortcut for --iterations=1\n"
     715                 :            :                "  -b --batch          Run in batch mode, accepting no input\n"
     716                 :            :                "     --depth=DEPTH    Maximum traversal depth (default: %u)\n"
     717                 :            :                "  -M --machine=       Show container\n"
     718                 :            :                "\nSee the %s for details.\n"
     719                 :            :                , program_invocation_short_name
     720                 :            :                , arg_depth
     721                 :            :                , link
     722                 :            :         );
     723                 :            : 
     724                 :         12 :         return 0;
     725                 :            : }
     726                 :            : 
     727                 :         16 : static int parse_argv(int argc, char *argv[]) {
     728                 :            :         enum {
     729                 :            :                 ARG_VERSION = 0x100,
     730                 :            :                 ARG_DEPTH,
     731                 :            :                 ARG_CPU_TYPE,
     732                 :            :                 ARG_ORDER,
     733                 :            :                 ARG_RECURSIVE,
     734                 :            :         };
     735                 :            : 
     736                 :            :         static const struct option options[] = {
     737                 :            :                 { "help",         no_argument,       NULL, 'h'           },
     738                 :            :                 { "version",      no_argument,       NULL, ARG_VERSION   },
     739                 :            :                 { "delay",        required_argument, NULL, 'd'           },
     740                 :            :                 { "iterations",   required_argument, NULL, 'n'           },
     741                 :            :                 { "batch",        no_argument,       NULL, 'b'           },
     742                 :            :                 { "raw",          no_argument,       NULL, 'r'           },
     743                 :            :                 { "depth",        required_argument, NULL, ARG_DEPTH     },
     744                 :            :                 { "cpu",          optional_argument, NULL, ARG_CPU_TYPE  },
     745                 :            :                 { "order",        required_argument, NULL, ARG_ORDER     },
     746                 :            :                 { "recursive",    required_argument, NULL, ARG_RECURSIVE },
     747                 :            :                 { "machine",      required_argument, NULL, 'M'           },
     748                 :            :                 {}
     749                 :            :         };
     750                 :            : 
     751                 :            :         int c, r;
     752                 :            : 
     753         [ -  + ]:         16 :         assert(argc >= 1);
     754         [ -  + ]:         16 :         assert(argv);
     755                 :            : 
     756         [ +  - ]:         16 :         while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:1", options, NULL)) >= 0)
     757                 :            : 
     758   [ +  -  -  -  :         16 :                 switch (c) {
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                   +  - ]
     759                 :            : 
     760                 :         12 :                 case 'h':
     761                 :         12 :                         return help();
     762                 :            : 
     763                 :          0 :                 case ARG_VERSION:
     764                 :          0 :                         return version();
     765                 :            : 
     766                 :          0 :                 case ARG_CPU_TYPE:
     767         [ #  # ]:          0 :                         if (optarg) {
     768         [ #  # ]:          0 :                                 if (streq(optarg, "time"))
     769                 :          0 :                                         arg_cpu_type = CPU_TIME;
     770         [ #  # ]:          0 :                                 else if (streq(optarg, "percentage"))
     771                 :          0 :                                         arg_cpu_type = CPU_PERCENT;
     772                 :            :                                 else
     773         [ #  # ]:          0 :                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     774                 :            :                                                                "Unknown argument to --cpu=: %s",
     775                 :            :                                                                optarg);
     776                 :            :                         } else
     777                 :          0 :                                 arg_cpu_type = CPU_TIME;
     778                 :            : 
     779                 :          0 :                         break;
     780                 :            : 
     781                 :          0 :                 case ARG_DEPTH:
     782                 :          0 :                         r = safe_atou(optarg, &arg_depth);
     783         [ #  # ]:          0 :                         if (r < 0)
     784         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to parse depth parameter '%s': %m", optarg);
     785                 :            : 
     786                 :          0 :                         break;
     787                 :            : 
     788                 :          0 :                 case 'd':
     789                 :          0 :                         r = parse_sec(optarg, &arg_delay);
     790         [ #  # ]:          0 :                         if (r < 0)
     791         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to parse delay parameter '%s': %m", optarg);
     792         [ #  # ]:          0 :                         if (arg_delay <= 0)
     793         [ #  # ]:          0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     794                 :            :                                                        "Invalid delay parameter '%s'",
     795                 :            :                                                        optarg);
     796                 :            : 
     797                 :          0 :                         break;
     798                 :            : 
     799                 :          0 :                 case 'n':
     800                 :          0 :                         r = safe_atou(optarg, &arg_iterations);
     801         [ #  # ]:          0 :                         if (r < 0)
     802         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to parse iterations parameter '%s': %m", optarg);
     803                 :            : 
     804                 :          0 :                         break;
     805                 :            : 
     806                 :          0 :                 case '1':
     807                 :          0 :                         arg_iterations = 1;
     808                 :          0 :                         break;
     809                 :            : 
     810                 :          0 :                 case 'b':
     811                 :          0 :                         arg_batch = true;
     812                 :          0 :                         break;
     813                 :            : 
     814                 :          0 :                 case 'r':
     815                 :          0 :                         arg_raw = true;
     816                 :          0 :                         break;
     817                 :            : 
     818                 :          0 :                 case 'p':
     819                 :          0 :                         arg_order = ORDER_PATH;
     820                 :          0 :                         break;
     821                 :            : 
     822                 :          0 :                 case 't':
     823                 :          0 :                         arg_order = ORDER_TASKS;
     824                 :          0 :                         break;
     825                 :            : 
     826                 :          0 :                 case 'c':
     827                 :          0 :                         arg_order = ORDER_CPU;
     828                 :          0 :                         break;
     829                 :            : 
     830                 :          0 :                 case 'm':
     831                 :          0 :                         arg_order = ORDER_MEMORY;
     832                 :          0 :                         break;
     833                 :            : 
     834                 :          0 :                 case 'i':
     835                 :          0 :                         arg_order = ORDER_IO;
     836                 :          0 :                         break;
     837                 :            : 
     838                 :          0 :                 case ARG_ORDER:
     839         [ #  # ]:          0 :                         if (streq(optarg, "path"))
     840                 :          0 :                                 arg_order = ORDER_PATH;
     841         [ #  # ]:          0 :                         else if (streq(optarg, "tasks"))
     842                 :          0 :                                 arg_order = ORDER_TASKS;
     843         [ #  # ]:          0 :                         else if (streq(optarg, "cpu"))
     844                 :          0 :                                 arg_order = ORDER_CPU;
     845         [ #  # ]:          0 :                         else if (streq(optarg, "memory"))
     846                 :          0 :                                 arg_order = ORDER_MEMORY;
     847         [ #  # ]:          0 :                         else if (streq(optarg, "io"))
     848                 :          0 :                                 arg_order = ORDER_IO;
     849                 :            :                         else
     850         [ #  # ]:          0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     851                 :            :                                                        "Invalid argument to --order=: %s",
     852                 :            :                                                        optarg);
     853                 :          0 :                         break;
     854                 :            : 
     855                 :          0 :                 case 'k':
     856                 :          0 :                         arg_count = COUNT_ALL_PROCESSES;
     857                 :          0 :                         break;
     858                 :            : 
     859                 :          0 :                 case 'P':
     860                 :          0 :                         arg_count = COUNT_USERSPACE_PROCESSES;
     861                 :          0 :                         break;
     862                 :            : 
     863                 :          0 :                 case ARG_RECURSIVE:
     864                 :          0 :                         r = parse_boolean(optarg);
     865         [ #  # ]:          0 :                         if (r < 0)
     866         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to parse --recursive= argument '%s': %m", optarg);
     867                 :            : 
     868                 :          0 :                         arg_recursive = r;
     869                 :          0 :                         arg_recursive_unset = r == 0;
     870                 :          0 :                         break;
     871                 :            : 
     872                 :          0 :                 case 'M':
     873                 :          0 :                         arg_machine = optarg;
     874                 :          0 :                         break;
     875                 :            : 
     876                 :          4 :                 case '?':
     877                 :          4 :                         return -EINVAL;
     878                 :            : 
     879                 :          0 :                 default:
     880                 :          0 :                         assert_not_reached("Unhandled option");
     881                 :            :                 }
     882                 :            : 
     883         [ #  # ]:          0 :         if (optind == argc - 1)
     884                 :          0 :                 arg_root = argv[optind];
     885         [ #  # ]:          0 :         else if (optind < argc)
     886         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     887                 :            :                                        "Too many arguments.");
     888                 :            : 
     889                 :          0 :         return 1;
     890                 :            : }
     891                 :            : 
     892                 :          0 : static const char* counting_what(void) {
     893         [ #  # ]:          0 :         if (arg_count == COUNT_PIDS)
     894                 :          0 :                 return "tasks";
     895         [ #  # ]:          0 :         else if (arg_count == COUNT_ALL_PROCESSES)
     896                 :          0 :                 return "all processes (incl. kernel)";
     897                 :            :         else
     898                 :          0 :                 return "userspace processes (excl. kernel)";
     899                 :            : }
     900                 :            : 
     901                 :          0 : DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(group_hash_ops, char, path_hash_func, path_compare_func, Group, group_free);
     902                 :            : 
     903                 :         16 : static int run(int argc, char *argv[]) {
     904                 :         16 :         _cleanup_hashmap_free_ Hashmap *a = NULL, *b = NULL;
     905                 :         16 :         unsigned iteration = 0;
     906                 :         16 :         usec_t last_refresh = 0;
     907                 :         16 :         bool quit = false, immediate_refresh = false;
     908                 :         16 :         _cleanup_free_ char *root = NULL;
     909                 :            :         CGroupMask mask;
     910                 :            :         int r;
     911                 :            : 
     912                 :         16 :         log_show_color(true);
     913                 :         16 :         log_parse_environment();
     914                 :         16 :         log_open();
     915                 :            : 
     916                 :         16 :         r = parse_argv(argc, argv);
     917         [ +  - ]:         16 :         if (r <= 0)
     918                 :         16 :                 return r;
     919                 :            : 
     920                 :          0 :         r = cg_mask_supported(&mask);
     921         [ #  # ]:          0 :         if (r < 0)
     922         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to determine supported controllers: %m");
     923                 :            : 
     924                 :          0 :         arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES;
     925                 :            : 
     926   [ #  #  #  # ]:          0 :         if (arg_recursive_unset && arg_count == COUNT_PIDS)
     927         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     928                 :            :                                        "Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k.");
     929                 :            : 
     930                 :          0 :         r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root);
     931         [ #  # ]:          0 :         if (r < 0)
     932         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to get root control group path: %m");
     933         [ #  # ]:          0 :         log_debug("CGroup path: %s", root);
     934                 :            : 
     935                 :          0 :         a = hashmap_new(&group_hash_ops);
     936                 :          0 :         b = hashmap_new(&group_hash_ops);
     937   [ #  #  #  # ]:          0 :         if (!a || !b)
     938                 :          0 :                 return log_oom();
     939                 :            : 
     940                 :          0 :         signal(SIGWINCH, columns_lines_cache_reset);
     941                 :            : 
     942         [ #  # ]:          0 :         if (arg_iterations == (unsigned) -1)
     943                 :          0 :                 arg_iterations = on_tty() ? 0 : 1;
     944                 :            : 
     945         [ #  # ]:          0 :         while (!quit) {
     946                 :            :                 usec_t t;
     947                 :            :                 char key;
     948                 :            :                 char h[FORMAT_TIMESPAN_MAX];
     949                 :            : 
     950                 :          0 :                 t = now(CLOCK_MONOTONIC);
     951                 :            : 
     952   [ #  #  #  # ]:          0 :                 if (t >= last_refresh + arg_delay || immediate_refresh) {
     953                 :            : 
     954                 :          0 :                         r = refresh(root, a, b, iteration++);
     955         [ #  # ]:          0 :                         if (r < 0)
     956         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to refresh: %m");
     957                 :            : 
     958                 :          0 :                         hashmap_clear(b);
     959                 :          0 :                         SWAP_TWO(a, b);
     960                 :            : 
     961                 :          0 :                         last_refresh = t;
     962                 :          0 :                         immediate_refresh = false;
     963                 :            :                 }
     964                 :            : 
     965                 :          0 :                 display(b);
     966                 :            : 
     967   [ #  #  #  # ]:          0 :                 if (arg_iterations && iteration >= arg_iterations)
     968                 :          0 :                         break;
     969                 :            : 
     970         [ #  # ]:          0 :                 if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */
     971                 :          0 :                         fputs("\n", stdout);
     972                 :          0 :                 fflush(stdout);
     973                 :            : 
     974         [ #  # ]:          0 :                 if (arg_batch)
     975                 :          0 :                         (void) usleep(last_refresh + arg_delay - t);
     976                 :            :                 else {
     977                 :          0 :                         r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
     978         [ #  # ]:          0 :                         if (r == -ETIMEDOUT)
     979                 :          0 :                                 continue;
     980         [ #  # ]:          0 :                         if (r < 0)
     981         [ #  # ]:          0 :                                 return log_error_errno(r, "Couldn't read key: %m");
     982                 :            :                 }
     983                 :            : 
     984         [ #  # ]:          0 :                 if (on_tty()) { /* TTY: Clear any user keystroke */
     985                 :          0 :                         fputs("\r \r", stdout);
     986                 :          0 :                         fflush(stdout);
     987                 :            :                 }
     988                 :            : 
     989         [ #  # ]:          0 :                 if (arg_batch)
     990                 :          0 :                         continue;
     991                 :            : 
     992   [ #  #  #  #  :          0 :                 switch (key) {
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     993                 :            : 
     994                 :          0 :                 case ' ':
     995                 :          0 :                         immediate_refresh = true;
     996                 :          0 :                         break;
     997                 :            : 
     998                 :          0 :                 case 'q':
     999                 :          0 :                         quit = true;
    1000                 :          0 :                         break;
    1001                 :            : 
    1002                 :          0 :                 case 'p':
    1003                 :          0 :                         arg_order = ORDER_PATH;
    1004                 :          0 :                         break;
    1005                 :            : 
    1006                 :          0 :                 case 't':
    1007                 :          0 :                         arg_order = ORDER_TASKS;
    1008                 :          0 :                         break;
    1009                 :            : 
    1010                 :          0 :                 case 'c':
    1011                 :          0 :                         arg_order = ORDER_CPU;
    1012                 :          0 :                         break;
    1013                 :            : 
    1014                 :          0 :                 case 'm':
    1015                 :          0 :                         arg_order = ORDER_MEMORY;
    1016                 :          0 :                         break;
    1017                 :            : 
    1018                 :          0 :                 case 'i':
    1019                 :          0 :                         arg_order = ORDER_IO;
    1020                 :          0 :                         break;
    1021                 :            : 
    1022                 :          0 :                 case '%':
    1023                 :          0 :                         arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
    1024                 :          0 :                         break;
    1025                 :            : 
    1026                 :          0 :                 case 'k':
    1027         [ #  # ]:          0 :                         arg_count = arg_count != COUNT_ALL_PROCESSES ? COUNT_ALL_PROCESSES : COUNT_PIDS;
    1028                 :          0 :                         fprintf(stdout, "\nCounting: %s.", counting_what());
    1029                 :          0 :                         fflush(stdout);
    1030                 :          0 :                         sleep(1);
    1031                 :          0 :                         break;
    1032                 :            : 
    1033                 :          0 :                 case 'P':
    1034                 :          0 :                         arg_count = arg_count != COUNT_USERSPACE_PROCESSES ? COUNT_USERSPACE_PROCESSES : COUNT_PIDS;
    1035                 :          0 :                         fprintf(stdout, "\nCounting: %s.", counting_what());
    1036                 :          0 :                         fflush(stdout);
    1037                 :          0 :                         sleep(1);
    1038                 :          0 :                         break;
    1039                 :            : 
    1040                 :          0 :                 case 'r':
    1041         [ #  # ]:          0 :                         if (arg_count == COUNT_PIDS)
    1042                 :          0 :                                 fprintf(stdout, "\n\aCannot toggle recursive counting, not available in task counting mode.");
    1043                 :            :                         else {
    1044                 :          0 :                                 arg_recursive = !arg_recursive;
    1045                 :          0 :                                 fprintf(stdout, "\nRecursive process counting: %s", yes_no(arg_recursive));
    1046                 :            :                         }
    1047                 :          0 :                         fflush(stdout);
    1048                 :          0 :                         sleep(1);
    1049                 :          0 :                         break;
    1050                 :            : 
    1051                 :          0 :                 case '+':
    1052         [ #  # ]:          0 :                         if (arg_delay < USEC_PER_SEC)
    1053                 :          0 :                                 arg_delay += USEC_PER_MSEC*250;
    1054                 :            :                         else
    1055                 :          0 :                                 arg_delay += USEC_PER_SEC;
    1056                 :            : 
    1057                 :          0 :                         fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
    1058                 :          0 :                         fflush(stdout);
    1059                 :          0 :                         sleep(1);
    1060                 :          0 :                         break;
    1061                 :            : 
    1062                 :          0 :                 case '-':
    1063         [ #  # ]:          0 :                         if (arg_delay <= USEC_PER_MSEC*500)
    1064                 :          0 :                                 arg_delay = USEC_PER_MSEC*250;
    1065         [ #  # ]:          0 :                         else if (arg_delay < USEC_PER_MSEC*1250)
    1066                 :          0 :                                 arg_delay -= USEC_PER_MSEC*250;
    1067                 :            :                         else
    1068                 :          0 :                                 arg_delay -= USEC_PER_SEC;
    1069                 :            : 
    1070                 :          0 :                         fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
    1071                 :          0 :                         fflush(stdout);
    1072                 :          0 :                         sleep(1);
    1073                 :          0 :                         break;
    1074                 :            : 
    1075                 :          0 :                 case '?':
    1076                 :            :                 case 'h':
    1077                 :            : 
    1078                 :            : #define ON ANSI_HIGHLIGHT
    1079                 :            : #define OFF ANSI_NORMAL
    1080                 :            : 
    1081                 :          0 :                         fprintf(stdout,
    1082                 :            :                                 "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks/procs; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n"
    1083                 :            :                                 "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n"
    1084                 :            :                                 "\t<" ON "P" OFF "> Toggle count userspace processes; <" ON "k" OFF "> Toggle count all processes\n"
    1085                 :            :                                 "\t<" ON "r" OFF "> Count processes recursively; <" ON "q" OFF "> Quit");
    1086                 :          0 :                         fflush(stdout);
    1087                 :          0 :                         sleep(3);
    1088                 :          0 :                         break;
    1089                 :            : 
    1090                 :          0 :                 default:
    1091         [ #  # ]:          0 :                         if (key < ' ')
    1092                 :          0 :                                 fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key);
    1093                 :            :                         else
    1094                 :          0 :                                 fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
    1095                 :          0 :                         fflush(stdout);
    1096                 :          0 :                         sleep(1);
    1097                 :          0 :                         break;
    1098                 :            :                 }
    1099                 :            :         }
    1100                 :            : 
    1101                 :          0 :         return 0;
    1102                 :            : }
    1103                 :            : 
    1104                 :         16 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14