lscpu: new command

Add a lscpu(1) utility program.

[kzak@redhat.com:
	- indent by linux-2.6/scripts/Lindent
	- add lscpu.{c,1} to sys-utils/Makefile.am
	- add NLS suport
	- complete code refactoring
]

Co-Author: Karel Zak <kzak@redhat.com>
Signed-off-by: Cai Qian <qcai@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Cai Qian 2008-07-17 14:19:40 +02:00 committed by Karel Zak
parent bf09b61a32
commit 5dd7507c18
6 changed files with 564 additions and 2 deletions

View File

@ -2,6 +2,8 @@
The project utils-linux-ng doesn't use same license for all code. There are
code with:
* GPLv3+ (GNU General Public License version 3, or any later version)
* GPLv2+ (GNU General Public License version 2, or any later version)
* GPLv2 (GNU General Public License version 2)

View File

@ -98,6 +98,7 @@ sys-utils/flock.c
sys-utils/ipcrm.c
sys-utils/ipcs.c
sys-utils/ldattach.c
sys-utils/lscpu.c
sys-utils/rdev.c
sys-utils/readprofile.c
sys-utils/renice.c

View File

@ -8,6 +8,7 @@ ldattach
readprofile
renice
rtcwake
lscpu
setarch
setsid
tunelp

View File

@ -7,7 +7,7 @@ endif
usrbinexec_PROGRAMS = flock ipcrm ipcs renice setsid
if LINUX
usrbinexec_PROGRAMS += cytune setarch
usrbinexec_PROGRAMS += cytune setarch lscpu
endif
cytune_SOURCES = cytune.c cyclades.h
@ -25,7 +25,7 @@ tunelp_SOURCES = tunelp.c lp.h
dist_man_MANS = flock.1 readprofile.1 \
ctrlaltdel.8 cytune.8 dmesg.1 ipcrm.1 ipcs.1 ldattach.8 renice.1 \
setsid.1 tunelp.8 setarch.8 rtcwake.8
setsid.1 tunelp.8 setarch.8 rtcwake.8 lscpu.1
info_TEXINFOS = ipc.texi

29
sys-utils/lscpu.1 Normal file
View File

@ -0,0 +1,29 @@
.\" 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@redhat.com>
.SH AVAILABILITY
The setarch command is part of the util-linux-ng package and is available from
ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/.

529
sys-utils/lscpu.c Normal file
View File

