[RESEND][RFC] lscpu - CPU architecture information helper

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
}

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux