[lxc-devel] [PATCH v2 4/4] add statistics to lxc-info
Serge Hallyn
serge.hallyn at ubuntu.com
Wed Nov 6 14:14:52 UTC 2013
Quoting Dwight Engen (dwight.engen at oracle.com):
> - allow lxc-info to show more than one container, using regex for the name
>
> Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
Acked-by: Serge E. Hallyn <serge.hallyn at ubuntu.com>
> ---
> doc/lxc-info.sgml.in | 65 ++++++++++++-
> src/lxc/lxc_info.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++----
> 2 files changed, 300 insertions(+), 22 deletions(-)
>
> diff --git a/doc/lxc-info.sgml.in b/doc/lxc-info.sgml.in
> index 819d5ca..791d780 100644
> --- a/doc/lxc-info.sgml.in
> +++ b/doc/lxc-info.sgml.in
> @@ -47,20 +47,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> <refsynopsisdiv>
> <cmdsynopsis>
> <command>lxc-info</command>
> - <arg choice="req">-n <replaceable>name</replaceable></arg>
> + <arg choice="opt">-n <replaceable>name</replaceable></arg>
> <arg choice="opt">-c <replaceable>KEY</replaceable></arg>
> <arg choice="opt">-s</arg>
> <arg choice="opt">-p</arg>
> <arg choice="opt">-i</arg>
> <arg choice="opt">-t <replaceable>state</replaceable></arg>
> + <arg choice="opt">-S</arg>
> + <arg choice="opt">-H</arg>
> </cmdsynopsis>
> </refsynopsisdiv>
>
> <refsect1>
> <title>Description</title>
> <para>
> - <command>lxc-info</command> queries and shows information about a
> - container.
> + <command>lxc-info</command> queries and shows information about
> + containers.
> </para>
> </refsect1>
>
> @@ -70,11 +72,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>
> <varlistentry>
> <term>
> - <option>-n <replaceable>name</replaceable></option>
> + <option><optional>-n <replaceable>name</replaceable></optional></option>
> </term>
> <listitem>
> <para>
> - The container name.
> + The container name. It is interpreted as a regular expression,
> + so it is possible to get information on all containers, several
> + of them or just one. See
> + <citerefentry>
> + <refentrytitle><command>regex</command></refentrytitle>
> + <manvolnum>7</manvolnum>
> + </citerefentry> for regular expression syntax. If not specified,
> + <replaceable>name</replaceable> will default to '.*' which
> + will give information on all containers in
> + <command>lxcpath</command>.
> </para>
> </listitem>
> </varlistentry>
> @@ -126,6 +137,41 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>
> <varlistentry>
> <term>
> + <option><optional>-S</optional></option>
> + </term>
> + <listitem>
> + <para>
> + Just print the container's statistics.
> + Note that for performance reasons the kernel does not account
> + kernel memory use unless a kernel memory limit is set. If a limit
> + is not set, <command>lxc-info</command> will display kernel memory
> + use as 0. A limit can be set by specifying
> + <programlisting>
> + lxc.cgroup.memory.kmem.limit_in_bytes = <replaceable>number</replaceable>
> + </programlisting>
> + in your container configuration file, see
> + <citerefentry>
> + <refentrytitle>lxc.conf</refentrytitle>
> + <manvolnum>5</manvolnum>
> + </citerefentry>.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry>
> + <term>
> + <option><optional>-H</optional></option>
> + </term>
> + <listitem>
> + <para>
> + Print the container's statistics in raw, non-humanized form. The
> + default is to print statistics in humanized form.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry>
> + <term>
> <option><optional>-t <replaceable>state</replaceable></optional></option>
> </term>
> <listitem>
> @@ -152,6 +198,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> </varlistentry>
>
> <varlistentry>
> + <term>lxc-info -n 'ubuntu.*'</term>
> + <listitem>
> + <para>
> + Show information for all containers whose name starts with ubuntu.
> + </para>
> + </listitem>
> + </varlistentry>
> +
> + <varlistentry>
> <term>lxc-info -n foo -t RUNNING</term>
> <listitem>
> <para>
> diff --git a/src/lxc/lxc_info.c b/src/lxc/lxc_info.c
> index aeaf9a8..ba43f37 100644
> --- a/src/lxc/lxc_info.c
> +++ b/src/lxc/lxc_info.c
> @@ -25,11 +25,14 @@
> #include <stdbool.h>
> #include <stdlib.h>
> #include <unistd.h>
> +#include <regex.h>
> +#include <limits.h>
> #include <libgen.h>
> #include <sys/types.h>
>
> #include <lxc/lxc.h>
> #include <lxc/log.h>
> +#include <lxc/utils.h>
> #include <lxc/lxccontainer.h>
>
> #include "commands.h"
> @@ -38,6 +41,8 @@
> static bool ips;
> static bool state;
> static bool pid;
> +static bool stats;
> +static bool humanize = true;
> static char *test_state = NULL;
> static char **key = NULL;
> static int keys = 0;
> @@ -53,6 +58,8 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
> case 'i': ips = true; break;
> case 's': state = true; break;
> case 'p': pid = true; break;
> + case 'S': stats = true; break;
> + case 'H': humanize = false; break;
> case 't': test_state = arg; break;
> }
> return 0;
> @@ -63,6 +70,8 @@ static const struct option my_longopts[] = {
> {"ips", no_argument, 0, 'i'},
> {"state", no_argument, 0, 's'},
> {"pid", no_argument, 0, 'p'},
> + {"stats", no_argument, 0, 'S'},
> + {"no-humanize", no_argument, 0, 'H'},
> {"state-is", required_argument, 0, 't'},
> LXC_COMMON_OPTIONS,
> };
> @@ -79,33 +88,177 @@ Options :\n\
> -c, --config=KEY show configuration variable KEY from running container\n\
> -i, --ips shows the IP addresses\n\
> -p, --pid shows the process id of the init container\n\
> + -S, --stats shows usage stats\n\
> + -H, --no-humanize shows stats as raw numbers, not humanized\n\
> -s, --state shows the state of the container\n\
> -t, --state-is=STATE test if current state is STATE\n\
> returns success if it matches, false otherwise\n",
> + .name = ".*",
> .options = my_longopts,
> .parser = my_parser,
> .checker = NULL,
> };
>
> -int main(int argc, char *argv[])
> +static void str_chomp(char *buf)
> {
> - struct lxc_container *c;
> + char *ch;
>
> - int i;
> + /* remove trailing whitespace from buf */
> + for(ch = &buf[strlen(buf)-1];
> + ch >= buf && (*ch == '\t' || *ch == '\n' || *ch == ' ');
> + ch--)
> + *ch = '\0';
> +}
>
> - if (lxc_arguments_parse(&my_args, argc, argv))
> - return -1;
> +static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
> +{
> + if (val > 1 << 30) {
> + snprintf(buf, bufsz, "%u.%2.2u GiB",
> + (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 MiB",
> + 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 KiB",
> + x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
> + } else {
> + snprintf(buf, bufsz, "%u bytes", (int)val);
> + }
> +}
>
> - if (!my_args.log_file)
> - my_args.log_file = "none";
> +static unsigned long long str_size_humanize(char *iobuf, size_t iobufsz)
> +{
> + unsigned long long val;
> + char *end = NULL;
>
> - if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
> - my_args.progname, my_args.quiet, my_args.lxcpath[0]))
> - return -1;
> + val = strtoull(iobuf, &end, 0);
> + if (humanize) {
> + if (*end == '\0' || *end == '\n')
> + size_humanize(val, iobuf, iobufsz);
> + else
> + *iobuf = '\0';
> + }
> + return val;
> +}
> +
> +static void print_net_stats(const char *name, const char *lxcpath)
> +{
> + int rc,netnr;
> + unsigned long long rx_bytes = 0, tx_bytes = 0;
> + char *ifname, *type;
> + char path[PATH_MAX];
> + char buf[256];
> +
> + for(netnr = 0; ;netnr++) {
> + sprintf(buf, "lxc.network.%d.type", netnr);
> + type = lxc_cmd_get_config_item(name, buf, lxcpath);
> + if (!type)
> + break;
>
> - c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
> - if (!c)
> + if (!strcmp(type, "veth")) {
> + sprintf(buf, "lxc.network.%d.veth.pair", netnr);
> + } else {
> + sprintf(buf, "lxc.network.%d.link", netnr);
> + }
> + free(type);
> + ifname = lxc_cmd_get_config_item(name, buf, lxcpath);
> + if (!ifname)
> + return;
> + printf("%-15s %s\n", "Link:", ifname);
> +
> + /* XXX: tx and rx are reversed from the host vs container
> + * perspective, print them from the container perspective
> + */
> + snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/rx_bytes", ifname);
> + rc = lxc_read_from_file(path, buf, sizeof(buf));
> + if (rc > 0) {
> + str_chomp(buf);
> + rx_bytes = str_size_humanize(buf, sizeof(buf));
> + printf("%-15s %s\n", " TX bytes:", buf);
> + }
> +
> + snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/tx_bytes", ifname);
> + rc = lxc_read_from_file(path, buf, sizeof(buf));
> + if (rc > 0) {
> + str_chomp(buf);
> + tx_bytes = str_size_humanize(buf, sizeof(buf));
> + printf("%-15s %s\n", " RX bytes:", buf);
> + }
> +
> + sprintf(buf, "%llu", rx_bytes + tx_bytes);
> + str_size_humanize(buf, sizeof(buf));
> + printf("%-15s %s\n", " Total bytes:", buf);
> + free(ifname);
> + }
> +}
> +
> +static void print_stats(struct lxc_container *c)
> +{
> + int i, ret;
> + char buf[256];
> +
> + ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf));
> + if (ret > 0 && ret < sizeof(buf)) {
> + str_chomp(buf);
> + if (humanize) {
> + float seconds = strtof(buf, NULL) / 1000000000.0;
> + printf("%-15s %.2f seconds\n", "CPU use:", seconds);
> + } else {
> + printf("%-15s %s\n", "CPU use:", buf);
> + }
> + }
> +
> + ret = c->get_cgroup_item(c, "blkio.throttle.io_service_bytes", buf, sizeof(buf));
> + if (ret > 0 && ret < sizeof(buf)) {
> + char *ch;
> +
> + /* put ch on last "Total" line */
> + str_chomp(buf);
> + for(ch = &buf[strlen(buf)-1]; ch > buf && *ch != '\n'; ch--)
> + ;
> + if (*ch == '\n')
> + ch++;
> +
> + if (strncmp(ch, "Total", 5) == 0) {
> + ch += 6;
> + memmove(buf, ch, strlen(ch)+1);
> + str_size_humanize(buf, sizeof(buf));
> + printf("%-15s %s\n", "BlkIO use:", buf);
> + }
> + }
> +
> + static const struct {
> + const char *name;
> + const char *file;
> + } lxstat[] = {
> + { "Memory use:", "memory.usage_in_bytes" },
> + { "KMem use:", "memory.kmem.usage_in_bytes" },
> + { NULL, NULL },
> + };
> +
> + for (i = 0; lxstat[i].name; i++) {
> + ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf));
> + if (ret > 0 && ret < sizeof(buf)) {
> + str_chomp(buf);
> + str_size_humanize(buf, sizeof(buf));
> + printf("%-15s %s\n", lxstat[i].name, buf);
> + }
> + }
> +}
> +
> +static int print_info(const char *name, const char *lxcpath)
> +{
> + int i;
> + struct lxc_container *c;
> +
> + c = lxc_container_new(name, lxcpath);
> + if (!c) {
> + fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
> return -1;
> + }
>
> if (!c->may_control(c)) {
> fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
> @@ -113,14 +266,16 @@ int main(int argc, char *argv[])
> return -1;
> }
>
> - if (!state && !pid && !ips && keys <= 0)
> - state = pid = ips = true;
> + if (!state && !pid && !ips && !stats && keys <= 0)
> + state = pid = ips = stats = true;
> +
> + printf("%-15s %s\n", "Name:", c->name);
>
> if (state || test_state) {
> if (test_state)
> return strcmp(c->state(c), test_state) != 0;
>
> - printf("state: \t%s\n", c->state(c));
> + printf("%-15s %s\n", "State:", c->state(c));
> }
>
> if (pid) {
> @@ -128,7 +283,7 @@ int main(int argc, char *argv[])
>
> initpid = c->init_pid(c);
> if (initpid >= 0)
> - printf("pid: \t%d\n", initpid);
> + printf("%-15s %d\n", "Pid:", initpid);
> }
>
> if (ips) {
> @@ -138,12 +293,17 @@ int main(int argc, char *argv[])
> i = 0;
> while (addresses[i]) {
> address = addresses[i];
> - printf("ip: \t%s\n", address);
> + printf("%-15s %s\n", "IP:", address);
> i++;
> }
> }
> }
>
> + if (stats) {
> + print_stats(c);
> + print_net_stats(name, lxcpath);
> + }
> +
> for(i = 0; i < keys; i++) {
> int len = c->get_config_item(c, key[i], NULL, 0);
>
> @@ -164,3 +324,66 @@ int main(int argc, char *argv[])
> lxc_container_put(c);
> return 0;
> }
> +
> +int main(int argc, char *argv[])
> +{
> + int rc, i, len, ret = EXIT_FAILURE;
> + char *regexp;
> + regex_t preg;
> + int ct_cnt;
> + char **ct_name;
> + bool printed;
> +
> + if (lxc_arguments_parse(&my_args, argc, argv))
> + goto err1;
> +
> + if (!my_args.log_file)
> + my_args.log_file = "none";
> +
> + if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
> + my_args.progname, my_args.quiet, my_args.lxcpath[0]))
> + goto err1;
> +
> + len = strlen(my_args.name) + 3;
> + regexp = malloc(len + 3);
> + if (!regexp) {
> + fprintf(stderr, "failed to allocate memory");
> + goto err1;
> + }
> + rc = snprintf(regexp, len, "^%s$", my_args.name);
> + if (rc < 0 || rc >= len) {
> + fprintf(stderr, "Name too long");
> + goto err2;
> + }
> +
> + if (regcomp(&preg, regexp, REG_NOSUB|REG_EXTENDED)) {
> + fprintf(stderr, "failed to compile the regex '%s'", my_args.name);
> + goto err2;
> + }
> +
> + printed = false;
> + ct_cnt = list_all_containers(my_args.lxcpath[0], &ct_name, NULL);
> + if (ct_cnt < 0)
> + goto err3;
> +
> + for (i = 0; i < ct_cnt; i++) {
> + if (regexec(&preg, ct_name[i], 0, NULL, 0) == 0)
> + {
> + if (printed)
> + printf("\n");
> + print_info(ct_name[i], my_args.lxcpath[0]);
> + printed = true;
> + }
> + free(ct_name[i]);
> + }
> + if (ct_name)
> + free(ct_name);
> + ret = EXIT_SUCCESS;
> +
> +err3:
> + regfree(&preg);
> +err2:
> + free(regexp);
> +err1:
> + return ret;
> +}
> --
> 1.8.3.1
>
>
> ------------------------------------------------------------------------------
> November Webinars for C, C++, Fortran Developers
> Accelerate application performance with scalable programming models. Explore
> techniques for threading, error checking, porting, and tuning. Get the most
> from the latest Intel processors and coprocessors. See abstracts and register
> http://pubads.g.doubleclick.net/gampad/clk?id=60136231&iu=/4140/ostg.clktrk
> _______________________________________________
> Lxc-devel mailing list
> Lxc-devel at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/lxc-devel
More information about the lxc-devel
mailing list