[RC] lscpu - CPU architecture information helper

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

 



Hi,

I attached the ready-to-release version of this new tool, and its
manpage. It has been tested successfully on various of machines
including i686, x86_64, ia64, ppc64 and s390(x). The final output will
like the following,

$ /usr/bin/lscpu 
Architecture:          x86_64
CPU(s):                8
Thread(s) per core:    1
Core(s) per socket:    4
CPU socket(s):         2
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 15
Stepping:              11
CPU MHz:               1862.010
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              4096K

$ /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,L2
0,0,0,0,0,0,0
1,1,0,0,1,1,0
2,2,0,0,2,2,1
3,3,0,0,3,3,1
4,4,1,0,4,4,2
5,5,1,0,5,5,2
6,6,1,0,6,6,3
7,7,1,0,7,7,3

Thanks,
CaiQian
.\" Process this file with
.\" groff -man -Tascii lscpu.1
.\"
.TH LSCPU 1 "JULY 2008" Linux "User Manuals"
.SH NAME
lscpu \- CPU architecture information helper
.SH SYNOPSIS
.B lscpu [-hp]
.SH DESCRIPTION
.B lscpu
gathers CPU architecture information like number of CPUs, threads,
cores, sockets, NUMA nodes, information about CPU caches, CPU family,
model and stepping from sysfs and /proc/cpuinfo, and prints it in
human-readable format. Alternatively, it can print out in parsable
format including how different caches are shared by different CPUs,
which can also be fed to other programs.
.SH OPTIONS
.IP -h, --help
Print a help message.
.IP -p, --parse
Print out in parsable instead of printable format.
.SH BUGS
The program at the moment does not handle the system installed with
different types of physical processors.
.SH AUTHOR
Cai Qian <qcai@xxxxxxxxxx>
/*
  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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#define CACHE_MAX 100

/* count the set bit in a mapping file */
int
sibling (char *file)
{
  int c, n;
  int result = 0;
  char s[2];
  FILE *fp;

  fp = fopen (file, "r");
  if (fp == NULL)
    err (1, "fopen %s", file);

  while ((c = fgetc (fp)) != EOF)
    {
      if (isxdigit (c))
        {
          s[0] = c;
          s[1] = '\0';
          for (n = strtol (s, NULL, 16); n > 0; n /= 2)
            {
              if (n % 2)
                result++;
            }
        }
    }
  fclose (fp);

  return result;
}

