cgtop.c revision b5efdb8af40ea759a1ea584c1bc44ecc81dd00ce
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering This file is part of systemd.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2012 Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is free software; you can redistribute it and/or modify it
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering under the terms of the GNU Lesser General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is distributed in the hope that it will be useful, but
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Lesser General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering You should have received a copy of the GNU Lesser General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringtypedef struct Group {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic unsigned arg_iterations = (unsigned) -1;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic bool arg_batch = false;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic bool arg_raw = false;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic bool arg_recursive = true;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void group_hashmap_clear(Hashmap *h) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void group_hashmap_free(Hashmap *h) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64_t t) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = cg_enumerate_processes(controller, path, &f);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_free_ char *p = NULL, *v = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = cg_get_path(controller, path, "pids.current", &p);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering } else if (streq(controller, "cpuacct") && cg_unified() <= 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_free_ char *p = NULL, *v = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = cg_get_path(controller, path, "cpuacct.usage", &p);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering g->cpu_fraction = (double) y / (double) x;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering } else if (streq(controller, "memory")) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering _cleanup_free_ char *p = NULL, *v = NULL;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = cg_get_path(controller, path, "memory.current", &p);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering } else if (streq(controller, "blkio") && cg_unified() <= 0) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering x = (uint64_t) (timestamp - g->io_timestamp);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering g->io_input_bps = (yr * 1000000000ULL) / x;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering g->io_output_bps = (yw * 1000000000ULL) / x;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = process(controller, path, a, b, iteration, &ours);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = cg_enumerate_subgroups(controller, path, &d);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_free_ char *fn = NULL, *p = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = refresh_one(controller, p, a, b, iteration, depth + 1, &child);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) &&
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Recursively sum up processes */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = refresh_one("memory", root, a, b, iteration, 0, NULL);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = refresh_one("blkio", root, a, b, iteration, 0, NULL);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = refresh_one("pids", root, a, b, iteration, 0, NULL);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int group_compare(const void*a, const void *b) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering const Group *x = *(Group**)a, *y = *(Group**)b;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (arg_order != ORDER_TASKS || arg_recursive) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Let's make sure that the parent is always before
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * the child. Except when ordering by tasks and
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * recursive summing is off, since that is actually
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * not accumulative for all children. */
switch (arg_order) {
case ORDER_PATH:
case ORDER_CPU:
} else if (x->cpu_valid)
else if (y->cpu_valid)
case ORDER_TASKS:
} else if (x->n_tasks_valid)
else if (y->n_tasks_valid)
case ORDER_MEMORY:
} else if (x->memory_valid)
else if (y->memory_valid)
case ORDER_IO:
} else if (x->io_valid)
else if (y->io_valid)
Iterator i;
Group *g;
signed path_columns;
assert(a);
if (on_tty())
HASHMAP_FOREACH(g, a, i)
array[n++] = g;
if (on_tty()) {
arg_order == ORDER_TASKS ? on : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+",
ansi_normal());
const char *path;
g = array[j];
if (g->n_tasks_valid)
if (g->cpu_valid)
printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
static void help(void) {
bool recursive_unset = false;
help();
case ARG_VERSION:
return version();
case ARG_CPU_TYPE:
if (optarg) {
return -EINVAL;
case ARG_DEPTH:
return -EINVAL;
if (r < 0 || arg_delay <= 0) {
return -EINVAL;
return -EINVAL;
arg_batch = true;
arg_raw = true;
case ARG_ORDER:
return -EINVAL;
case ARG_RECURSIVE:
arg_recursive = r;
recursive_unset = r == 0;
return -EINVAL;
return -EINVAL;
log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k.");
return -EINVAL;
static const char* counting_what(void) {
if (!arg_machine) {
if (!path)
return log_oom();
bus,
"org.freedesktop.systemd1",
path,
&error,
ret);
return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r));
unsigned iteration = 0;
log_open();
goto finish;
goto finish;
goto finish;
r = log_oom();
goto finish;
while (!quit) {
Hashmap *c;
usec_t t;
char key;
char h[FORMAT_TIMESPAN_MAX];
goto finish;
last_refresh = t;
immediate_refresh = false;
display(b);
if (arg_batch)
if (r == -ETIMEDOUT)
goto finish;
if (arg_batch)
switch (key) {
immediate_refresh = true;
quit = true;
"\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"
"\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n"