[lxc-devel] [PATCH] port lxc-top from lua to C for wider availability
Dwight Engen
dwight.engen at oracle.com
Mon Sep 22 18:42:44 UTC 2014
On Mon, 22 Sep 2014 17:15:40 +0000
Serge Hallyn <serge.hallyn at ubuntu.com> wrote:
> Quoting Dwight Engen (dwight.engen at oracle.com):
> > It seems lxc-top would be useful on some platforms which don't yet
> > bundle the lxc lua binding. This is pretty much a direct port of
> > the lua based lxc-top to C. It still just uses printf for output so
> > there are no additional dependencies for lxc.
> >
> > - 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 at oracle.com>
>
> Thanks, Dwight, that'll be nice to have. And I'm glad to see you're
> keeping the lua code around.
>
> In src/lxc/Makefile.am you left lxc-top in bin_SCRIPTS and EXTRA_DIST.
> Should those be removed?
>
> Also while 'lxc-top -r' worked, 'lxc-top -s m -r' didn't seem to.
> Looking at the code, you're using 'memsw.usage_in_bytes.' But that
> doesn't always exist. So in cmp_memory, I'd suggest checking whether
> both containers have memsw_used 0, and if so then just use mem_used.
Thanks, good catches. I'll fix them up and send a v2.
> But overall,
>
> Acked-by: Serge E. Hallyn <serge.hallyn at ubuntu.com>
>
>
> > ---
> > doc/lxc-top.sgml.in | 26 +--
> > src/lxc/Makefile.am | 1 +
> > src/lxc/lxc-top | 243 -------------------------
> > src/lxc/lxc-top.c | 516
> > ++++++++++++++++++++++++++++++++++++++++++++++++++++
> > src/lxc/lxc-top.lua | 243 +++++++++++++++++++++++++ 5 files
> > changed, 766 insertions(+), 263 deletions(-) delete mode 100755
> > src/lxc/lxc-top create mode 100644 src/lxc/lxc-top.c
> > create mode 100755 src/lxc/lxc-top.lua
> >
> > diff --git a/doc/lxc-top.sgml.in b/doc/lxc-top.sgml.in
> > index ba727c5..2e2f774 100644
> > --- a/doc/lxc-top.sgml.in
> > +++ b/doc/lxc-top.sgml.in
> > @@ -47,7 +47,6 @@ Foundation, Inc., 51 Franklin Street, Fifth
> > Floor, Boston, MA 02110-1301 USA <cmdsynopsis>
> > <command>lxc-top</command>
> > <arg choice="opt">--help</arg>
> > - <arg choice="opt">--max
> > <replaceable>count</replaceable></arg> <arg choice="opt">--delay
> > <replaceable>delay</replaceable></arg> <arg choice="opt">--sort
> > <replaceable>sortby</replaceable></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 is updated every <replaceable>delay</replaceable> seconds,
> > and is ordered according to the <replaceable>sortby</replaceable>
> > value
> > - given. Specifying <replaceable>count</replaceable> will
> > limit the
> > - number of containers displayed, otherwise
> > <command>lxc-top</command>
> > - will display as many containers as can fit in your terminal.
> > + given. <command>lxc-top</command> will display as many
> > containers as
> > + can fit in your terminal. Press 'q' to quit. Press one of
> > the sort
> > + key letters to sort by that statistic. Pressing a sort key
> > letter a
> > + second time reverses the sort order.
> > </para>
> > </refsect1>
> >
> > @@ -72,26 +72,12 @@ Foundation, Inc., 51 Franklin Street, Fifth
> > Floor, Boston, MA 02110-1301 USA
> > <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>
> > - <term>
> > <option><optional>-d, --delay
> > <replaceable>delay</replaceable></optional></option> </term>
> > <listitem>
> > <para>
> > Amount of time in seconds to delay between screen
> > updates.
> > - This can be specified as less than a second by giving a
> > - rational number, for example 0.5 for a half second
> > delay. The
> > - default is 3 seconds.
> > + The default is 3 seconds.
> > </para>
> > </listitem>
> > </varlistentry>
> > @@ -103,7 +89,7 @@ Foundation, Inc., 51 Franklin Street, Fifth
> > Floor, Boston, MA 02110-1301 USA <para>
> > Sort the containers by name, cpu use, or memory use.
> > The <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'.
> > </para>
> > </listitem>
> > diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
> > index 8322e62..65c1f91 100644
> > --- a/src/lxc/Makefile.am
> > +++ b/src/lxc/Makefile.am
> > @@ -198,6 +198,7 @@ bin_PROGRAMS = \
> > lxc-snapshot \
> > lxc-start \
> > lxc-stop \
> > + lxc-top \
> > lxc-unfreeze \
> > lxc-unshare \
> > lxc-usernsexec \
> > diff --git a/src/lxc/lxc-top b/src/lxc/lxc-top
> > deleted file mode 100755
> > index b5b3a69..0000000
> > --- a/src/lxc/lxc-top
> > +++ /dev/null
> > @@ -1,243 +0,0 @@
> > -#!/usr/bin/env lua
> > ---
> > --- top(1) like monitor for lxc containers
> > ---
> > --- Copyright © 2012 Oracle.
> > ---
> > --- Authors:
> > --- Dwight Engen <dwight.engen at oracle.com>
> > ---
> > --- This library is free software; you can redistribute it and/or
> > modify --- it under the terms of the GNU General Public License
> > version 2, as --- published by the Free Software Foundation.
> > ---
> > --- This program 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 General Public License for more details.
> > ---
> > --- You should have received a copy of the GNU General Public
> > License along --- with this program; if not, write to the Free
> > Software Foundation, Inc., --- 51 Franklin Street, Fifth Floor,
> > Boston, MA 02110-1301 USA. ---
> > -
> > -local lxc = require("lxc")
> > -local core = require("lxc.core")
> > -local getopt = require("alt_getopt")
> > -
> > -local USER_HZ = 100
> > -local ESC = string.format("%c", 27)
> > -local TERMCLEAR = ESC.."[H"..ESC.."[J"
> > -local TERMNORM = ESC.."[0m"
> > -local TERMBOLD = ESC.."[1m"
> > -local TERMRVRS = ESC.."[7m"
> > -
> > -local containers = {}
> > -local stats = {}
> > -local stats_total = {}
> > -local max_containers
> > -
> > -function printf(...)
> > - local function wrapper(...) io.write(string.format(...)) end
> > - local status, result = pcall(wrapper, ...)
> > - if not status then
> > - error(result, 2)
> > - end
> > -end
> > -
> > -function string:split(delim, max_cols)
> > - local cols = {}
> > - local start = 1
> > - local nextc
> > - repeat
> > - nextc = string.find(self, delim, start)
> > - if (nextc and #cols ~= max_cols - 1) then
> > - table.insert(cols, string.sub(self, start, nextc-1))
> > - start = nextc + #delim
> > - else
> > - table.insert(cols, string.sub(self, start,
> > string.len(self)))
> > - nextc = nil
> > - end
> > - until nextc == nil or start > #self
> > - return cols
> > -end
> > -
> > -function strsisize(size, width)
> > - local KiB = 1024
> > - local MiB = 1048576
> > - local GiB = 1073741824
> > - local TiB = 1099511627776
> > - local PiB = 1125899906842624
> > - local EiB = 1152921504606846976
> > - local ZiB = 1180591620717411303424
> > -
> > - if (size >= ZiB) then
> > - return string.format("%d.%2.2d ZB", size / ZiB,
> > (math.floor(size % ZiB) * 100) / ZiB)
> > - end
> > - if (size >= EiB) then
> > - return string.format("%d.%2.2d EB", size / EiB,
> > (math.floor(size % EiB) * 100) / EiB)
> > - end
> > - if (size >= PiB) then
> > - return string.format("%d.%2.2d PB", size / PiB,
> > (math.floor(size % PiB) * 100) / PiB)
> > - end
> > - if (size >= TiB) then
> > - return string.format("%d.%2.2d TB", size / TiB,
> > (math.floor(size % TiB) * 100) / TiB)
> > - end
> > - if (size >= GiB) then
> > - return string.format("%d.%2.2d GB", size / GiB,
> > (math.floor(size % GiB) * 100) / GiB)
> > - end
> > - if (size >= MiB) then
> > - return string.format("%d.%2.2d MB", size / MiB,
> > (math.floor(size % MiB) * 1000) / (MiB * 10))
> > - end
> > - if (size >= KiB) then
> > - return string.format("%d.%2.2d KB", size / KiB,
> > (math.floor(size % KiB) * 1000) / (KiB * 10))
> > - end
> > - return string.format("%3d.00 ", size)
> > -end
> > -
> > -function tty_lines()
> > - local rows = 25
> > - local f = assert(io.popen("stty -a | head -n 1"))
> > - for line in f:lines() do
> > - local stty_rows
> > - _,_,stty_rows = string.find(line, "rows (%d+)")
> > - if (stty_rows ~= nil) then
> > - rows = stty_rows
> > - break
> > - end
> > - end
> > - f:close()
> > - return rows
> > -end
> > -
> > -function container_sort(a, b)
> > - if (optarg["r"]) then
> > - if (optarg["s"] == "n") then return (a > b)
> > - elseif (optarg["s"] == "c") then return
> > (stats[a].cpu_use_nanos < stats[b].cpu_use_nanos)
> > - elseif (optarg["s"] == "d") then return (stats[a].blkio <
> > stats[b].blkio)
> > - elseif (optarg["s"] == "m") then return (stats[a].mem_used
> > < stats[b].mem_used)
> > - elseif (optarg["s"] == "k") then return
> > (stats[a].kmem_used < stats[b].kmem_used)
> > - end
> > - else
> > - if (optarg["s"] == "n") then return (a < b)
> > - elseif (optarg["s"] == "c") then return
> > (stats[a].cpu_use_nanos > stats[b].cpu_use_nanos)
> > - elseif (optarg["s"] == "d") then return (stats[a].blkio >
> > stats[b].blkio)
> > - elseif (optarg["s"] == "m") then return (stats[a].mem_used
> > > stats[b].mem_used)
> > - elseif (optarg["s"] == "k") then return
> > (stats[a].kmem_used > stats[b].kmem_used)
> > - end
> > - end
> > -end
> > -
> > -function container_list_update()
> > - local now_running
> > -
> > - now_running = lxc.containers_running(true)
> > -
> > - -- check for newly started containers
> > - for _,v in ipairs(now_running) do
> > - if (containers[v] == nil) then
> > - local ct = lxc.container:new(v)
> > - -- note, this is a "mixed" table, ie both dictionary
> > and list
> > - containers[v] = ct
> > - table.insert(containers, v)
> > - end
> > - end
> > -
> > - -- check for newly stopped containers
> > - local indx = 1
> > - while (indx <= #containers) do
> > - local ctname = containers[indx]
> > - if (now_running[ctname] == nil) then
> > - containers[ctname] = nil
> > - stats[ctname] = nil
> > - table.remove(containers, indx)
> > - else
> > - indx = indx + 1
> > - end
> > - end
> > -
> > - -- get stats for all current containers and resort the list
> > - lxc.stats_clear(stats_total)
> > - for _,ctname in ipairs(containers) do
> > - stats[ctname] = containers[ctname]:stats_get(stats_total)
> > - end
> > - table.sort(containers, container_sort)
> > -end
> > -
> > -function stats_print_header(stats_total)
> > - printf(TERMRVRS .. TERMBOLD)
> > - printf("%-15s %8s %8s %8s %10s %10s", "Container", "CPU",
> > "CPU", "CPU", "BlkIO", "Mem")
> > - if (stats_total.kmem_used > 0) then printf(" %10s", "KMem") end
> > - printf("\n")
> > -
> > - printf("%-15s %8s %8s %8s %10s %10s", "Name", "Used",
> > "Sys", "User", "Total", "Used")
> > - if (stats_total.kmem_used > 0) then printf(" %10s", "Used") end
> > - printf("\n")
> > - printf(TERMNORM)
> > -end
> > -
> > -function stats_print(name, stats, stats_total)
> > - printf("%-15s %8.2f %8.2f %8.2f %10s %10s",
> > - name,
> > - stats.cpu_use_nanos / 1000000000,
> > - stats.cpu_use_sys / USER_HZ,
> > - stats.cpu_use_user / USER_HZ,
> > - strsisize(stats.blkio),
> > - strsisize(stats.mem_used))
> > - if (stats_total.kmem_used > 0) then
> > - printf(" %10s", strsisize(stats.kmem_used))
> > - end
> > -end
> > -
> > -function usage()
> > - printf("Usage: lxc-top [options]\n" ..
> > - " -h|--help print this help message\n" ..
> > - " -m|--max display maximum number of
> > containers\n" ..
> > - " -d|--delay delay in seconds between refreshes
> > (default: 3.0)\n" ..
> > - " -s|--sort sort by [n,c,d,m] (default: n)
> > where\n" ..
> > - " n = Name\n" ..
> > - " c = CPU use\n" ..
> > - " d = Disk I/O use\n" ..
> > - " m = Memory use\n" ..
> > - " k = Kernel memory use\n" ..
> > - " -r|--reverse sort in reverse (descending) order\n"
> > - )
> > - os.exit(1)
> > -end
> > -
> > -local long_opts = {
> > - help = "h",
> > - delay = "d",
> > - max = "m",
> > - reverse = "r",
> > - sort = "s",
> > -}
> > -
> > -optarg,optind = alt_getopt.get_opts (arg, "hd:m:rs:", long_opts)
> > -optarg["d"] = tonumber(optarg["d"]) or 3.0
> > -optarg["m"] = tonumber(optarg["m"]) or tonumber(tty_lines() - 3)
> > -optarg["r"] = optarg["r"] or false
> > -optarg["s"] = optarg["s"] or "n"
> > -if (optarg["h"] ~= nil) then
> > - usage()
> > -end
> > -
> > -while true
> > -do
> > - container_list_update()
> > - -- if some terminal we care about doesn't support the simple
> > escapes, we
> > - -- may fall back to this, or ncurses. ug.
> > - --os.execute("tput clear")
> > - printf(TERMCLEAR)
> > - stats_print_header(stats_total)
> > - for index,ctname in ipairs(containers) do
> > - stats_print(ctname, stats[ctname], stats_total)
> > - printf("\n")
> > - if (index >= optarg["m"]) then
> > - break
> > - end
> > - end
> > - stats_print(string.format("TOTAL (%-2d)", #containers),
> > stats_total, stats_total)
> > - io.flush()
> > - core.usleep(optarg["d"] * 1000000)
> > -end
> > diff --git a/src/lxc/lxc-top.c b/src/lxc/lxc-top.c
> > new file mode 100644
> > index 0000000..fe89cc5
> > --- /dev/null
> > +++ b/src/lxc/lxc-top.c
> > @@ -0,0 +1,516 @@
> > +/*
> > + * lxc: linux Container library
> > + *
> > + * Copyright © 2014 Oracle.
> > + *
> > + * Authors:
> > + * Dwight Engen <dwight.engen at 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 memsw_used;
> > + uint64_t memsw_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 (!cols[j])
> > + goto err1;
> > + 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->memsw_used = stat_get_int(c,
> > "memory.memsw.usage_in_bytes");
> > + ct->stats->memsw_limit = stat_get_int(c,
> > "memory.memsw.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->memsw_used = total->memsw_used +
> > ct->stats->memsw_used;
> > + total->memsw_limit = total->memsw_limit +
> > ct->stats->memsw_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->memsw_used <
> > ct1->stats->memsw_used;
> > + return ct1->stats->memsw_used < ct2->stats->memsw_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) {
> > + printf("stats free\n");
> > + 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);
> > + 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;
> > +}
> > diff --git a/src/lxc/lxc-top.lua b/src/lxc/lxc-top.lua
> > new file mode 100755
> > index 0000000..b5b3a69
> > --- /dev/null
> > +++ b/src/lxc/lxc-top.lua
> > @@ -0,0 +1,243 @@
> > +#!/usr/bin/env lua
> > +--
> > +-- top(1) like monitor for lxc containers
> > +--
> > +-- Copyright © 2012 Oracle.
> > +--
> > +-- Authors:
> > +-- Dwight Engen <dwight.engen at oracle.com>
> > +--
> > +-- This library is free software; you can redistribute it and/or
> > modify +-- it under the terms of the GNU General Public License
> > version 2, as +-- published by the Free Software Foundation.
> > +--
> > +-- This program 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 General Public License for more details.
> > +--
> > +-- You should have received a copy of the GNU General Public
> > License along +-- with this program; if not, write to the Free
> > Software Foundation, Inc., +-- 51 Franklin Street, Fifth Floor,
> > Boston, MA 02110-1301 USA. +--
> > +
> > +local lxc = require("lxc")
> > +local core = require("lxc.core")
> > +local getopt = require("alt_getopt")
> > +
> > +local USER_HZ = 100
> > +local ESC = string.format("%c", 27)
> > +local TERMCLEAR = ESC.."[H"..ESC.."[J"
> > +local TERMNORM = ESC.."[0m"
> > +local TERMBOLD = ESC.."[1m"
> > +local TERMRVRS = ESC.."[7m"
> > +
> > +local containers = {}
> > +local stats = {}
> > +local stats_total = {}
> > +local max_containers
> > +
> > +function printf(...)
> > + local function wrapper(...) io.write(string.format(...)) end
> > + local status, result = pcall(wrapper, ...)
> > + if not status then
> > + error(result, 2)
> > + end
> > +end
> > +
> > +function string:split(delim, max_cols)
> > + local cols = {}
> > + local start = 1
> > + local nextc
> > + repeat
> > + nextc = string.find(self, delim, start)
> > + if (nextc and #cols ~= max_cols - 1) then
> > + table.insert(cols, string.sub(self, start, nextc-1))
> > + start = nextc + #delim
> > + else
> > + table.insert(cols, string.sub(self, start,
> > string.len(self)))
> > + nextc = nil
> > + end
> > + until nextc == nil or start > #self
> > + return cols
> > +end
> > +
> > +function strsisize(size, width)
> > + local KiB = 1024
> > + local MiB = 1048576
> > + local GiB = 1073741824
> > + local TiB = 1099511627776
> > + local PiB = 1125899906842624
> > + local EiB = 1152921504606846976
> > + local ZiB = 1180591620717411303424
> > +
> > + if (size >= ZiB) then
> > + return string.format("%d.%2.2d ZB", size / ZiB,
> > (math.floor(size % ZiB) * 100) / ZiB)
> > + end
> > + if (size >= EiB) then
> > + return string.format("%d.%2.2d EB", size / EiB,
> > (math.floor(size % EiB) * 100) / EiB)
> > + end
> > + if (size >= PiB) then
> > + return string.format("%d.%2.2d PB", size / PiB,
> > (math.floor(size % PiB) * 100) / PiB)
> > + end
> > + if (size >= TiB) then
> > + return string.format("%d.%2.2d TB", size / TiB,
> > (math.floor(size % TiB) * 100) / TiB)
> > + end
> > + if (size >= GiB) then
> > + return string.format("%d.%2.2d GB", size / GiB,
> > (math.floor(size % GiB) * 100) / GiB)
> > + end
> > + if (size >= MiB) then
> > + return string.format("%d.%2.2d MB", size / MiB,
> > (math.floor(size % MiB) * 1000) / (MiB * 10))
> > + end
> > + if (size >= KiB) then
> > + return string.format("%d.%2.2d KB", size / KiB,
> > (math.floor(size % KiB) * 1000) / (KiB * 10))
> > + end
> > + return string.format("%3d.00 ", size)
> > +end
> > +
> > +function tty_lines()
> > + local rows = 25
> > + local f = assert(io.popen("stty -a | head -n 1"))
> > + for line in f:lines() do
> > + local stty_rows
> > + _,_,stty_rows = string.find(line, "rows (%d+)")
> > + if (stty_rows ~= nil) then
> > + rows = stty_rows
> > + break
> > + end
> > + end
> > + f:close()
> > + return rows
> > +end
> > +
> > +function container_sort(a, b)
> > + if (optarg["r"]) then
> > + if (optarg["s"] == "n") then return (a > b)
> > + elseif (optarg["s"] == "c") then return
> > (stats[a].cpu_use_nanos < stats[b].cpu_use_nanos)
> > + elseif (optarg["s"] == "d") then return (stats[a].blkio <
> > stats[b].blkio)
> > + elseif (optarg["s"] == "m") then return (stats[a].mem_used
> > < stats[b].mem_used)
> > + elseif (optarg["s"] == "k") then return
> > (stats[a].kmem_used < stats[b].kmem_used)
> > + end
> > + else
> > + if (optarg["s"] == "n") then return (a < b)
> > + elseif (optarg["s"] == "c") then return
> > (stats[a].cpu_use_nanos > stats[b].cpu_use_nanos)
> > + elseif (optarg["s"] == "d") then return (stats[a].blkio >
> > stats[b].blkio)
> > + elseif (optarg["s"] == "m") then return (stats[a].mem_used
> > > stats[b].mem_used)
> > + elseif (optarg["s"] == "k") then return
> > (stats[a].kmem_used > stats[b].kmem_used)
> > + end
> > + end
> > +end
> > +
> > +function container_list_update()
> > + local now_running
> > +
> > + now_running = lxc.containers_running(true)
> > +
> > + -- check for newly started containers
> > + for _,v in ipairs(now_running) do
> > + if (containers[v] == nil) then
> > + local ct = lxc.container:new(v)
> > + -- note, this is a "mixed" table, ie both dictionary
> > and list
> > + containers[v] = ct
> > + table.insert(containers, v)
> > + end
> > + end
> > +
> > + -- check for newly stopped containers
> > + local indx = 1
> > + while (indx <= #containers) do
> > + local ctname = containers[indx]
> > + if (now_running[ctname] == nil) then
> > + containers[ctname] = nil
> > + stats[ctname] = nil
> > + table.remove(containers, indx)
> > + else
> > + indx = indx + 1
> > + end
> > + end
> > +
> > + -- get stats for all current containers and resort the list
> > + lxc.stats_clear(stats_total)
> > + for _,ctname in ipairs(containers) do
> > + stats[ctname] = containers[ctname]:stats_get(stats_total)
> > + end
> > + table.sort(containers, container_sort)
> > +end
> > +
> > +function stats_print_header(stats_total)
> > + printf(TERMRVRS .. TERMBOLD)
> > + printf("%-15s %8s %8s %8s %10s %10s", "Container", "CPU",
> > "CPU", "CPU", "BlkIO", "Mem")
> > + if (stats_total.kmem_used > 0) then printf(" %10s", "KMem") end
> > + printf("\n")
> > +
> > + printf("%-15s %8s %8s %8s %10s %10s", "Name", "Used",
> > "Sys", "User", "Total", "Used")
> > + if (stats_total.kmem_used > 0) then printf(" %10s", "Used") end
> > + printf("\n")
> > + printf(TERMNORM)
> > +end
> > +
> > +function stats_print(name, stats, stats_total)
> > + printf("%-15s %8.2f %8.2f %8.2f %10s %10s",
> > + name,
> > + stats.cpu_use_nanos / 1000000000,
> > + stats.cpu_use_sys / USER_HZ,
> > + stats.cpu_use_user / USER_HZ,
> > + strsisize(stats.blkio),
> > + strsisize(stats.mem_used))
> > + if (stats_total.kmem_used > 0) then
> > + printf(" %10s", strsisize(stats.kmem_used))
> > + end
> > +end
> > +
> > +function usage()
> > + printf("Usage: lxc-top [options]\n" ..
> > + " -h|--help print this help message\n" ..
> > + " -m|--max display maximum number of
> > containers\n" ..
> > + " -d|--delay delay in seconds between refreshes
> > (default: 3.0)\n" ..
> > + " -s|--sort sort by [n,c,d,m] (default: n)
> > where\n" ..
> > + " n = Name\n" ..
> > + " c = CPU use\n" ..
> > + " d = Disk I/O use\n" ..
> > + " m = Memory use\n" ..
> > + " k = Kernel memory use\n" ..
> > + " -r|--reverse sort in reverse (descending) order\n"
> > + )
> > + os.exit(1)
> > +end
> > +
> > +local long_opts = {
> > + help = "h",
> > + delay = "d",
> > + max = "m",
> > + reverse = "r",
> > + sort = "s",
> > +}
> > +
> > +optarg,optind = alt_getopt.get_opts (arg, "hd:m:rs:", long_opts)
> > +optarg["d"] = tonumber(optarg["d"]) or 3.0
> > +optarg["m"] = tonumber(optarg["m"]) or tonumber(tty_lines() - 3)
> > +optarg["r"] = optarg["r"] or false
> > +optarg["s"] = optarg["s"] or "n"
> > +if (optarg["h"] ~= nil) then
> > + usage()
> > +end
> > +
> > +while true
> > +do
> > + container_list_update()
> > + -- if some terminal we care about doesn't support the simple
> > escapes, we
> > + -- may fall back to this, or ncurses. ug.
> > + --os.execute("tput clear")
> > + printf(TERMCLEAR)
> > + stats_print_header(stats_total)
> > + for index,ctname in ipairs(containers) do
> > + stats_print(ctname, stats[ctname], stats_total)
> > + printf("\n")
> > + if (index >= optarg["m"]) then
> > + break
> > + end
> > + end
> > + stats_print(string.format("TOTAL (%-2d)", #containers),
> > stats_total, stats_total)
> > + io.flush()
> > + core.usleep(optarg["d"] * 1000000)
> > +end
> > --
> > 1.9.3
> >
> > _______________________________________________
> > lxc-devel mailing list
> > lxc-devel at lists.linuxcontainers.org
> > http://lists.linuxcontainers.org/listinfo/lxc-devel
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel
More information about the lxc-devel
mailing list