along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#define __STDC_FORMAT_MACROS
#include <errno.h>
#include <string.h>
#include <stdlib.h>
+#include <stdint.h>
#include <unistd.h>
#include <alloca.h>
#include <getopt.h>
#include "hashmap.h"
#include "cgroup-util.h"
#include "build.h"
+#include "fileio.h"
typedef struct Group {
char *path;
ORDER_IO
} arg_order = ORDER_CPU;
+static enum {
+ CPU_PERCENT,
+ CPU_TIME,
+} arg_cpu_type = CPU_PERCENT;
+
static void group_free(Group *g) {
assert(g);
static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
Group *g;
int r;
- FILE *f;
+ FILE *f = NULL;
pid_t pid;
unsigned n;
/* Regardless which controller, let's find the maximum number
* of processes in any of it */
- r = cg_enumerate_tasks(controller, path, &f);
+ r = cg_enumerate_processes(controller, path, &f);
if (r < 0)
return r;
return 1;
if (arg_order == ORDER_CPU) {
- if (x->cpu_valid && y->cpu_valid) {
-
- if (x->cpu_fraction > y->cpu_fraction)
+ 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 (x->cpu_fraction < y->cpu_fraction)
+ else if (y->cpu_valid)
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;
+ }
}
if (arg_order == ORDER_TASKS) {
return strcmp(x->path, y->path);
}
+#define ON ANSI_HIGHLIGHT_ON
+#define OFF ANSI_HIGHLIGHT_OFF
+
static int display(Hashmap *a) {
Iterator i;
Group *g;
Group **array;
- unsigned rows, path_columns, n = 0, j;
+ signed path_columns;
+ unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 0;
+ char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
assert(a);
/* Set cursor to top left corner and clear screen */
- fputs("\033[H"
- "\033[2J", stdout);
+ if (on_tty())
+ fputs("\033[H"
+ "\033[2J", stdout);
array = alloca(sizeof(Group*) * hashmap_size(a));
if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
array[n++] = g;
- qsort(array, n, sizeof(Group*), group_compare);
-
- rows = fd_lines(STDOUT_FILENO);
- if (rows <= 0)
- rows = 25;
-
- path_columns = columns() - 42;
- if (path_columns < 10)
- path_columns = 10;
-
- printf("%s%-*s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
- arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_ON : "", path_columns, "Path",
- arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_ON : "", "Tasks",
- arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_ON : "", "%CPU",
- arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory",
- arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Input/s",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Output/s",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "");
+ 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), (nsec_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, "Path",
+ arg_order == ORDER_PATH ? OFF : "",
+ arg_order == ORDER_TASKS ? ON : "", "Tasks",
+ 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++) {
char *p;
- char m[FORMAT_BYTES_MAX];
- if (j + 5 > rows)
+ if (on_tty() && j + 5 > rows)
break;
g = array[j];
else
fputs(" -", stdout);
- if (g->cpu_valid)
- printf(" %6.1f", g->cpu_fraction*100);
- 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), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
if (g->memory_valid)
- printf(" %8s", format_bytes(m, sizeof(m), g->memory));
+ printf(" %8s", format_bytes(buffer, sizeof(buffer), g->memory));
else
fputs(" -", stdout);
if (g->io_valid) {
printf(" %8s",
- format_bytes(m, sizeof(m), g->io_input_bps));
+ format_bytes(buffer, sizeof(buffer), g->io_input_bps));
printf(" %8s",
- format_bytes(m, sizeof(m), g->io_output_bps));
+ format_bytes(buffer, sizeof(buffer), g->io_output_bps));
} else
fputs(" - -", stdout);
}
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"
" -c Order by CPU load\n"
" -m Order by memory load\n"
" -i Order by IO load\n"
- " -d --delay=DELAY Specify delay\n"
+ " --cpu[=TYPE] Show CPU usage as time or percentage (default)\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: 2)\n",
- program_invocation_short_name);
-}
-
-static void version(void) {
- puts(PACKAGE_STRING " cgtop");
+ " --depth=DEPTH Maximum traversal depth (default: %u)\n"
+ , program_invocation_short_name, arg_depth);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_DEPTH,
+ ARG_CPU_TYPE
};
static const struct option options[] = {
{ "iterations", required_argument, NULL, 'n' },
{ "batch", no_argument, NULL, 'b' },
{ "depth", required_argument, NULL, ARG_DEPTH },
- { NULL, 0, NULL, 0 }
+ { "cpu", optional_argument, NULL, ARG_CPU_TYPE},
+ {}
};
int c;
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hptcmin:bd:", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "hptcmin:bd:", options, NULL)) >= 0)
switch (c) {
return 0;
case ARG_VERSION:
- version();
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
return 0;
+ case ARG_CPU_TYPE:
+ if (optarg) {
+ if (strcmp(optarg, "time") == 0)
+ arg_cpu_type = CPU_TIME;
+ else if (strcmp(optarg, "percentage") == 0)
+ arg_cpu_type = CPU_PERCENT;
+ else
+ return -EINVAL;
+ }
+ break;
+
case ARG_DEPTH:
r = safe_atou(optarg, &arg_depth);
if (r < 0) {
break;
case 'd':
- r = parse_usec(optarg, &arg_delay);
+ r = parse_sec(optarg, &arg_delay);
if (r < 0 || arg_delay <= 0) {
log_error("Failed to parse delay parameter.");
return -EINVAL;
return -EINVAL;
default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
+ assert_not_reached("Unhandled option");
}
- }
if (optind < argc) {
log_error("Too many arguments.");
if (r <= 0)
goto finish;
- a = hashmap_new(string_hash_func, string_compare_func);
- b = hashmap_new(string_hash_func, string_compare_func);
+ a = hashmap_new(&string_hash_ops);
+ b = hashmap_new(&string_hash_ops);
if (!a || !b) {
r = log_oom();
goto finish;
}
- signal(SIGWINCH, columns_cache_reset);
+ signal(SIGWINCH, columns_lines_cache_reset);
+
+ if (!on_tty())
+ arg_iterations = 1;
while (!quit) {
Hashmap *c;
arg_order = ORDER_IO;
break;
+ case '%':
+ arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
+ 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));
+ fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
fflush(stdout);
sleep(1);
break;
else
arg_delay -= USEC_PER_SEC;
- fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
+ 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<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n"
- "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh");
+ "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n"
+ "\t<" ON "+" OFF "> Increase delay; <" ON "-" OFF "> Decrease delay; <" ON "%%" OFF "> Toggle time\n"
+ "\t<" ON "q" OFF "> Quit; <" ON "SPACE" OFF "> Refresh");
fflush(stdout);
sleep(3);
break;
}
}
- log_info("Exiting.");
-
r = 0;
finish: