]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lscpu: extend --parse functionality
authorKarel Zak <kzak@redhat.com>
Wed, 27 Jul 2011 21:10:01 +0000 (23:10 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 27 Jul 2011 21:10:01 +0000 (23:10 +0200)
... to allow define output columns, for example:

$ lscpu --parse=CPU,CORE,NODE,CACHE
# CPU,Core,Node,L1d:L1i:L2
0,0,0,0:0:0
1,1,0,1:1:0

Note that CPU caches are separated by ":" in the new format. The
output for --parse (without the list of the columns) is backwardly
compatible, it means:

$ lscpu --parse
# CPU,Core,Socket,Node,,L1d,L1i,L2
0,0,0,0,,0,0,0
1,1,0,0,,1,1,0

Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/Makefile.am
sys-utils/lscpu.1
sys-utils/lscpu.c

index 53cdc3718c9e170c7cbfb60690f81228a7ad2e85..feb58884fd5d26881cadba0bff8007df408baa36 100644 (file)
@@ -19,7 +19,7 @@ dist_man_MANS += dmesg.1 ctrlaltdel.8 cytune.8 setarch.8 \
 
 if HAVE_CPU_SET_T
 usrbin_exec_PROGRAMS += lscpu
-lscpu_SOURCES = lscpu.c $(top_srcdir)/lib/cpuset.c
+lscpu_SOURCES = lscpu.c $(top_srcdir)/lib/cpuset.c $(top_srcdir)/lib/strutils.c
 dist_man_MANS += lscpu.1
 endif
 
index 7a0a97240d184347763e43369e5d012d8f40b5c8..4795e91c8819cafde97f618c98e92b313127d528 100644 (file)
@@ -23,8 +23,21 @@ which can be fed to other programs.
 .BR \-h , " \-\-help"
 Print a help message.
 .TP
-.BR \-p , " \-\-parse"
+.BR \-p , " \-\-parse " \fI[=list]\fP
 Print out in parsable instead of human-readable format.
+
+If the \fIlist\fP argument is not given then the default backwardly compatible
+output is printed.  The backwardly compatible format uses two commas to
+separate CPU cache columns. If no CPU caches are identified, then the cache
+columns are not printed at all.
+
+The \fIlist\fP argument is comma delimited list of the columns. Currently
+supported are CPU, Core, Node, Socket, Book and Cache columns. If the
+\fIlist\fP argument is given then always all requested columns are printed in
+the defined order. The Cache columns are separated by ':'.
+
+Note that the optional \fIlist\fP argument cannot be separated from the
+option by a space, the correct form is for example '\fB-p=cpu,node\fP' or '\fB--parse=cpu,node\fP'.
 .TP
 .BR \-s , " \-\-sysroot " \fIdirectory\fP
 Use the specified \fIdirectory\fP as system root.  This allows you to inspect
index c039596cad5167a3a040e3e93031b53448d266d1..738b0865109be98642153b3d20fa1cd04a0d80ca 100644 (file)
@@ -30,7 +30,6 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 #include <stdarg.h>
-#include <bitops.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -38,6 +37,9 @@
 #include "nls.h"
 #include "xalloc.h"
 #include "c.h"
+#include "strutils.h"
+#include "bitops.h"
+
 
 #define CACHE_MAX 100
 
@@ -153,6 +155,43 @@ static int path_exist(const char *path, ...)
 static cpu_set_t *path_cpuset(const char *path, ...)
                __attribute__ ((__format__ (__printf__, 1, 2)));
 
+/*
+ * Parsable output
+ */
+enum {
+       COL_CPU,
+       COL_CORE,
+       COL_SOCKET,
+       COL_NODE,
+       COL_BOOK,
+       COL_CACHE
+};
+
+static const char *colnames[] =
+{
+       [COL_CPU] = "CPU",
+       [COL_CORE] = "Core",
+       [COL_SOCKET] = "Socket",
+       [COL_NODE] = "Node",
+       [COL_BOOK] = "Book",
+       [COL_CACHE] = "Cache"
+};
+
+
+static int column_name_to_id(const char *name, size_t namesz)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(colnames); i++) {
+               const char *cn = colnames[i];
+
+               if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+                       return i;
+       }
+       warnx(_("unknown column: %s"), name);
+       return -1;
+}
+
 static const char *
 path_vcreate(const char *path, va_list ap)
 {
@@ -727,91 +766,138 @@ read_nodes(struct lscpu_desc *desc)
 }
 
 static void
