-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
#include <alloca.h>
+#include <errno.h>
#include <getopt.h>
#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sd-bus.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "terminal-util.h"
#include "process-util.h"
+#include "stdio-util.h"
+#include "terminal-util.h"
+#include "unit-name.h"
#include "util.h"
-#include "hashmap.h"
-#include "cgroup-util.h"
-#include "build.h"
-#include "fileio.h"
typedef struct Group {
char *path;
static bool arg_batch = false;
static bool arg_raw = false;
static usec_t arg_delay = 1*USEC_PER_SEC;
+static char* arg_machine = NULL;
+static char* arg_root = NULL;
+static bool arg_recursive = true;
-enum {
+static enum {
COUNT_PIDS,
COUNT_USERSPACE_PROCESSES,
COUNT_ALL_PROCESSES,
} arg_count = COUNT_PIDS;
-static bool arg_recursive = true;
static enum {
ORDER_PATH,
if (g->memory > 0)
g->memory_valid = true;
- } else if (streq(controller, "blkio") && cg_unified() <= 0) {
+ } else if ((streq(controller, "io") && cg_unified() > 0) ||
+ (streq(controller, "blkio") && cg_unified() <= 0)) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
+ bool unified = cg_unified() > 0;
uint64_t wr = 0, rd = 0;
nsec_t timestamp;
- r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
+ r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p);
if (r < 0)
return r;
if (!fgets(line, sizeof(line), f))
break;
+ /* Trim and skip the device */
l = strstrip(line);
l += strcspn(l, WHITESPACE);
l += strspn(l, WHITESPACE);
- if (first_word(l, "Read")) {
- l += 4;
- q = &rd;
- } else if (first_word(l, "Write")) {
- l += 5;
- q = ≀
- } else
- continue;
-
- l += strspn(l, WHITESPACE);
- r = safe_atou64(l, &k);
- if (r < 0)
- continue;
+ if (unified) {
+ while (!isempty(l)) {
+ if (sscanf(l, "rbytes=%" SCNu64, &k))
+ rd += k;
+ else if (sscanf(l, "wbytes=%" SCNu64, &k))
+ wr += k;
- *q += k;
+ l += strcspn(l, WHITESPACE);
+ l += strspn(l, WHITESPACE);
+ }
+ } else {
+ if (first_word(l, "Read")) {
+ l += 4;
+ q = &rd;
+ } else if (first_word(l, "Write")) {
+ l += 5;
+ q = ≀
+ } else
+ continue;
+
+ l += strspn(l, WHITESPACE);
+ r = safe_atou64(l, &k);
+ if (r < 0)
+ continue;
+
+ *q += k;
+ }
}
timestamp = now_nsec(CLOCK_MONOTONIC);
Group **ret) {
_cleanup_closedir_ DIR *d = NULL;
- Group *ours;
+ Group *ours = NULL;
int r;
assert(controller);
if (r < 0)
return r;
r = refresh_one("memory", root, a, b, iteration, 0, NULL);
+ if (r < 0)
+ return r;
+ r = refresh_one("io", root, a, b, iteration, 0, NULL);
if (r < 0)
return r;
r = refresh_one("blkio", root, a, b, iteration, 0, NULL);
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;
assert(a);
- /* Set cursor to top left corner and clear screen */
- if (on_tty())
- fputs("\033[H"
- "\033[2J", stdout);
+ if (!terminal_is_dumb())
+ fputs(ANSI_HOME_CLEAR, stdout);
array = alloca(sizeof(Group*) * hashmap_size(a));
}
if (arg_cpu_type == CPU_PERCENT)
- snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
+ xsprintf(buffer, "%6s", "%CPU");
else
- snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
+ xsprintf(buffer, "%*s", maxtcpu, "CPU Time");
rows = lines();
if (rows <= 10)
rows = 10;
if (on_tty()) {
+ const char *on, *off;
+
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 : "");
+ on = ansi_highlight_underline();
+ off = ansi_underline();
+
+ printf("%s%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s%s\n",
+ ansi_underline(),
+ 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 : "",
+ ansi_normal());
} else
path_columns = maxtpath;
_cleanup_free_ char *ellipsized = NULL;
const char *path;
- if (on_tty() && j + 5 > rows)
+ if (on_tty() && j + 6 > rows)
break;
g = array[j];
}
static void help(void) {
- printf("%s [OPTIONS...]\n\n"
+ printf("%s [OPTIONS...] [CGROUP]\n\n"
"Show top control groups by their resource usage.\n\n"
" -h --help Show this help\n"
" --version Show package version\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);
}
{ "cpu", optional_argument, NULL, ARG_CPU_TYPE },
{ "order", required_argument, NULL, ARG_ORDER },
{ "recursive", required_argument, NULL, ARG_RECURSIVE },
+ { "machine", required_argument, NULL, 'M' },
{}
};
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hptcmin:brd:kP", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:", options, NULL)) >= 0)
switch (c) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_CPU_TYPE:
if (optarg) {
recursive_unset = r == 0;
break;
+ case 'M':
+ arg_machine = optarg;
+ break;
+
case '?':
return -EINVAL;
assert_not_reached("Unhandled option");
}
- if (optind < argc) {
+ if (optind == argc-1) {
+ if (arg_machine) {
+ log_error("Specifying a control group path together with the -M option is not allowed");
+ return -EINVAL;
+ }
+ arg_root = argv[optind];
+ } else if (optind < argc) {
log_error("Too many arguments.");
return -EINVAL;
}
return "userspace processes (excl. kernel)";
}
+static int get_cgroup_root(char **ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_free_ char *unit = NULL, *path = NULL;
+ const char *m;
+ int r;
+
+ if (arg_root) {
+ char *aux;
+
+ aux = strdup(arg_root);
+ if (!aux)
+ return log_oom();
+
+ *ret = aux;
+ return 0;
+ }
+
+ 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_connect_transport_systemd(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;
if (r <= 0)
goto finish;
- r = cg_get_root_path(&root);
+ r = get_cgroup_root(&root);
if (r < 0) {
log_error_errno(r, "Failed to get root control group path: %m");
goto finish;
case '?':
case 'h':
+
+#define ON ANSI_HIGHLIGHT
+#define OFF ANSI_NORMAL
+
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"