@ -0,0 +1,529 @@
/*
* lscpu - CPU architecture information helper
*
* Copyright (C) 2008 Cai Qian <qcai@redhat.com>
* Copyright (C) 2008 Karel Zak <kzak@redhat.com>
*
* 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/utsname.h>
#include <unistd.h>
#include <stdarg.h>
#include "nls.h"
#define CACHE_MAX 100
/* /sys paths */
#define _PATH_SYS_SYSTEM "/sys/devices/system"
#define _PATH_SYS_CPU0 _PATH_SYS_SYSTEM "/cpu/cpu0"
#define _PATH_PROC_XENCAP "/proc/xen/capabilities"
#define _PATH_PROC_CPUINFO "/proc/cpuinfo"
int have_topology;
int have_cache;
int have_node;
/* CPU(s) description */
struct cpu_decs {
/* counters */
int ct_cpu;
int ct_thread;
int ct_core;
int ct_socket;
int ct_node;
int ct_cache;
/* who is who */
char *arch;
char *vendor;
char *family;
char *model;
/* caches */
char *caname[CACHE_MAX];
char *casize[CACHE_MAX];
int camap[CACHE_MAX];
/* misc */
char *mhz;
char *stepping;
char *flags;
/* NUMA */
int *nodecpu;
};
char pathbuf[PATH_MAX];
static void path_scanstr(char *result, const char *path, ...)
__attribute__ ((__format__ (__printf__, 2, 3)));
static int path_exist(const char *path, ...)
__attribute__ ((__format__ (__printf__, 1, 2)));
static int path_sibling(const char *path, ...)
__attribute__ ((__format__ (__printf__, 1, 2)));
static FILE *
xfopen(const char *path, const char *mode)
{
FILE *fd = fopen(path, mode);
if (!fd)
err(EXIT_FAILURE, _("error: %s"), path);
return fd;
}
static FILE *
path_vfopen(const char *mode, const char *path, va_list ap)
{
vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
return xfopen(pathbuf, mode);
}
static void
path_scanstr(char *result, const char *path, ...)
{
FILE *fd;
va_list ap;
va_start(ap, path);
fd = path_vfopen("r", path, ap);
va_end(ap);
if (fscanf(fd, "%s", result) != 1) {
if (ferror(fd))
err(EXIT_FAILURE, _("error: %s"), pathbuf);
else
errx(EXIT_FAILURE, _("error parse: %s"), pathbuf);
}
fclose(fd);
}
static int
path_exist(const char *path, ...)
{
va_list ap;
va_start(ap, path);
vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
va_end(ap);
return access(pathbuf, F_OK) == 0;
}
char *
xstrdup(const char *str)
{
char *s = strdup(str);
if (!s)
err(EXIT_FAILURE, _("error: strdup failed"));
return s;
}
/* count the set bit in a mapping file */
static int
path_sibling(const char *path, ...)
{
int c, n;
int result = 0;
char s[2];
FILE *fp;
va_list ap;
va_start(ap, path);
fp = path_vfopen("r", path, ap);
va_end(ap);
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.
* Format is:
*
* "<pattern> : <key>"
*/
int lookup(char *line, char *pattern, char **value)
{
char *p, *v;
int len = strlen(pattern);
if (!*line)
return 0;
/* pattern */
if (strncmp(line, pattern, len))
return 0;
/* white spaces */
for (p = line + len; isspace(*p); p++);
/* separator */
if (*p != ':')
return 0;
/* white spaces */
for (++p; isspace(*p); p++);
/* value */
if (!*p)
return 0;
v = p;
/* end of value */
len = strlen(line) - 1;
for (p = line + len; isspace(*(p-1)); p--);
*p = '\0';
*value = xstrdup(v);
return 1;
}
static void
read_basicinfo(struct cpu_decs *cpu)
{
FILE *fp = xfopen(_PATH_PROC_CPUINFO, "r");
char buf[BUFSIZ];
struct utsname utsbuf;
/* architecture */
if (uname(&utsbuf) == -1)
err(EXIT_FAILURE, _("error: uname failed"));
cpu->arch = xstrdup(utsbuf.machine);
/* count CPU(s) */
while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", cpu->ct_cpu))
cpu->ct_cpu++;
/* details */
while (fgets(buf, sizeof(buf), fp) != NULL) {
/* IA64 */
if (lookup(buf, "vendor", &cpu->vendor)) ;
else if (lookup(buf, "vendor_id", &cpu->vendor)) ;
/* IA64 */
else if (lookup(buf, "family", &cpu->family)) ;
else if (lookup(buf, "cpu family", &cpu->family)) ;
else if (lookup(buf, "model", &cpu->model)) ;
else if (lookup(buf, "stepping", &cpu->stepping)) ;
else if (lookup(buf, "cpu MHz", &cpu->mhz)) ;
else if (lookup(buf, "flags", &cpu->flags)) ;
else
continue;
}
fclose(fp);
}
static void
read_topology(struct cpu_decs *cpu)
{
/* number of threads */
cpu->ct_thread = path_sibling(
_PATH_SYS_CPU0 "/topology/thread_siblings");
/* number of cores */
cpu->ct_core = path_sibling(
_PATH_SYS_CPU0 "/topology/core_siblings")
/ cpu->ct_thread;
/* number of sockets */
cpu->ct_socket = cpu->ct_cpu / cpu->ct_core / cpu->ct_thread;
}
static void
read_cache(struct cpu_decs *cpu)
{
char buf[256];
DIR *dp;
struct dirent *dir;
int level, type;
dp = opendir(_PATH_SYS_CPU0 "/cache");
if (dp == NULL)
err(EXIT_FAILURE, _("error: %s"), _PATH_SYS_CPU0 "/cache");
while ((dir = readdir(dp)) != NULL) {
if (!strcmp(dir->d_name, ".")
|| !strcmp(dir->d_name, ".."))
continue;
/* cache type */
path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/type", dir->d_name);
if (!strcmp(buf, "Data"))
type = 'd';
else if (!strcmp(buf, "Instruction"))
type = 'i';
else
type = 0;
/* cache level */
path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/level", dir->d_name);
level = atoi(buf);
if (type)
snprintf(buf, sizeof(buf), "L%d%c", level, type);
else
snprintf(buf, sizeof(buf), "L%d", level);
cpu->caname[cpu->ct_cache] = xstrdup(buf);
/* cache size */
path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/size", dir->d_name);
cpu->casize[cpu->ct_cache] = xstrdup(buf);
/* information about how CPUs share different caches */
cpu->camap[cpu->ct_cache] = path_sibling(
_PATH_SYS_CPU0 "/cache/%s/shared_cpu_map",
dir->d_name);
cpu->ct_cache++;
}
}
static void
read_nodes(struct cpu_decs *cpu)
{
int i;
/* number of NUMA node */
while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", cpu->ct_node))
cpu->ct_node++;
cpu->nodecpu = (int *) malloc(cpu->ct_node * sizeof(int));
if (!cpu->nodecpu)
err(EXIT_FAILURE, _("error: malloc failed"));
/* information about how nodes share different CPUs */
for (i = 0; i < cpu->ct_node; i++)
cpu->nodecpu[i] = path_sibling(
_PATH_SYS_SYSTEM "/node/node%d/cpumap",
i);
}
static void
check_system(void)
{
FILE *fd;
char buf[256];
/* Dom0 Kernel gives wrong information. */
fd = fopen(_PATH_PROC_XENCAP, "r");
if (fd) {
if (fscanf(fd, "%s", buf) == 1 && !strcmp(buf, "control_d"))
errx(EXIT_FAILURE,
_("error: Dom0 Kernel is unsupported."));
fclose(fd);
}
/* Read through sysfs. */
if (access(_PATH_SYS_SYSTEM, F_OK))
errx(1, _("error: /sys filesystem is not accessable."));
if (!access(_PATH_SYS_SYSTEM "/node", F_OK))
have_node = 1;
if (!access(_PATH_SYS_CPU0 "/topology/thread_siblings", F_OK))
have_topology = 1;
if (!access(_PATH_SYS_CPU0 "/cache", F_OK))
have_cache = 1;
}
static void
print_parsable(struct cpu_decs *cpu)
{
int i, j;
puts(
"# 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"
"# CPU,Core,Socket,Node");
if (have_cache) {
/* separator between CPU topology and cache information */
putchar(',');
for (i = cpu->ct_cache - 1; i >= 0; i--)
printf(",%s", cpu->caname[i]);
}
putchar('\n');
for (i = 0; i < cpu->ct_cpu; i++) {
printf("%d", i);
if (have_topology)
printf(",%d,%d",
i / cpu->ct_thread,
i / cpu->ct_core / cpu->ct_thread);
else
printf(",,");
if (have_node) {
int c = 0;
for (j = 0; j < cpu->ct_node; j++) {
c += cpu->nodecpu[j];
if (i < c) {
printf(",%d", j);
break;
}
}
} else
putchar(',');
if (have_cache) {
putchar(',');
for (j = cpu->ct_cache - 1; j >= 0; j--) {
/* If shared_cpu_map is 0, all CPUs share the same
cache. */
if (cpu->camap[j] == 0)
cpu->camap[j] = cpu->ct_core *
cpu->ct_thread;
printf(",%d", i / cpu->camap[j]);
}
}
putchar('\n');
}
}
/* output formats "<key> <value>"*/
#define print_s(_key, _val) printf("%-23s%s\n", _key, _val)
#define print_n(_key, _val) printf("%-23s%d\n", _key, _val)
static void
print_readable(struct cpu_decs *cpu)
{
char buf[BUFSIZ];
print_s("Architecture:", cpu->arch);
print_n("CPU(s):", cpu->ct_cpu);
if (have_topology) {
print_n(_("Thread(s) per core:"), cpu->ct_thread);
print_n(_("Core(s) per socket:"), cpu->ct_core);
print_n(_("CPU socket(s):"), cpu->ct_socket);
}
if (have_node)
print_n(_("NUMA node(s):"), cpu->ct_node);
if (cpu->vendor)
print_s(_("Vendor ID:"), cpu->vendor);
if (cpu->family)
print_s(_("CPU family:"), cpu->family);
if (cpu->model)
print_s(_("Model:"), cpu->model);
if (cpu->stepping)
print_s(_("Stepping:"), cpu->stepping);
if (cpu->mhz)
print_s(_("CPU MHz:"), cpu->mhz);
if (cpu->flags) {
snprintf(buf, sizeof(buf), " %s ", cpu->flags);
if (strstr(buf, " svm "))
print_s(_("Virtualization:"), "AMD-V");
else if (strstr(buf, " vmx "))
print_s(_("Virtualization:"), "VT-x");
}
if (have_cache) {
int i;
for (i = cpu->ct_cache - 1; i >= 0; i--) {
snprintf(buf, sizeof(buf),
_("%s cache:"), cpu->caname[i]);
print_s(buf, cpu->casize[i]);
}
}
}
void usage(int rc)
{
printf(_("Usage: %s [option]\n"),
program_invocation_short_name);
puts(_( "CPU architecture information helper\n\n"
" -h, --help usage information\n"
" -p, --parse print out in parsable instead of printable format.\n"));
exit(rc);
}
int main(int argc, char *argv[])
{
struct cpu_decs _cpu, *cpu = &_cpu;
int parsable = 0, c;
struct option longopts[] = {
{ "help", no_argument, 0, 'h' },
{ "parse", no_argument, 0, 'p' },
{ NULL, 0, 0, 0 }
};
setlocale(LC_MESSAGES, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
while((c = getopt_long(argc, argv, "hp", longopts, NULL)) != -1) {
switch (c) {
case 'h':
usage(EXIT_SUCCESS);
case 'p':
parsable = 1;
break;
default:
usage(EXIT_FAILURE);
}
}
memset(cpu, 0, sizeof(*cpu));
check_system();
read_basicinfo(cpu);
if (have_topology)
read_topology(cpu);
if (have_cache)
read_cache(cpu);
if (have_node)
read_nodes(cpu);
/* Show time! */
if (parsable)
print_parsable(cpu);
else
print_readable(cpu);
return EXIT_FAILURE;
}