cgtop.c revision 96a6426f30dc9bf3c4dd1f61548c334fa12034df
8a77240a809197c92c0736c431b4b88947a7bac1Christian Maeder/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8a77240a809197c92c0736c431b4b88947a7bac1Christian Maeder
8a77240a809197c92c0736c431b4b88947a7bac1Christian Maeder/***
adea2e45fa61f1097aadc490a0aeaf4831b729ccChristian Maeder This file is part of systemd.
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder Copyright 2012 Lennart Poettering
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
98890889ffb2e8f6f722b00e265a211f13b5a861Corneliu-Claudiu Prodescu systemd is free software; you can redistribute it and/or modify it
2eeec5240b424984e3ee26296da1eeab6c6d739eChristian Maeder under the terms of the GNU Lesser General Public License as published by
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski the Free Software Foundation; either version 2.1 of the License, or
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski (at your option) any later version.
f3a94a197960e548ecd6520bb768cb0d547457bbChristian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder systemd is distributed in the hope that it will be useful, but
0095c7efbddd0ffeed6aaf8ec015346be161d819Till Mossakowski WITHOUT ANY WARRANTY; without even the implied warranty of
adea2e45fa61f1097aadc490a0aeaf4831b729ccChristian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski Lesser General Public License for more details.
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder You should have received a copy of the GNU Lesser General Public License
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder along with systemd; If not, see <http://www.gnu.org/licenses/>.
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder***/
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder#include <errno.h>
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder#include <string.h>
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include <stdlib.h>
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include <stdint.h>
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include <unistd.h>
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include <alloca.h>
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include <getopt.h>
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include <signal.h>
ad270004874ce1d0697fb30d7309f180553bb315Christian Maeder
cf31aaf25d0fe96b0578755e7ee18b732e337343Christian Maeder#include "path-util.h"
ef9e8535c168d3f774d9e74368a2317a9eda5826Christian Maeder#include "terminal-util.h"
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder#include "process-util.h"
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder#include "util.h"
d3c9318c22fcf44d9135a3b2c64f880b9a785babChristian Maeder#include "hashmap.h"
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include "cgroup-util.h"
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include "build.h"
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include "fileio.h"
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include "sd-bus.h"
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder#include "bus-util.h"
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski#include "bus-error.h"
adea2e45fa61f1097aadc490a0aeaf4831b729ccChristian Maeder#include "unit-name.h"
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskitypedef struct Group {
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder char *path;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder bool n_tasks_valid:1;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder bool cpu_valid:1;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder bool memory_valid:1;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder bool io_valid:1;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder uint64_t n_tasks;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski unsigned cpu_iteration;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski nsec_t cpu_usage;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski nsec_t cpu_timestamp;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski double cpu_fraction;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski uint64_t memory;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski unsigned io_iteration;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski uint64_t io_input, io_output;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder nsec_t io_timestamp;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder uint64_t io_input_bps, io_output_bps;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder} Group;
adea2e45fa61f1097aadc490a0aeaf4831b729ccChristian Maeder
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic unsigned arg_depth = 3;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic unsigned arg_iterations = (unsigned) -1;
adea2e45fa61f1097aadc490a0aeaf4831b729ccChristian Maederstatic bool arg_batch = false;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic bool arg_raw = false;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maederstatic usec_t arg_delay = 1*USEC_PER_SEC;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic char* arg_machine = NULL;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskienum {
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski COUNT_PIDS,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski COUNT_USERSPACE_PROCESSES,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski COUNT_ALL_PROCESSES,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski} arg_count = COUNT_PIDS;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maederstatic bool arg_recursive = true;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic enum {
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder ORDER_PATH,
adea2e45fa61f1097aadc490a0aeaf4831b729ccChristian Maeder ORDER_TASKS,
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder ORDER_CPU,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski ORDER_MEMORY,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski ORDER_IO,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski} arg_order = ORDER_CPU;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic enum {
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder CPU_PERCENT,
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder CPU_TIME,
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder} arg_cpu_type = CPU_PERCENT;
e7d2b3903c7b44db432538b0d720c21062c24823Christian Maeder
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic void group_free(Group *g) {
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski assert(g);
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder free(g->path);
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski free(g);
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski}
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maederstatic void group_hashmap_clear(Hashmap *h) {
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski Group *g;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder while ((g = hashmap_steal_first(h)))
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski group_free(g);
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski}
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic void group_hashmap_free(Hashmap *h) {
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski group_hashmap_clear(h);
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu hashmap_free(h);
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu}
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowskistatic const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64_t t) {
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski if (!is_valid)
e7d2b3903c7b44db432538b0d720c21062c24823Christian Maeder return "-";
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (arg_raw) {
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski snprintf(buf, l, "%jd", t);
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski return buf;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski }
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder return format_bytes(buf, l, t);
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski}
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maederstatic int process(
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski const char *controller,
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder const char *path,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski Hashmap *a,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski Hashmap *b,
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder unsigned iteration,
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski Group **ret) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski Group *g;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski int r;
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski
df11e5eab86d8247f58e301d8f0a2c6ecf4c9541Till Mossakowski assert(controller);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder assert(path);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder assert(a);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g = hashmap_get(a, path);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (!g) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g = hashmap_get(b, path);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (!g) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g = new0(Group, 1);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (!g)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return -ENOMEM;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->path = strdup(path);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (!g->path) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder group_free(g);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return -ENOMEM;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder }
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder r = hashmap_put(a, g->path, g);
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder if (r < 0) {
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder group_free(g);
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder }
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder } else {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = hashmap_move_one(a, b, path);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder }
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder }
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder _cleanup_fclose_ FILE *f = NULL;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder pid_t pid;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = cg_enumerate_processes(controller, path, &f);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r == -ENOENT)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return 0;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->n_tasks = 0;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder while (cg_read_pid(f, &pid) > 0) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0)
4d7d53fec6b551333c79da6ae3b8ca2af0a741abChristian Maeder continue;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder g->n_tasks++;
3e8b136f23ed57d40ee617f49bcac37830b58cabChristian Maeder }
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
d3c9318c22fcf44d9135a3b2c64f880b9a785babChristian Maeder if (g->n_tasks > 0)
d3c9318c22fcf44d9135a3b2c64f880b9a785babChristian Maeder g->n_tasks_valid = true;
d3c9318c22fcf44d9135a3b2c64f880b9a785babChristian Maeder
d3c9318c22fcf44d9135a3b2c64f880b9a785babChristian Maeder } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder _cleanup_free_ char *p = NULL, *v = NULL;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = cg_get_path(controller, path, "pids.current", &p);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
cd6e5706893519bfcf24539afa252fcbed5097ddKlaus Luettich
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = read_one_line_file(p, &v);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r == -ENOENT)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return 0;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = safe_atou64(v, &g->n_tasks);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
e7d2b3903c7b44db432538b0d720c21062c24823Christian Maeder if (g->n_tasks > 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->n_tasks_valid = true;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder } else if (streq(controller, "cpuacct") && cg_unified() <= 0) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder _cleanup_free_ char *p = NULL, *v = NULL;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder uint64_t new_usage;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder nsec_t timestamp;
e8eb2b9d68adc3024eb1aa9899b902ed5a3fb460Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = cg_get_path(controller, path, "cpuacct.usage", &p);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = read_one_line_file(p, &v);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r == -ENOENT)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return 0;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
cd6e5706893519bfcf24539afa252fcbed5097ddKlaus Luettich
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = safe_atou64(v, &new_usage);
e7d2b3903c7b44db432538b0d720c21062c24823Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder timestamp = now_nsec(CLOCK_MONOTONIC);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (g->cpu_iteration == iteration - 1 &&
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder (nsec_t) new_usage > g->cpu_usage) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
a7be28e157e9ceeec73a8fd0e642c36ea29d4218Christian Maeder nsec_t x, y;
a7be28e157e9ceeec73a8fd0e642c36ea29d4218Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder x = timestamp - g->cpu_timestamp;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (x < 1)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder x = 1;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder y = (nsec_t) new_usage - g->cpu_usage;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->cpu_fraction = (double) y / (double) x;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->cpu_valid = true;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder }
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->cpu_usage = (nsec_t) new_usage;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->cpu_timestamp = timestamp;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->cpu_iteration = iteration;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder } else if (streq(controller, "memory")) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder _cleanup_free_ char *p = NULL, *v = NULL;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (cg_unified() <= 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu else
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu r = cg_get_path(controller, path, "memory.current", &p);
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = read_one_line_file(p, &v);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r == -ENOENT)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return 0;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return r;
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu r = safe_atou64(v, &g->memory);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
a98fd29a06e80e447af26d898044c23497adbc73Mihai Codescu return r;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (g->memory > 0)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder g->memory_valid = true;
e7d2b3903c7b44db432538b0d720c21062c24823Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder } else if (streq(controller, "blkio") && cg_unified() <= 0) {
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder _cleanup_fclose_ FILE *f = NULL;
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder _cleanup_free_ char *p = NULL;
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder uint64_t wr = 0, rd = 0;
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder nsec_t timestamp;
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder if (r < 0)
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder return r;
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder f = fopen(p, "re");
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder if (!f) {
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder if (errno == ENOENT)
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder return 0;
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder return -errno;
c911a0ec80ca4a178399c68f1e28be4e2bf42fceChristian Maeder }
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder for (;;) {
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder char line[LINE_MAX], *l;
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder uint64_t k, *q;
4ef5e33657aae95850b7e6941f67ac1fb73cd13fChristian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (!fgets(line, sizeof(line), f))
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder break;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder l = strstrip(line);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder l += strcspn(l, WHITESPACE);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder l += strspn(l, WHITESPACE);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (first_word(l, "Read")) {
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder l += 4;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder q = &rd;
e8eb2b9d68adc3024eb1aa9899b902ed5a3fb460Christian Maeder } else if (first_word(l, "Write")) {
e8eb2b9d68adc3024eb1aa9899b902ed5a3fb460Christian Maeder l += 5;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder q = &wr;
e8eb2b9d68adc3024eb1aa9899b902ed5a3fb460Christian Maeder } else
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder continue;
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder l += strspn(l, WHITESPACE);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder r = safe_atou64(l, &k);
09b431a868c79a92ae7c9bd141565f43f9034144Christian Maeder if (r < 0)
cd6e5706893519bfcf24539afa252fcbed5097ddKlaus Luettich continue;
cd6e5706893519bfcf24539afa252fcbed5097ddKlaus Luettich
c40a1fdc8ec6978bd27240d6780d0e0a7b6b0056Dominik Luecke *q += k;
cd6e5706893519bfcf24539afa252fcbed5097ddKlaus Luettich }
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder timestamp = now_nsec(CLOCK_MONOTONIC);
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder if (g->io_iteration == iteration - 1) {
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder uint64_t x, yr, yw;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder x = (uint64_t) (timestamp - g->io_timestamp);
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder if (x < 1)
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder x = 1;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder if (rd > g->io_input)
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder yr = rd - g->io_input;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder else
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder yr = 0;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder if (wr > g->io_output)
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder yw = wr - g->io_output;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder else
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder yw = 0;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder if (yr > 0 || yw > 0) {
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder g->io_input_bps = (yr * 1000000000ULL) / x;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder g->io_output_bps = (yw * 1000000000ULL) / x;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder g->io_valid = true;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder }
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder }
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder g->io_input = rd;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder g->io_output = wr;
556f473448dfcceee22afaa89ed7a364489cdbbbChristian Maeder g->io_timestamp = timestamp;
g->io_iteration = iteration;
}
if (ret)
*ret = g;
return 0;
}
static int refresh_one(
const char *controller,
const char *path,
Hashmap *a,
Hashmap *b,
unsigned iteration,
unsigned depth,
Group **ret) {
_cleanup_closedir_ DIR *d = NULL;
Group *ours;
int r;
assert(controller);
assert(path);
assert(a);
if (depth > arg_depth)
return 0;
r = process(controller, path, a, b, iteration, &ours);
if (r < 0)
return r;
r = cg_enumerate_subgroups(controller, path, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *fn = NULL, *p = NULL;
Group *child = NULL;
r = cg_read_subgroup(d, &fn);
if (r < 0)
return r;
if (r == 0)
break;
p = strjoin(path, "/", fn, NULL);
if (!p)
return -ENOMEM;
path_kill_slashes(p);
r = refresh_one(controller, p, a, b, iteration, depth + 1, &child);
if (r < 0)
return r;
if (arg_recursive &&
IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) &&
child &&
child->n_tasks_valid &&
streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
/* Recursively sum up processes */
if (ours->n_tasks_valid)
ours->n_tasks += child->n_tasks;
else {
ours->n_tasks = child->n_tasks;
ours->n_tasks_valid = true;
}
}
}
if (ret)
*ret = ours;
return 1;
}
static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) {
int r;
assert(a);
r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL);
if (r < 0)
return r;
r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL);
if (r < 0)
return r;
r = refresh_one("memory", root, a, b, iteration, 0, NULL);
if (r < 0)
return r;
r = refresh_one("blkio", root, a, b, iteration, 0, NULL);
if (r < 0)
return r;
r = refresh_one("pids", root, a, b, iteration, 0, NULL);
if (r < 0)
return r;
return 0;
}
static int group_compare(const void*a, const void *b) {
const Group *x = *(Group**)a, *y = *(Group**)b;
if (arg_order != ORDER_TASKS || arg_recursive) {
/* Let's make sure that the parent is always before
* the child. Except when ordering by tasks and
* recursive summing is off, since that is actually
* not accumulative for all children. */
if (path_startswith(y->path, x->path))
return -1;
if (path_startswith(x->path, y->path))
return 1;
}
switch (arg_order) {
case ORDER_PATH:
break;
case ORDER_CPU:
if (arg_cpu_type == CPU_PERCENT) {
if (x->cpu_valid && y->cpu_valid) {
if (x->cpu_fraction > y->cpu_fraction)
return -1;
else if (x->cpu_fraction < y->cpu_fraction)
return 1;
} else if (x->cpu_valid)
return -1;
else if (y->cpu_valid)
return 1;
} else {
if (x->cpu_usage > y->cpu_usage)
return -1;
else if (x->cpu_usage < y->cpu_usage)
return 1;
}
break;
case ORDER_TASKS:
if (x->n_tasks_valid && y->n_tasks_valid) {
if (x->n_tasks > y->n_tasks)
return -1;
else if (x->n_tasks < y->n_tasks)
return 1;
} else if (x->n_tasks_valid)
return -1;
else if (y->n_tasks_valid)
return 1;
break;
case ORDER_MEMORY:
if (x->memory_valid && y->memory_valid) {
if (x->memory > y->memory)
return -1;
else if (x->memory < y->memory)
return 1;
} else if (x->memory_valid)
return -1;
else if (y->memory_valid)
return 1;
break;
case ORDER_IO:
if (x->io_valid && y->io_valid) {
if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
return -1;
else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
return 1;
} else if (x->io_valid)
return -1;
else if (y->io_valid)
return 1;
}
return path_compare(x->path, y->path);
}
#define ON ANSI_HIGHLIGHT_ON
#define OFF ANSI_HIGHLIGHT_OFF
static void display(Hashmap *a) {
Iterator i;
Group *g;
Group **array;
signed path_columns;
unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 3; /* 3 for ellipsize() to work properly */
char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
assert(a);
/* Set cursor to top left corner and clear screen */
if (on_tty())
fputs("\033[H"
"\033[2J", stdout);
array = alloca(sizeof(Group*) * hashmap_size(a));
HASHMAP_FOREACH(g, a, i)
if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
array[n++] = g;
qsort_safe(array, n, sizeof(Group*), group_compare);
/* Find the longest names in one run */
for (j = 0; j < n; j++) {
unsigned cputlen, pathtlen;
format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
cputlen = strlen(buffer);
maxtcpu = MAX(maxtcpu, cputlen);
pathtlen = strlen(array[j]->path);
maxtpath = MAX(maxtpath, pathtlen);
}
if (arg_cpu_type == CPU_PERCENT)
snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
else
snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
rows = lines();
if (rows <= 10)
rows = 10;
if (on_tty()) {
path_columns = columns() - 36 - strlen(buffer);
if (path_columns < 10)
path_columns = 10;
printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group",
arg_order == ORDER_PATH ? OFF : "",
arg_order == ORDER_TASKS ? ON : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+",
arg_order == ORDER_TASKS ? OFF : "",
arg_order == ORDER_CPU ? ON : "", buffer,
arg_order == ORDER_CPU ? OFF : "",
arg_order == ORDER_MEMORY ? ON : "", "Memory",
arg_order == ORDER_MEMORY ? OFF : "",
arg_order == ORDER_IO ? ON : "", "Input/s",
arg_order == ORDER_IO ? OFF : "",
arg_order == ORDER_IO ? ON : "", "Output/s",
arg_order == ORDER_IO ? OFF : "");
} else
path_columns = maxtpath;
for (j = 0; j < n; j++) {
_cleanup_free_ char *ellipsized = NULL;
const char *path;
if (on_tty() && j + 5 > rows)
break;
g = array[j];
path = isempty(g->path) ? "/" : g->path;
ellipsized = ellipsize(path, path_columns, 33);
printf("%-*s", path_columns, ellipsized ?: path);
if (g->n_tasks_valid)
printf(" %7" PRIu64, g->n_tasks);
else
fputs(" -", stdout);
if (arg_cpu_type == CPU_PERCENT) {
if (g->cpu_valid)
printf(" %6.1f", g->cpu_fraction*100);
else
fputs(" -", stdout);
} else
printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory));
printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps));
printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_output_bps));
putchar('\n');
}
}
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Show top control groups by their resource usage.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -p --order=path Order by path\n"
" -t --order=tasks Order by number of tasks/processes\n"
" -c --order=cpu Order by CPU load (default)\n"
" -m --order=memory Order by memory load\n"
" -i --order=io Order by IO load\n"
" -r --raw Provide raw (not human-readable) numbers\n"
" --cpu=percentage Show CPU usage as percentage (default)\n"
" --cpu=time Show CPU usage as time\n"
" -P Count userspace processes instead of tasks (excl. kernel)\n"
" -k Count all processes instead of tasks (incl. kernel)\n"
" --recursive=BOOL Sum up process count recursively\n"
" -d --delay=DELAY Delay between updates\n"
" -n --iterations=N Run for N iterations before exiting\n"
" -b --batch Run in batch mode, accepting no input\n"
" --depth=DEPTH Maximum traversal depth (default: %u)\n"
" -M --machine= Show container\n"
, program_invocation_short_name, arg_depth);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_DEPTH,
ARG_CPU_TYPE,
ARG_ORDER,
ARG_RECURSIVE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "delay", required_argument, NULL, 'd' },
{ "iterations", required_argument, NULL, 'n' },
{ "batch", no_argument, NULL, 'b' },
{ "raw", no_argument, NULL, 'r' },
{ "depth", required_argument, NULL, ARG_DEPTH },
{ "cpu", optional_argument, NULL, ARG_CPU_TYPE },
{ "order", required_argument, NULL, ARG_ORDER },
{ "recursive", required_argument, NULL, ARG_RECURSIVE },
{ "machine", required_argument, NULL, 'M' },
{}
};
bool recursive_unset = false;
int c, r;
assert(argc >= 1);
assert(argv);
while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:", options, NULL)) >= 0)
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case ARG_CPU_TYPE:
if (optarg) {
if (streq(optarg, "time"))
arg_cpu_type = CPU_TIME;
else if (streq(optarg, "percentage"))
arg_cpu_type = CPU_PERCENT;
else {
log_error("Unknown argument to --cpu=: %s", optarg);
return -EINVAL;
}
} else
arg_cpu_type = CPU_TIME;
break;
case ARG_DEPTH:
r = safe_atou(optarg, &arg_depth);
if (r < 0) {
log_error("Failed to parse depth parameter.");
return -EINVAL;
}
break;
case 'd':
r = parse_sec(optarg, &arg_delay);
if (r < 0 || arg_delay <= 0) {
log_error("Failed to parse delay parameter.");
return -EINVAL;
}
break;
case 'n':
r = safe_atou(optarg, &arg_iterations);
if (r < 0) {
log_error("Failed to parse iterations parameter.");
return -EINVAL;
}
break;
case 'b':
arg_batch = true;
break;
case 'r':
arg_raw = true;
break;
case 'p':
arg_order = ORDER_PATH;
break;
case 't':
arg_order = ORDER_TASKS;
break;
case 'c':
arg_order = ORDER_CPU;
break;
case 'm':
arg_order = ORDER_MEMORY;
break;
case 'i':
arg_order = ORDER_IO;
break;
case ARG_ORDER:
if (streq(optarg, "path"))
arg_order = ORDER_PATH;
else if (streq(optarg, "tasks"))
arg_order = ORDER_TASKS;
else if (streq(optarg, "cpu"))
arg_order = ORDER_CPU;
else if (streq(optarg, "memory"))
arg_order = ORDER_MEMORY;
else if (streq(optarg, "io"))
arg_order = ORDER_IO;
else {
log_error("Invalid argument to --order=: %s", optarg);
return -EINVAL;
}
break;
case 'k':
arg_count = COUNT_ALL_PROCESSES;
break;
case 'P':
arg_count = COUNT_USERSPACE_PROCESSES;
break;
case ARG_RECURSIVE:
r = parse_boolean(optarg);
if (r < 0) {
log_error("Failed to parse --recursive= argument: %s", optarg);
return r;
}
arg_recursive = r;
recursive_unset = r == 0;
break;
case 'M':
arg_machine = optarg;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
if (optind < argc) {
log_error("Too many arguments.");
return -EINVAL;
}
if (recursive_unset && arg_count == COUNT_PIDS) {
log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k.");
return -EINVAL;
}
return 1;
}
static const char* counting_what(void) {
if (arg_count == COUNT_PIDS)
return "tasks";
else if (arg_count == COUNT_ALL_PROCESSES)
return "all processes (incl. kernel)";
else
return "userspace processes (excl. kernel)";
}
static int get_cgroup_root(char **ret) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
_cleanup_free_ char *unit = NULL, *path = NULL;
const char *m;
int r;
if (!arg_machine) {
r = cg_get_root_path(ret);
if (r < 0)
return log_error_errno(r, "Failed to get root control group path: %m");
return 0;
}
m = strjoina("/run/systemd/machines/", arg_machine);
r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
if (r < 0)
return log_error_errno(r, "Failed to load machine data: %m");
path = unit_dbus_path_from_name(unit);
if (!path)
return log_oom();
r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
if (r < 0)
return log_error_errno(r, "Failed to create bus connection: %m");
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
path,
unit_dbus_interface_from_name(unit),
"ControlGroup",
&error,
ret);
if (r < 0)
return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r));
return 0;
}
int main(int argc, char *argv[]) {
int r;
Hashmap *a = NULL, *b = NULL;
unsigned iteration = 0;
usec_t last_refresh = 0;
bool quit = false, immediate_refresh = false;
_cleanup_free_ char *root = NULL;
CGroupMask mask;
log_parse_environment();
log_open();
r = cg_mask_supported(&mask);
if (r < 0) {
log_error_errno(r, "Failed to determine supported controllers: %m");
goto finish;
}
arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES;
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
r = get_cgroup_root(&root);
if (r < 0) {
log_error_errno(r, "Failed to get root control group path: %m");
goto finish;
}
a = hashmap_new(&string_hash_ops);
b = hashmap_new(&string_hash_ops);
if (!a || !b) {
r = log_oom();
goto finish;
}
signal(SIGWINCH, columns_lines_cache_reset);
if (arg_iterations == (unsigned) -1)
arg_iterations = on_tty() ? 0 : 1;
while (!quit) {
Hashmap *c;
usec_t t;
char key;
char h[FORMAT_TIMESPAN_MAX];
t = now(CLOCK_MONOTONIC);
if (t >= last_refresh + arg_delay || immediate_refresh) {
r = refresh(root, a, b, iteration++);
if (r < 0) {
log_error_errno(r, "Failed to refresh: %m");
goto finish;
}
group_hashmap_clear(b);
c = a;
a = b;
b = c;
last_refresh = t;
immediate_refresh = false;
}
display(b);
if (arg_iterations && iteration >= arg_iterations)
break;
if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */
fputs("\n", stdout);
fflush(stdout);
if (arg_batch)
(void) usleep(last_refresh + arg_delay - t);
else {
r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
if (r == -ETIMEDOUT)
continue;
if (r < 0) {
log_error_errno(r, "Couldn't read key: %m");
goto finish;
}
}
if (on_tty()) { /* TTY: Clear any user keystroke */
fputs("\r \r", stdout);
fflush(stdout);
}
if (arg_batch)
continue;
switch (key) {
case ' ':
immediate_refresh = true;
break;
case 'q':
quit = true;
break;
case 'p':
arg_order = ORDER_PATH;
break;
case 't':
arg_order = ORDER_TASKS;
break;
case 'c':
arg_order = ORDER_CPU;
break;
case 'm':
arg_order = ORDER_MEMORY;
break;
case 'i':
arg_order = ORDER_IO;
break;
case '%':
arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
break;
case 'k':
arg_count = arg_count != COUNT_ALL_PROCESSES ? COUNT_ALL_PROCESSES : COUNT_PIDS;
fprintf(stdout, "\nCounting: %s.", counting_what());
fflush(stdout);
sleep(1);
break;
case 'P':
arg_count = arg_count != COUNT_USERSPACE_PROCESSES ? COUNT_USERSPACE_PROCESSES : COUNT_PIDS;
fprintf(stdout, "\nCounting: %s.", counting_what());
fflush(stdout);
sleep(1);
break;
case 'r':
if (arg_count == COUNT_PIDS)
fprintf(stdout, "\n\aCannot toggle recursive counting, not available in task counting mode.");
else {
arg_recursive = !arg_recursive;
fprintf(stdout, "\nRecursive process counting: %s", yes_no(arg_recursive));
}
fflush(stdout);
sleep(1);
break;
case '+':
if (arg_delay < USEC_PER_SEC)
arg_delay += USEC_PER_MSEC*250;
else
arg_delay += USEC_PER_SEC;
fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
fflush(stdout);
sleep(1);
break;
case '-':
if (arg_delay <= USEC_PER_MSEC*500)
arg_delay = USEC_PER_MSEC*250;
else if (arg_delay < USEC_PER_MSEC*1250)
arg_delay -= USEC_PER_MSEC*250;
else
arg_delay -= USEC_PER_SEC;
fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
fflush(stdout);
sleep(1);
break;
case '?':
case 'h':
fprintf(stdout,
"\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"
"\t<" ON "P" OFF "> Toggle count userspace processes; <" ON "k" OFF "> Toggle count all processes\n"
"\t<" ON "r" OFF "> Count processes recursively; <" ON "q" OFF "> Quit");
fflush(stdout);
sleep(3);
break;
default:
if (key < ' ')
fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key);
else
fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
fflush(stdout);
sleep(1);
break;
}
}
r = 0;
finish:
group_hashmap_free(a);
group_hashmap_free(b);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}