/* Lookup a pattern and get the value from cpuinfo*/
int
lookup (char *line, char *pattern, char **value)
{
  char buf[BUFSIZ], buf2[BUFSIZ];
  int i = 0, j = 0;

  /* empty line */
  if (line[0] == '\0')
    return 0;

  snprintf (buf2, BUFSIZ, "%s : %%s", pattern);
  if (sscanf (line, buf2, buf) != 1)
    return 0;

  i = strcspn (line, ":");
  strncpy (buf, line + i, strlen (line + i) + 1);

  /* Get the value. */
  for (i = 0; isspace (buf[i]) || buf[i] == ':'; i++);
  for (j = strlen (buf) - 1; isspace (buf[j]); j--);
  buf[j + 1] = '\0';

  *value = strdup (buf + i);
  if (*value == NULL)
    err (1, "strdup %s", buf + i);

  return 1;
}

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, pad = 23;
  char buf[BUFSIZ], buf2[BUFSIZ], buf3[BUFSIZ];
  char *syspath = "/sys/devices/system", *cpu0path;
  char *arch, *vendor = NULL, *family = NULL, *model = NULL;
  char *mhz = NULL, *stepping = NULL, *flags = 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);

  /* 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.");

  snprintf (buf, BUFSIZ, "%s/cpu/cpu0", syspath);
  cpu0path = strdup (buf);
  if (cpu0path == NULL)
    err (1, "strdup %s", buf);

  snprintf (buf, BUFSIZ, "%s/node", syspath);
  if (stat (buf, &info) == 0)
    have_node = 1;

  snprintf (buf, BUFSIZ, "%s/topology/thread_siblings", cpu0path);
  if (stat (buf, &info) == 0)
    have_topology = 1;

  snprintf (buf, BUFSIZ, "%s/cache", cpu0path);
  if (stat (buf, &info) == 0)
    have_cache = 1;

  /* number of CPUs */
  for (;;)
    {
      snprintf (buf, BUFSIZ, "%s/cpu/cpu%d", syspath, cpu);
      if (stat (buf, &info) == 0)
        cpu++;
      else
        break;
    }

  if (have_topology)
    {
      /* number of threads */
      snprintf (buf, BUFSIZ, "%s/topology/thread_siblings", cpu0path);
      thread = sibling (buf);

      /* number of cores */
      snprintf (buf, BUFSIZ, "%s/topology/core_siblings", cpu0path);
      core = sibling (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)
    {
      /* IA64 */
      if (lookup (buf2, "vendor", &vendor))
        ;
      else if (lookup (buf2, "vendor_id", &vendor))
        ;
      /* IA64 */
      else if (lookup (buf2, "family", &family))
        ;
      else if (lookup (buf2, "cpu family", &family))
        ;
      else if (lookup (buf2, "model", &model))
        ;
      else if (lookup (buf2, "stepping", &stepping))
        ;
      else if (lookup (buf2, "cpu MHz", &mhz))
        ;
      else if (lookup (buf2, "flags", &flags))
        ;
      else
        continue;
    }
  fclose (fp);

  /* cache infomation */
  if (have_cache)
    {
      snprintf (buf, BUFSIZ, "%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 */
          snprintf (buf2, BUFSIZ, "%s/cache/%s/level", cpu0path, dir->d_name);
          fp = fopen (buf2, "r");
          if (fp == NULL)
            err (1, "fopen %s", buf2);

          fscanf (fp, "%s", buf2);
          fclose (fp);

          /* cache type */
          snprintf (buf3, BUFSIZ, "%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"))
            {
              snprintf (buf, BUFSIZ, "L%sd", buf2);
              caname[ncache] = strdup (buf);
              if (caname[ncache] == NULL)
                err (1, "strdup %s", buf);
            }
          else if (! strcmp (buf3, "Instruction"))
            {
              snprintf (buf, BUFSIZ, "L%si", buf2);
              caname[ncache] = strdup (buf);
              if (caname[ncache] == NULL)
                err (1, "strdup %s", buf);
            }
          else
            {
              snprintf (buf, BUFSIZ, "L%s", buf2);
              caname[ncache] = strdup (buf);
              if (caname[ncache] == NULL)
                err (1, "strdup %s", buf);
            }

          /* cache size */
          snprintf (buf, BUFSIZ, "%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 */
          snprintf (buf, BUFSIZ, "%s/cache/%s/shared_cpu_map",
                    cpu0path, dir->d_name);
          camap[ncache] = sibling (buf);

          ncache++;
        }
    }

  if (have_node)
    {
      /* number of NUMA node */
      for (;;)
        {
          snprintf (buf, BUFSIZ, "%s/node/node%d", syspath, node);
          if (stat (buf, &info) == 0)
            node++;
          else
            break;
        }

      nodecpu = (int *) malloc (node * sizeof (int));
      if (nodecpu == NULL)
        err (1, "malloc nodecpu");

      /* information about how nodes share different CPUs */
      for (i = 0; i < node; i++)
        {
          snprintf (buf, BUFSIZ, "%s/node/node%d/cpumap", syspath, i);
          nodecpu[i] = sibling (buf);
        }
    }

  /* Show time! */
  if (parsable)
    {
      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,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", i);

          if (have_topology)
            printf (",%d,%d", (int) (i / thread),
                    (int) (i / core / thread));
          else
            printf (",,");

          if (have_node)
            {
              c = 0;
              for (j = 0; j < node; j++)
                {
                  c += nodecpu[j];
                  if (i < c)
                    {
                      printf (",%d", j);
                      break;
                    }
                }
            }
          else
            putchar (',');

          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%s\n", pad, "Architecture:", arch);
      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 (flags != NULL)
        {
          snprintf (buf, BUFSIZ, " %s ", flags);
          if (strstr (buf, " svm "))
            printf ("%-*s%s\n", pad, "Virtualization:", "AMD-V");
          else if (strstr (buf, " vmx "))
            printf ("%-*s%s\n", pad, "Virtualization:", "VT-x");
        }

      if (have_cache)
        {
          for (i = ncache - 1; i >= 0; i--)
            {
              snprintf (buf, BUFSIZ, "%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