mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-14 09:18:23 +00:00
port lxc-top from lua to C for wider availability
- keep but rename the lua version as an example of how to use the lua API - got rid of the fairly useless --max argument Signed-off-by: Dwight Engen <dwight.engen@oracle.com> Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
3cd988ccdb
commit
7dc6f6e279
@ -47,7 +47,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>lxc-top</command>
|
<command>lxc-top</command>
|
||||||
<arg choice="opt">--help</arg>
|
<arg choice="opt">--help</arg>
|
||||||
<arg choice="opt">--max <replaceable>count</replaceable></arg>
|
|
||||||
<arg choice="opt">--delay <replaceable>delay</replaceable></arg>
|
<arg choice="opt">--delay <replaceable>delay</replaceable></arg>
|
||||||
<arg choice="opt">--sort <replaceable>sortby</replaceable></arg>
|
<arg choice="opt">--sort <replaceable>sortby</replaceable></arg>
|
||||||
<arg choice="opt">--reverse</arg>
|
<arg choice="opt">--reverse</arg>
|
||||||
@ -60,9 +59,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
<command>lxc-top</command> displays container statistics. The output
|
<command>lxc-top</command> displays container statistics. The output
|
||||||
is updated every <replaceable>delay</replaceable> seconds, and is
|
is updated every <replaceable>delay</replaceable> seconds, and is
|
||||||
ordered according to the <replaceable>sortby</replaceable> value
|
ordered according to the <replaceable>sortby</replaceable> value
|
||||||
given. Specifying <replaceable>count</replaceable> will limit the
|
given. <command>lxc-top</command> will display as many containers as
|
||||||
number of containers displayed, otherwise <command>lxc-top</command>
|
can fit in your terminal. Press 'q' to quit. Press one of the sort
|
||||||
will display as many containers as can fit in your terminal.
|
key letters to sort by that statistic. Pressing a sort key letter a
|
||||||
|
second time reverses the sort order.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
@ -70,18 +70,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
<title>Options</title>
|
<title>Options</title>
|
||||||
<variablelist>
|
<variablelist>
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term>
|
|
||||||
<option><optional>-m, --max <replaceable>count</replaceable></optional></option>
|
|
||||||
</term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
Limit the number of containers displayed to
|
|
||||||
<replaceable>count</replaceable>.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<option><optional>-d, --delay <replaceable>delay</replaceable></optional></option>
|
<option><optional>-d, --delay <replaceable>delay</replaceable></optional></option>
|
||||||
@ -89,9 +77,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Amount of time in seconds to delay between screen updates.
|
Amount of time in seconds to delay between screen updates.
|
||||||
This can be specified as less than a second by giving a
|
The default is 3 seconds.
|
||||||
rational number, for example 0.5 for a half second delay. The
|
|
||||||
default is 3 seconds.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -103,7 +89,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
<para>
|
<para>
|
||||||
Sort the containers by name, cpu use, or memory use. The
|
Sort the containers by name, cpu use, or memory use. The
|
||||||
<replaceable>sortby</replaceable> argument should be one of
|
<replaceable>sortby</replaceable> argument should be one of
|
||||||
the letters n,c,d,m,k to sort by name, cpu use, disk I/O, memory,
|
the letters n,c,b,m,k to sort by name, cpu use, block I/O, memory,
|
||||||
or kernel memory use respectively. The default is 'n'.
|
or kernel memory use respectively. The default is 'n'.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
@ -165,7 +165,7 @@ bin_SCRIPTS = lxc-checkconfig
|
|||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
lxc-device \
|
lxc-device \
|
||||||
lxc-ls \
|
lxc-ls \
|
||||||
lxc-top \
|
lxc-top.lua \
|
||||||
lxc.net \
|
lxc.net \
|
||||||
lxc-restore-net
|
lxc-restore-net
|
||||||
|
|
||||||
@ -177,10 +177,6 @@ else
|
|||||||
bin_SCRIPTS += legacy/lxc-ls
|
bin_SCRIPTS += legacy/lxc-ls
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_LUA
|
|
||||||
bin_SCRIPTS += lxc-top
|
|
||||||
endif
|
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
lxc-attach \
|
lxc-attach \
|
||||||
lxc-autostart \
|
lxc-autostart \
|
||||||
@ -198,6 +194,7 @@ bin_PROGRAMS = \
|
|||||||
lxc-snapshot \
|
lxc-snapshot \
|
||||||
lxc-start \
|
lxc-start \
|
||||||
lxc-stop \
|
lxc-stop \
|
||||||
|
lxc-top \
|
||||||
lxc-unfreeze \
|
lxc-unfreeze \
|
||||||
lxc-unshare \
|
lxc-unshare \
|
||||||
lxc-usernsexec \
|
lxc-usernsexec \
|
||||||
@ -231,6 +228,7 @@ lxc_monitord_SOURCES = lxc_monitord.c
|
|||||||
lxc_clone_SOURCES = lxc_clone.c
|
lxc_clone_SOURCES = lxc_clone.c
|
||||||
lxc_start_SOURCES = lxc_start.c
|
lxc_start_SOURCES = lxc_start.c
|
||||||
lxc_stop_SOURCES = lxc_stop.c
|
lxc_stop_SOURCES = lxc_stop.c
|
||||||
|
lxc_top_SOURCES = lxc_top.c
|
||||||
lxc_unfreeze_SOURCES = lxc_unfreeze.c
|
lxc_unfreeze_SOURCES = lxc_unfreeze.c
|
||||||
lxc_unshare_SOURCES = lxc_unshare.c
|
lxc_unshare_SOURCES = lxc_unshare.c
|
||||||
lxc_wait_SOURCES = lxc_wait.c
|
lxc_wait_SOURCES = lxc_wait.c
|
||||||
|
511
src/lxc/lxc_top.c
Normal file
511
src/lxc/lxc_top.c
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
/*
|
||||||
|
* lxc: linux Container library
|
||||||
|
*
|
||||||
|
* Copyright © 2014 Oracle.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Dwight Engen <dwight.engen@oracle.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <lxc/lxccontainer.h>
|
||||||
|
|
||||||
|
#include "arguments.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "lxc.h"
|
||||||
|
#include "mainloop.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
lxc_log_define(lxc_top_ui, lxc);
|
||||||
|
|
||||||
|
#define USER_HZ 100
|
||||||
|
#define ESC "\033"
|
||||||
|
#define TERMCLEAR ESC "[H" ESC "[J"
|
||||||
|
#define TERMNORM ESC "[0m"
|
||||||
|
#define TERMBOLD ESC "[1m"
|
||||||
|
#define TERMRVRS ESC "[7m"
|
||||||
|
|
||||||
|
struct stats {
|
||||||
|
uint64_t mem_used;
|
||||||
|
uint64_t mem_limit;
|
||||||
|
uint64_t kmem_used;
|
||||||
|
uint64_t kmem_limit;
|
||||||
|
uint64_t cpu_use_nanos;
|
||||||
|
uint64_t cpu_use_user;
|
||||||
|
uint64_t cpu_use_sys;
|
||||||
|
uint64_t blkio;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ct {
|
||||||
|
struct lxc_container *c;
|
||||||
|
struct stats *stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int delay = 3;
|
||||||
|
static char sort_by = 'n';
|
||||||
|
static int sort_reverse = 0;
|
||||||
|
|
||||||
|
static struct termios oldtios;
|
||||||
|
static struct ct *ct = NULL;
|
||||||
|
static int ct_alloc_cnt = 0;
|
||||||
|
|
||||||
|
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
case 'd': delay = atoi(arg); break;
|
||||||
|
case 's': sort_by = arg[0]; break;
|
||||||
|
case 'r': sort_reverse = 1; break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct option my_longopts[] = {
|
||||||
|
{"delay", required_argument, 0, 'd'},
|
||||||
|
{"sort", required_argument, 0, 's'},
|
||||||
|
{"reverse", no_argument, 0, 'r'},
|
||||||
|
LXC_COMMON_OPTIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct lxc_arguments my_args = {
|
||||||
|
.progname = "lxc-top",
|
||||||
|
.help = "\
|
||||||
|
[--name=NAME]\n\
|
||||||
|
\n\
|
||||||
|
lxc-top monitors the state of the active containers\n\
|
||||||
|
\n\
|
||||||
|
Options :\n\
|
||||||
|
-d, --delay delay in seconds between refreshes (default: 3.0)\n\
|
||||||
|
-s, --sort sort by [n,c,b,m] (default: n) where\n\
|
||||||
|
n = Name\n\
|
||||||
|
c = CPU use\n\
|
||||||
|
b = Block I/O use\n\
|
||||||
|
m = Memory use\n\
|
||||||
|
k = Kernel memory use\n\
|
||||||
|
-r, --reverse sort in reverse (descending) order\n",
|
||||||
|
.name = ".*",
|
||||||
|
.options = my_longopts,
|
||||||
|
.parser = my_parser,
|
||||||
|
.checker = NULL,
|
||||||
|
.lxcpath_additional = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void stdin_tios_restore(void)
|
||||||
|
{
|
||||||
|
tcsetattr(0, TCSAFLUSH, &oldtios);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stdin_tios_setup(void)
|
||||||
|
{
|
||||||
|
struct termios newtios;
|
||||||
|
|
||||||
|
if (!isatty(0)) {
|
||||||
|
ERROR("stdin is not a tty");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcgetattr(0, &oldtios)) {
|
||||||
|
SYSERROR("failed to get current terminal settings");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
newtios = oldtios;
|
||||||
|
|
||||||
|
/* turn off echo and line buffering */
|
||||||
|
newtios.c_iflag &= ~IGNBRK;
|
||||||
|
newtios.c_iflag &= BRKINT;
|
||||||
|
newtios.c_lflag &= ~(ECHO|ICANON);
|
||||||
|
newtios.c_cc[VMIN] = 1;
|
||||||
|
newtios.c_cc[VTIME] = 0;
|
||||||
|
|
||||||
|
if (tcsetattr(0, TCSAFLUSH, &newtios)) {
|
||||||
|
ERROR("failed to set new terminal settings");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stdin_tios_rows(void)
|
||||||
|
{
|
||||||
|
struct winsize wsz;
|
||||||
|
if (isatty(0) && ioctl(0, TIOCGWINSZ, &wsz) == 0)
|
||||||
|
return wsz.ws_row;
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stdin_handler(int fd, uint32_t events, void *data,
|
||||||
|
struct lxc_epoll_descr *descr)
|
||||||
|
{
|
||||||
|
char *in_char = data;
|
||||||
|
|
||||||
|
if (events & EPOLLIN) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = read(fd, in_char, sizeof(*in_char));
|
||||||
|
if (rc <= 0)
|
||||||
|
*in_char = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events & EPOLLHUP)
|
||||||
|
*in_char = 'q';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sig_handler(int sig)
|
||||||
|
{
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
|
||||||
|
{
|
||||||
|
if (val > 1 << 30) {
|
||||||
|
snprintf(buf, bufsz, "%u.%2.2u GB",
|
||||||
|
(int)(val >> 30),
|
||||||
|
(int)(val & ((1 << 30) - 1)) / 10737419);
|
||||||
|
} else if (val > 1 << 20) {
|
||||||
|
int x = val + 5243; /* for rounding */
|
||||||
|
snprintf(buf, bufsz, "%u.%2.2u MB",
|
||||||
|
x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
|
||||||
|
} else if (val > 1 << 10) {
|
||||||
|
int x = val + 5; /* for rounding */
|
||||||
|
snprintf(buf, bufsz, "%u.%2.2u KB",
|
||||||
|
x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, bufsz, "%3u.00 ", (int)val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t stat_get_int(struct lxc_container *c, const char *item)
|
||||||
|
{
|
||||||
|
char buf[80];
|
||||||
|
int len;
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
len = c->get_cgroup_item(c, item, buf, sizeof(buf));
|
||||||
|
if (len <= 0) {
|
||||||
|
ERROR("unable to read cgroup item %s", item);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = strtoull(buf, NULL, 0);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
|
||||||
|
const char *match, int column)
|
||||||
|
{
|
||||||
|
char buf[4096];
|
||||||
|
int i,j,len;
|
||||||
|
uint64_t val = 0;
|
||||||
|
char **lines, **cols;
|
||||||
|
size_t matchlen;
|
||||||
|
|
||||||
|
len = c->get_cgroup_item(c, item, buf, sizeof(buf));
|
||||||
|
if (len <= 0) {
|
||||||
|
ERROR("unable to read cgroup item %s", item);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = lxc_string_split_and_trim(buf, '\n');
|
||||||
|
if (!lines)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
matchlen = strlen(match);
|
||||||
|
for (i = 0; lines[i]; i++) {
|
||||||
|
if (strncmp(lines[i], match, matchlen) == 0) {
|
||||||
|
cols = lxc_string_split_and_trim(lines[i], ' ');
|
||||||
|
if (!cols)
|
||||||
|
goto err1;
|
||||||
|
for (j = 0; cols[j]; j++) {
|
||||||
|
if (j == column) {
|
||||||
|
val = strtoull(cols[j], NULL, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lxc_free_array((void **)cols, free);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err1:
|
||||||
|
lxc_free_array((void **)lines, free);
|
||||||
|
out:
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stats_get(struct lxc_container *c, struct ct *ct, struct stats *total)
|
||||||
|
{
|
||||||
|
ct->c = c;
|
||||||
|
ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes");
|
||||||
|
ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes");
|
||||||
|
ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
|
||||||
|
ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
|
||||||
|
ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage");
|
||||||
|
ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
|
||||||
|
ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
|
||||||
|
ct->stats->blkio = stat_match_get_int(c, "blkio.throttle.io_service_bytes", "Total", 1);
|
||||||
|
|
||||||
|
if (total) {
|
||||||
|
total->mem_used = total->mem_used + ct->stats->mem_used;
|
||||||
|
total->mem_limit = total->mem_limit + ct->stats->mem_limit;
|
||||||
|
total->kmem_used = total->kmem_used + ct->stats->kmem_used;
|
||||||
|
total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit;
|
||||||
|
total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos;
|
||||||
|
total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user;
|
||||||
|
total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys;
|
||||||
|
total->blkio = total->blkio + ct->stats->blkio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stats_print_header(struct stats *stats)
|
||||||
|
{
|
||||||
|
printf(TERMRVRS TERMBOLD);
|
||||||
|
printf("%-18s %8s %8s %8s %10s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem");
|
||||||
|
if (stats->kmem_used > 0)
|
||||||
|
printf(" %10s", "KMem");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("%-18s %8s %8s %8s %10s %10s", "Name", "Used", "Sys", "User", "Total", "Used");
|
||||||
|
if (stats->kmem_used > 0)
|
||||||
|
printf(" %10s", "Used");
|
||||||
|
printf("\n");
|
||||||
|
printf(TERMNORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stats_print(const char *name, const struct stats *stats,
|
||||||
|
const struct stats *total)
|
||||||
|
{
|
||||||
|
char blkio_str[20];
|
||||||
|
char mem_used_str[20];
|
||||||
|
char kmem_used_str[20];
|
||||||
|
|
||||||
|
size_humanize(stats->blkio, blkio_str, sizeof(blkio_str));
|
||||||
|
size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str));
|
||||||
|
|
||||||
|
printf("%-18s %8.2f %8.2f %8.2f %10s %10s",
|
||||||
|
name,
|
||||||
|
(float)stats->cpu_use_nanos / 1000000000,
|
||||||
|
(float)stats->cpu_use_sys / USER_HZ,
|
||||||
|
(float)stats->cpu_use_user / USER_HZ,
|
||||||
|
blkio_str,
|
||||||
|
mem_used_str);
|
||||||
|
if (total->kmem_used > 0) {
|
||||||
|
size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str));
|
||||||
|
printf(" %10s", kmem_used_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmp_name(const void *sct1, const void *sct2)
|
||||||
|
{
|
||||||
|
const struct ct *ct1 = sct1;
|
||||||
|
const struct ct *ct2 = sct2;
|
||||||
|
|
||||||
|
if (sort_reverse)
|
||||||
|
return strcmp(ct2->c->name, ct1->c->name);
|
||||||
|
return strcmp(ct1->c->name, ct2->c->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmp_cpuuse(const void *sct1, const void *sct2)
|
||||||
|
{
|
||||||
|
const struct ct *ct1 = sct1;
|
||||||
|
const struct ct *ct2 = sct2;
|
||||||
|
|
||||||
|
if (sort_reverse)
|
||||||
|
return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos;
|
||||||
|
return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmp_blkio(const void *sct1, const void *sct2)
|
||||||
|
{
|
||||||
|
const struct ct *ct1 = sct1;
|
||||||
|
const struct ct *ct2 = sct2;
|
||||||
|
|
||||||
|
if (sort_reverse)
|
||||||
|
return ct2->stats->blkio < ct1->stats->blkio;
|
||||||
|
return ct1->stats->blkio < ct2->stats->blkio;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmp_memory(const void *sct1, const void *sct2)
|
||||||
|
{
|
||||||
|
const struct ct *ct1 = sct1;
|
||||||
|
const struct ct *ct2 = sct2;
|
||||||
|
|
||||||
|
if (sort_reverse)
|
||||||
|
return ct2->stats->mem_used < ct1->stats->mem_used;
|
||||||
|
return ct1->stats->mem_used < ct2->stats->mem_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmp_kmemory(const void *sct1, const void *sct2)
|
||||||
|
{
|
||||||
|
const struct ct *ct1 = sct1;
|
||||||
|
const struct ct *ct2 = sct2;
|
||||||
|
|
||||||
|
if (sort_reverse)
|
||||||
|
return ct2->stats->kmem_used < ct1->stats->kmem_used;
|
||||||
|
return ct1->stats->kmem_used < ct2->stats->kmem_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ct_sort(int active)
|
||||||
|
{
|
||||||
|
int (*cmp_func)(const void *, const void *);
|
||||||
|
|
||||||
|
switch(sort_by) {
|
||||||
|
default:
|
||||||
|
case 'n': cmp_func = cmp_name; break;
|
||||||
|
case 'c': cmp_func = cmp_cpuuse; break;
|
||||||
|
case 'b': cmp_func = cmp_blkio; break;
|
||||||
|
case 'm': cmp_func = cmp_memory; break;
|
||||||
|
case 'k': cmp_func = cmp_kmemory; break;
|
||||||
|
}
|
||||||
|
qsort(ct, active, sizeof(*ct), (int (*)(const void *,const void *))cmp_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ct_free(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ct_alloc_cnt; i++) {
|
||||||
|
if (ct[i].c) {
|
||||||
|
lxc_container_put(ct[i].c);
|
||||||
|
ct[i].c = NULL;
|
||||||
|
}
|
||||||
|
if (ct[i].stats) {
|
||||||
|
free(ct[i].stats);
|
||||||
|
ct[i].stats = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ct_realloc(int active_cnt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (active_cnt > ct_alloc_cnt) {
|
||||||
|
ct_free();
|
||||||
|
ct = realloc(ct, sizeof(*ct) * active_cnt);
|
||||||
|
if (!ct) {
|
||||||
|
ERROR("cannot alloc mem");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
for (i = 0; i < active_cnt; i++) {
|
||||||
|
ct[i].stats = malloc(sizeof(*ct[0].stats));
|
||||||
|
if (!ct[i].stats) {
|
||||||
|
ERROR("cannot alloc mem");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ct_alloc_cnt = active_cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct lxc_epoll_descr descr;
|
||||||
|
int ret, ct_print_cnt;
|
||||||
|
char in_char;
|
||||||
|
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
if (lxc_arguments_parse(&my_args, argc, argv))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ct_print_cnt = stdin_tios_rows() - 3; /* 3 -> header and total */
|
||||||
|
if (stdin_tios_setup() < 0) {
|
||||||
|
ERROR("failed to setup terminal");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ensure the terminal gets restored */
|
||||||
|
atexit(stdin_tios_restore);
|
||||||
|
signal(SIGINT, sig_handler);
|
||||||
|
signal(SIGQUIT, sig_handler);
|
||||||
|
|
||||||
|
if (lxc_mainloop_open(&descr)) {
|
||||||
|
ERROR("failed to create mainloop");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &in_char);
|
||||||
|
if (ret) {
|
||||||
|
ERROR("failed to add stdin handler");
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
struct lxc_container **active;
|
||||||
|
int i, active_cnt;
|
||||||
|
struct stats total;
|
||||||
|
char total_name[30];
|
||||||
|
|
||||||
|
active_cnt = list_active_containers(my_args.lxcpath[0], NULL, &active);
|
||||||
|
ct_realloc(active_cnt);
|
||||||
|
|
||||||
|
memset(&total, 0, sizeof(total));
|
||||||
|
for (i = 0; i < active_cnt; i++)
|
||||||
|
stats_get(active[i], &ct[i], &total);
|
||||||
|
|
||||||
|
ct_sort(active_cnt);
|
||||||
|
|
||||||
|
printf(TERMCLEAR);
|
||||||
|
stats_print_header(&total);
|
||||||
|
for (i = 0; i < active_cnt && i < ct_print_cnt; i++) {
|
||||||
|
stats_print(ct[i].c->name, ct[i].stats, &total);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
sprintf(total_name, "TOTAL %d of %d", i, active_cnt);
|
||||||
|
stats_print(total_name, &total, &total);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
for (i = 0; i < active_cnt; i++) {
|
||||||
|
lxc_container_put(ct[i].c);
|
||||||
|
ct[i].c = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_char = '\0';
|
||||||
|
ret = lxc_mainloop(&descr, 1000 * delay);
|
||||||
|
if (ret != 0 || in_char == 'q')
|
||||||
|
break;
|
||||||
|
switch(in_char) {
|
||||||
|
case 'r':
|
||||||
|
sort_reverse ^= 1;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
case 'c':
|
||||||
|
case 'b':
|
||||||
|
case 'm':
|
||||||
|
case 'k':
|
||||||
|
if (sort_by == in_char)
|
||||||
|
sort_reverse ^= 1;
|
||||||
|
else
|
||||||
|
sort_reverse = 0;
|
||||||
|
sort_by = in_char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
err1:
|
||||||
|
lxc_mainloop_close(&descr);
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user