-print_parsable(struct lscpu_desc *desc)
+print_parsable_cell(struct lscpu_desc *desc, int i, int col, int compatible)
 {
-       int i, j;
+       int j;
        size_t setsize = CPU_ALLOC_SIZE(maxcpus);
 
-       printf(_(
-       "# The following is the parsable format, which can be fed to other\n"
-       "# programs. Each different item in every column has an unique ID\n"
-       "# starting from zero.\n"
-       "# CPU,Core,Socket,Node,Book"));
-
-       if (desc->ncaches) {
-               /* separator between CPU topology and cache information */
-               putchar(',');
-
-               for (i = desc->ncaches - 1; i >= 0; i--)
-                       printf(",%s", desc->caches[i].name);
-       }
-       putchar('\n');
-
-       for (i = 0; i < desc->ncpus; i++) {
-
-               if (desc->online && !is_cpu_online(desc, i))
-                       continue;
-
-               /* #CPU */
+       switch (col) {
+       case COL_CPU:
                printf("%d", i);
-
-               /* Core */
+               break;
+       case COL_CORE:
                for (j = 0; j < desc->ncores; j++) {
                        if (CPU_ISSET_S(i, setsize, desc->coremaps[j])) {
-                               printf(",%d", j);
+                               printf("%d", j);
                                break;
                        }
                }
-               if (j == desc->ncores)
-                       putchar(',');
-
-               /* Socket */
+               break;
+       case COL_SOCKET:
                for (j = 0; j < desc->nsockets; j++) {
                        if (CPU_ISSET_S(i, setsize, desc->socketmaps[j])) {
-                               printf(",%d", j);
+                               printf("%d", j);
                                break;
                        }
                }
-               if (j == desc->nsockets)
-                       putchar(',');
-
-               /* Nodes */
+               break;
+       case COL_NODE:
                for (j = 0; j < desc->nnodes; j++) {
                        if (CPU_ISSET_S(i, setsize, desc->nodemaps[j])) {
-                               printf(",%d", j);
+                               printf("%d", j);
                                break;
                        }
                }
-               if (j == desc->nnodes)
-                       putchar(',');
-
-               /* Book */
+               break;
+       case COL_BOOK:
                for (j = 0; j < desc->nbooks; j++) {
                        if (CPU_ISSET_S(i, setsize, desc->bookmaps[j])) {
-                               printf(",%d", j);
+                               printf("%d", j);
                                break;
                        }
                }
-               if (j == desc->nbooks)
-                       putchar(',');
-
-               if (desc->ncaches)
-                       putchar(',');
-
-               /* Caches */
+               break;
+       case COL_CACHE:
                for (j = desc->ncaches - 1; j >= 0; j--) {
                        struct cpu_cache *ca = &desc->caches[j];
                        int x;
 
                        for (x = 0; x < ca->nsharedmaps; x++) {
                                if (CPU_ISSET_S(i, setsize, ca->sharedmaps[x])) {
-                                       printf(",%d", x);
+                                       if (j != desc->ncaches - 1)
+                                               putchar(compatible ? ',' : ':');
+                                       printf("%d", x);
                                        break;
                                }
                        }
                        if (x == ca->nsharedmaps)
                                putchar(',');
                }
+               break;
+       }
+}
+
+/*
+ * We support two formats:
+ *
+ * 1) "compatible" -- this format is compatible with the original lscpu(1)
+ * output and it contains fixed set of the columns. The CACHE columns are at
+ * the end of the line and the CACHE is not printed if the number of the caches
+ * is zero. The CACHE columns are separated by two commas, for example:
+ *
+ *    $ lscpu --parse
+ *    # CPU,Core,Socket,Node,,L1d,L1i,L2
+ *    0,0,0,0,,0,0,0
+ *    1,1,0,0,,1,1,0
+ *
+ * 2) "user defined output" -- this format prints always all columns without
+ * special prefix for CACHE column. If there are not CACHEs then the column is
+ * empty and the header "Cache" is printed rather than a real name of the cache.
+ * The CACHE columns are separated by ':'.
+ *
+ *     $ lscpu --parse=CPU,CORE,SOCKET,NODE,CACHE
+ *     # CPU,Core,Socket,Node,L1d:L1i:L2
+ *     0,0,0,0,0:0:0
+ *     1,1,0,0,1:1:0
+ */
+static void
+print_parsable(struct lscpu_desc *desc, int cols[], int ncols, int compatible)
+{
+       int i, c;
+
+       printf(_(
+       "# The following is the parsable format, which can be fed to other\n"
+       "# programs. Each different item in every column has an unique ID\n"
+       "# starting from zero.\n"));
+
+       fputs("# ", stdout);
+       for (i = 0; i < ncols; i++) {
+               if (cols[i] == COL_CACHE) {
+                       if (compatible && !desc->ncaches)
+                               continue;
+                       if (i > 0)
+                               putchar(',');
+                       if (compatible && i != 0)
+                               putchar(',');
+                       for (c = desc->ncaches - 1; c >= 0; c--) {
+                               printf("%s", desc->caches[c].name);
+                               if (c > 0)
+                                       putchar(compatible ? ',' : ':');
+                       }
+                       if (!desc->ncaches)
+                               fputs(colnames[cols[i]], stdout);
+               } else {
+                       if (i > 0)
+                               putchar(',');
+                       fputs(colnames[cols[i]], stdout);
+               }
+       }
+       putchar('\n');
+
+       for (i = 0; i < desc->ncpus; i++) {
+               if (desc->online && !is_cpu_online(desc, i))
+                       continue;
+               for (c = 0; c < ncols; c++) {
+                       if (compatible && cols[c] == COL_CACHE) {
+                               if (!desc->ncaches)
+                                       continue;
+                               if (c > 0)
+                                       putchar(',');
+                       }
+                       if (c > 0)
+                               putchar(',');
+                       print_parsable_cell(desc, i, cols[c], compatible);
+               }
                putchar('\n');
        }
 }
@@ -953,7 +1039,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 
        puts(_( "\nOptions:\n"
                "  -h, --help         print this help\n"
-               "  -p, --parse        print out a parsable instead of a readable format\n"
+               "  -p, --parse[=LIST] print out a parsable instead of a readable format\n"
                "  -s, --sysroot DIR  use directory DIR as system root\n"
                "  -x, --hex          print hexadecimal masks rather than lists of CPUs\n"));
 
@@ -964,10 +1050,12 @@ int main(int argc, char *argv[])
 {
        struct lscpu_desc _desc, *desc = &_desc;
        int parsable = 0, c, i, hex = 0;
+       int columns[ARRAY_SIZE(colnames)], ncolumns = 0;
+       int compatible = 0;
 
        static const struct option longopts[] = {
                { "help",       no_argument,       0, 'h' },
-               { "parse",      no_argument,       0, 'p' },
+               { "parse",      optional_argument, 0, 'p' },
                { "sysroot",    required_argument, 0, 's' },
                { "hex",        no_argument,       0, 'x' },
                { NULL,         0, 0, 0 }
@@ -977,12 +1065,28 @@ int main(int argc, char *argv[])
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
 
-       while((c = getopt_long(argc, argv, "hps:x", longopts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "hp::s:x", longopts, NULL)) != -1) {
                switch (c) {
                case 'h':
                        usage(stdout);
                case 'p':
                        parsable = 1;
+                       if (optarg) {
+                               if (*optarg == '=')
+                                       optarg++;
+                               ncolumns = string_to_idarray(optarg,
+                                               columns, ARRAY_SIZE(columns),
+                                               column_name_to_id);
+                               if (ncolumns < 0)
+                                       return EXIT_FAILURE;
+                       } else {
+                               columns[ncolumns++] = COL_CPU;
+                               columns[ncolumns++] = COL_CORE;
+                               columns[ncolumns++] = COL_SOCKET,
+                               columns[ncolumns++] = COL_NODE,
+                               columns[ncolumns++] = COL_CACHE;
+                               compatible = 1;
+                       }
                        break;
                case 's':
                        sysrootlen = strlen(optarg);
@@ -1016,7 +1120,7 @@ int main(int argc, char *argv[])
 
        /* Show time! */
        if (parsable)
-               print_parsable(desc);
+               print_parsable(desc, columns, ncolumns, compatible);
        else
                print_readable(desc, hex);