Hi, Thanks for all your comments. I have rewritten the tool in C, and made several changes. The new output is like the following, $ /usr/bin/lscpu CPU(s): 8 Thread(s) per core: 2 Core(s) per socket: 2 CPU socket(s): 2 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: Itanium 2 Model: 0 CPU MHz: 1598.000005 L1d cache: 16K L1i cache: 16K L2d cache: 256K L2i cache: 1024K L3 cache: 12288K $ /usr/bin/lscpu -p #The following is the parsable format, which can be fed to other #programs. Each different item in every column has a unique ID #starting from zero. # #CPU,Core,Socket,Node,L1d,L1i,L2d,L2i,L3 0,0,0,0,0,0,0,0,0 1,0,0,0,0,0,0,0,0 2,1,0,0,1,1,1,0,0 3,1,0,0,1,1,1,0,0 4,2,1,0,2,2,2,1,1 5,2,1,0,2,2,2,1,1 6,3,1,0,3,3,3,1,1 7,3,1,0,3,3,3,1,1 If you are happy about the output. I'll tidy up the code a little bit, do more testing, and create a manpage. Thanks, CaiQian
/* lscpu - CPU architecture information helper Copyright (C) 2008 Cai Qian <qcai@xxxxxxxxxx> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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, see <http://www.gnu.org/licenses/>. */ #include <ctype.h> #include <dirent.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/utsname.h> #include <unistd.h> #define BASE 2 #define CACHE_MAX 100 #define MARK_LABEL ":" /* Calculate on number of silbings from a decimal mapping. */ int sibling (double mapping) { int i = 0, j = 0; while (mapping != 0) { i++; j = (int) (log (mapping) / log (BASE)); mapping -= pow (BASE, j); } return i; } /* Convert hexdecimal number from a mapping file to decimal.*/ double decimal (char *file) { int i, c; char buf[BUFSIZ]; double result; FILE *fp; fp = fopen (file, "r"); if (fp == NULL) err (1, "fopen %s", file); buf[0] = '0'; buf[1] = 'x'; i = 2; while ((c = fgetc (fp)) != EOF) { if (isxdigit (c)) buf[i++] = c; } buf[i] = '\0'; result = strtod (buf, (char **) NULL); if (errno == ERANGE) err (1, "strtod %s", buf); fclose (fp); return result; } void usage (void) { printf ( "Usage: lscpu [OPTION]\n" "CPU architecture information helper\n" "\n" " -h, --help usage information\n" " -p, --parse print out in parsable instead of printable format." "\n"); } int main (int argc, char *argv[]) { int c, i, j; int parsable = 0, have_topology = 0, have_cache = 0, have_node = 0; int cpu = 0, thread = 0, core = 0, socket = 0, node = 0, ncache = 0; int option_index, camap[CACHE_MAX], nodecpu = 0, pad = 23; char *arch, buf[BUFSIZ], buf2[BUFSIZ], buf3[BUFSIZ]; char *syspath = "/sys/devices/system", *cpu0path, *node0path; char *vendor = NULL, *family = NULL, *model = NULL; char *mhz = "", *stepping = NULL; char *(caname[CACHE_MAX]), *(casize[CACHE_MAX]); FILE *fp; DIR *dp; struct dirent *dir; struct utsname utsbuf; struct stat info; struct option global[] = { {"help", no_argument, 0, 'h'}, {"parse", no_argument, 0, 'p'}, {0, 0, 0, 0} }; for (;;) { option_index = 0; c = getopt_long (argc, argv, "hp", global, &option_index); if (c == -1) break; switch (c) { /* --help */ case 'h': usage (); return 0; /* --parse */ case 'p': parsable = 1; break; default: usage (); exit (1); } } if (uname (&utsbuf) == -1) err (1, "uname"); arch = strdup (utsbuf.machine); if (arch == NULL) err (1, "strdup %s", utsbuf.machine); /* Only i686, X86_64 and IA64 are supported at the moment. */ if (strcmp (arch, "x86_64") && strcmp (arch, "i686") && strcmp (arch, "ia64")) errx (1, "error - %s is not supported.", arch); /* Dom0 Kernel gives wrong information. */ fp = fopen ("/proc/xen/capabilities", "r"); if (fp != NULL) { if (fscanf (fp, "%s", buf) == 1) { if (! strcmp (buf, "control_d")) errx (1, "error - Dom0 Kernel is not supported."); } fclose (fp); } /* Read through sysfs. */ if (stat (syspath, &info) == -1) errx (1, "error - sysfs is not accessable."); sprintf (buf, "%s/cpu/cpu0", syspath); cpu0path = strdup (buf); if (cpu0path == NULL) err (1, "strdup %s", buf); sprintf (buf, "%s/node/node0", syspath); node0path = strdup (buf); if (node0path == NULL) err (1, "strdup %s", buf); if (stat (node0path, &info) == 0) have_node = 1; sprintf (buf, "%s/topology", cpu0path); if (stat (buf, &info) == 0) have_topology = 1; sprintf (buf, "%s/cache", cpu0path); if (stat (buf, &info) == 0) have_cache = 1; /* number of CPUs */ for (;;) { sprintf (buf, "%s/cpu/cpu%d", syspath, cpu); if (stat (buf, &info) == 0) cpu++; else break; } if (have_topology) { /* number of threads */ sprintf (buf, "%s/topology/thread_siblings", cpu0path); thread = sibling (decimal (buf)); /* number of cores */ sprintf (buf, "%s/topology/core_siblings", cpu0path); core = sibling (decimal (buf)) / thread; /* number of sockets */ socket = cpu / core / thread; } /* Read through cpuinfo. */ fp = fopen ("/proc/cpuinfo", "r"); if (fp == NULL) err (1, "fopen cpuinfo"); while (fgets (buf2, BUFSIZ, fp) != NULL) { if (! strcmp (arch, "ia64")) { if (sscanf (buf2, "vendor : %s", buf) == 1) { /* Fix me - magic number. */ i = strcspn (buf2, MARK_LABEL); strcpy (buf, buf2 + i + 2); buf[strlen (buf2) - i - 3] = '\0'; vendor = strdup (buf); if (vendor == NULL) err (1, "strdup %s", buf); } if (sscanf (buf2, "family : %s", buf) == 1) { i = strcspn (buf2, MARK_LABEL); strcpy (buf, buf2 + i + 2); buf[strlen (buf2) - i - 3] = '\0'; family = strdup (buf); if (family == NULL) err (1, "strdup %s", buf); } } else { if (sscanf (buf2, "vendor_id : %s", buf) == 1) { i = strcspn (buf2, MARK_LABEL); strcpy (buf, buf2 + i + 2); buf[strlen (buf2) - i - 3] = '\0'; vendor = strdup (buf); if (vendor == NULL) err (1, "strdup %s", buf); } if (sscanf (buf2, "cpu family : %s", buf) == 1) { i = strcspn (buf2, MARK_LABEL); strcpy (buf, buf2 + i + 2); buf[strlen (buf2) - i - 3] = '\0'; family = strdup (buf); if (family == NULL) err (1, "strdup %s", buf); } } if (sscanf (buf2, "model : %s", buf) == 1) { model = strdup (buf); if (model == NULL) err (1, "strdup %s", buf); } if (sscanf (buf2, "stepping : %s", buf) == 1) { stepping = strdup (buf); if (stepping == NULL) err (1, "strdup %s", buf); } if (sscanf (buf2, "cpu MHz : %s", buf) == 1) { mhz = strdup (buf); if (mhz == NULL) err (1, "strdup %s", buf); } } fclose (fp); /* cache infomation */ if (have_cache) { sprintf (buf, "%s/cache", cpu0path); dp = opendir (buf); if (dp == NULL) err (1, "opendir %s", buf); while ((dir = readdir (dp)) != NULL) { if (! strcmp (dir->d_name, ".") || ! strcmp (dir->d_name, "..")) continue; /* cache level */ sprintf (buf2, "%s/cache/%s/level", cpu0path, dir->d_name); fp = fopen (buf2, "r"); if (fp == NULL) errx (1, "fopen %s", buf2); fscanf (fp, "%s", buf2); fclose (fp); /* cache type */ sprintf (buf3, "%s/cache/%s/type", cpu0path, dir->d_name); fp = fopen (buf3, "r"); if (fp == NULL) err (1, "fopen %s", buf3); fscanf (fp, "%s", buf3); fclose (fp); if (! strcmp (buf3, "Data")) { sprintf (buf, "L%sd", buf2); caname[ncache] = strdup (buf); if (caname[ncache] == NULL) err (1, "strdup %s", buf); } else if (! strcmp (buf3, "Instruction")) { sprintf (buf, "L%si", buf2); caname[ncache] = strdup (buf); if (caname[ncache] == NULL) err (1, "strdup %s", buf); } else { sprintf (buf, "L%s", buf2); caname[ncache] = strdup (buf); if (caname[ncache] == NULL) err (1, "strdup %s", buf); } /* cache size */ sprintf (buf, "%s/cache/%s/size", cpu0path, dir->d_name); fp = fopen (buf, "r"); if (fp == NULL) err (1, "fopen %s", buf); fscanf (fp, "%s", buf); casize[ncache] = strdup (buf); if (casize[ncache] == NULL) err (1, "strdup %s", buf); fclose (fp); /* information about how CPUs share different caches */ sprintf (buf, "%s/cache/%s/shared_cpu_map", cpu0path, dir->d_name); camap[ncache] = sibling (decimal (buf)); ncache++; } } if (have_node) { /* number of NUMA node */ for (;;) { sprintf (buf, "%s/node/node%d", syspath, node); if (stat (buf, &info) == 0) node++; else break; } /* information about how nodes share different CPUs */ sprintf (buf, "%s/cpumap", node0path); nodecpu = sibling (decimal (buf)); } /* Show time! */ if (parsable) { if (! have_topology) errx (1, ( "error - CPU topology information is " "required for the parsable format.")); printf ( "#The following is the parsable format, which can be " "fed to other\n#programs. Each different item in " "every column has a unique ID\n#starting from zero.\n" "#\n" "#CPU,Core,Socket"); if (have_node) printf (",Node"); if (have_cache) { for (i = ncache - 1; i >= 0; i--) printf (",%s", caname[i]); } putchar ('\n'); for (i = 0; i < cpu; i++) { printf ("%d,%d,%d", i, (int) (i / thread), (int) (i / core / thread)); if (have_node) printf (",%d", (int) (i / nodecpu)); if (have_cache) { for (j = ncache - 1; j >= 0; j--) { /* If shared_cpu_map is 0, all CPUs share the same cache. */ if (camap[j] == 0) camap[j] = core * thread; printf (",%d", (int) (i / camap[j])); } } putchar ('\n'); } } else { printf ("%-*s%d\n", pad, "CPU(s):", cpu); if (have_topology) { printf ("%-*s%d\n", pad, "Thread(s) per core:", thread); printf ("%-*s%d\n", pad, "Core(s) per socket:", core); printf ("%-*s%d\n", pad, "CPU socket(s):", socket); } if (have_node) printf ("%-*s%d\n", pad, "NUMA node(s):", node); if (vendor != NULL) printf ("%-*s%s\n", pad, "Vendor ID:", vendor); if (family != NULL) printf ("%-*s%s\n", pad, "CPU family:", family); if (model != NULL) printf ("%-*s%s\n", pad, "Model:", model); if (stepping != NULL) printf ("%-*s%s\n", pad, "Stepping:", stepping); if (mhz != NULL) printf ("%-*s%s\n", pad, "CPU MHz:", mhz); if (have_cache) { for (i = ncache - 1; i >= 0; i--) { sprintf (buf, "%s cache:", caname[i]); printf ("%-*s%s\n", pad, buf, casize[i]); } } } return 0; }