]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/lscpu.c
prlimit: use TT_FL_FREEDATA for tt table
[thirdparty/util-linux.git] / sys-utils / lscpu.c
CommitLineData
5dd7507c
CQ
1/*
2 * lscpu - CPU architecture information helper
3 *
4 * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
5 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
6 *
80dde62f 7 * This program is free software; you can redistribute it and/or modify
5dd7507c 8 * it under the terms of the GNU General Public License as published by
80dde62f 9 * the Free Software Foundation; either version 2 of the License, or
5dd7507c
CQ
10 * (at your option) any later version.
11 *
80dde62f 12 * This program is distributed in the hope that it would be useful,
5dd7507c
CQ
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
7cebf0bb
SK
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
5dd7507c
CQ
20 */
21
22#include <ctype.h>
23#include <dirent.h>
5dd7507c
CQ
24#include <errno.h>
25#include <fcntl.h>
26#include <getopt.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/utsname.h>
31#include <unistd.h>
32#include <stdarg.h>
39561c70
KZ
33#include <sys/types.h>
34#include <sys/stat.h>
5dd7507c 35
7e03f383 36#include "cpuset.h"
5dd7507c 37#include "nls.h"
4581647a 38#include "xalloc.h"
eb76ca98 39#include "c.h"
477251f8
KZ
40#include "strutils.h"
41#include "bitops.h"
ba45d8c1 42#include "tt.h"
8148217b 43#include "path.h"
efb8854f 44#include "closestream.h"
41a8940d 45#include "optutils.h"
fb2627ce 46#include "lscpu.h"
477251f8 47
5dd7507c
CQ
48#define CACHE_MAX 100
49
50/* /sys paths */
d50363cd 51#define _PATH_SYS_SYSTEM "/sys/devices/system"
7e03f383 52#define _PATH_SYS_CPU _PATH_SYS_SYSTEM "/cpu"
d50363cd 53#define _PATH_PROC_XEN "/proc/xen"
c8b64f6d 54#define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
d50363cd
KZ
55#define _PATH_PROC_CPUINFO "/proc/cpuinfo"
56#define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
b8ec7bdf 57#define _PATH_PROC_SYSINFO "/proc/sysinfo"
fb2627ce
OO
58#define _PATH_PROC_STATUS "/proc/self/status"
59#define _PATH_PROC_VZ "/proc/vz"
60#define _PATH_PROC_BC "/proc/bc"
61#define _PATH_DEV_MEM "/dev/mem"
5dd7507c 62
c8b64f6d
KZ
63/* virtualization types */
64enum {
65 VIRT_NONE = 0,
66 VIRT_PARA,
857d9cdf
KZ
67 VIRT_FULL,
68 VIRT_CONT
c8b64f6d
KZ
69};
70const char *virt_types[] = {
71 [VIRT_NONE] = N_("none"),
72 [VIRT_PARA] = N_("para"),
857d9cdf
KZ
73 [VIRT_FULL] = N_("full"),
74 [VIRT_CONT] = N_("container"),
c8b64f6d
KZ
75};
76
c8b64f6d
KZ
77const char *hv_vendors[] = {
78 [HYPER_NONE] = NULL,
79 [HYPER_XEN] = "Xen",
80 [HYPER_KVM] = "KVM",
99fbc877 81 [HYPER_MSHV] = "Microsoft",
b8ec7bdf 82 [HYPER_VMWARE] = "VMware",
fb2627ce
OO
83 [HYPER_IBM] = "IBM",
84 [HYPER_VSERVER] = "Linux-VServer",
85 [HYPER_UML] = "User-mode Linux",
86 [HYPER_INNOTEK] = "Innotek GmbH",
87 [HYPER_HITACHI] = "Hitachi",
857d9cdf 88 [HYPER_PARALLELS] = "Parallels"
c8b64f6d
KZ
89};
90
f633ad4e 91/* CPU modes */
79e8b41a 92enum {
f633ad4e
KZ
93 MODE_32BIT = (1 << 1),
94 MODE_64BIT = (1 << 2)
79e8b41a 95};
c8b64f6d 96
e8aa16ee
KZ
97/* cache(s) description */
98struct cpu_cache {
7e03f383
KZ
99 char *name;
100 char *size;
101
102 int nsharedmaps;
103 cpu_set_t **sharedmaps;
e8aa16ee
KZ
104};
105
a0fff77e
HC
106/* dispatching modes */
107enum {
108 DISP_HORIZONTAL = 0,
109 DISP_VERTICAL = 1
110};
111
112const char *disp_modes[] = {
113 [DISP_HORIZONTAL] = N_("horizontal"),
114 [DISP_VERTICAL] = N_("vertical")
115};
116
2b8fcb81
HC
117/* cpu polarization */
118enum {
119 POLAR_UNKNOWN = 0,
120 POLAR_VLOW,
121 POLAR_VMEDIUM,
122 POLAR_VHIGH,
123 POLAR_HORIZONTAL
124};
125
8005924a
KZ
126struct polarization_modes {
127 char *parsable;
128 char *readable;
129};
130
131struct polarization_modes polar_modes[] = {
132 [POLAR_UNKNOWN] = {"U", "-"},
133 [POLAR_VLOW] = {"VL", "vert-low"},
134 [POLAR_VMEDIUM] = {"VM", "vert-medium"},
135 [POLAR_VHIGH] = {"VH", "vert-high"},
136 [POLAR_HORIZONTAL] = {"H", "horizontal"},
2b8fcb81
HC
137};
138
e8aa16ee
KZ
139/* global description */
140struct lscpu_desc {
5dd7507c
CQ
141 char *arch;
142 char *vendor;
143 char *family;
144 char *model;
1461094c 145 char *modelname;
c8b64f6d 146 char *virtflag; /* virtualization flag (vmx, svm) */
10829cd7 147 char *hypervisor; /* hypervisor software */
c8b64f6d
KZ
148 int hyper; /* hypervisor vendor ID */
149 int virtype; /* VIRT_PARA|FULL|NONE ? */
5dd7507c 150 char *mhz;
44320710 151 char **mmhz; /* maximum mega hertz */
5dd7507c 152 char *stepping;
9b8d4d5f 153 char *bogomips;
5dd7507c 154 char *flags;
a0fff77e 155 int dispatching; /* none, horizontal or vertical */
79e8b41a
KZ
156 int mode; /* rm, lm or/and tm */
157
a5cfffff
TK
158 int ncpuspos; /* maximal possible CPUs */
159 int ncpus; /* number of present CPUs */
160 cpu_set_t *present; /* mask with present CPUs */
aac1e59e 161 cpu_set_t *online; /* mask with online CPUs */
7e03f383
KZ
162
163 int nnodes; /* number of NUMA modes */
164 cpu_set_t **nodemaps; /* array with NUMA nodes */
165
56baaa4e
HC
166 /* books -- based on book_siblings (internal kernel map of cpuX's
167 * hardware threads within the same book */
168 int nbooks; /* number of all online books */
169 cpu_set_t **bookmaps; /* unique book_siblings */
170
7e03f383
KZ
171 /* sockets -- based on core_siblings (internal kernel map of cpuX's
172 * hardware threads within the same physical_package_id (socket)) */
e282eec2 173 int nsockets; /* number of all online sockets */
7e03f383
KZ
174 cpu_set_t **socketmaps; /* unique core_siblings */
175
176 /* cores -- based on thread_siblings (internel kernel map of cpuX's
177 * hardware threads within the same core as cpuX) */
e282eec2 178 int ncores; /* number of all online cores */
7e03f383
KZ
179 cpu_set_t **coremaps; /* unique thread_siblings */
180
e282eec2 181 int nthreads; /* number of online threads */
7e03f383
KZ
182
183 int ncaches;
184 struct cpu_cache *caches;
2b8fcb81
HC
185
186 int *polarization; /* cpu polarization */
596b8845 187 int *addresses; /* physical cpu addresses */
d231eea1 188 int *configured; /* cpu configured */
5dd7507c
CQ
189};
190
8005924a
KZ
191enum {
192 OUTPUT_SUMMARY = 0, /* default */
193 OUTPUT_PARSABLE, /* -p */
ba45d8c1 194 OUTPUT_READABLE, /* -e */
8005924a
KZ
195};
196
8148217b
HC
197enum {
198 SYSTEM_LIVE = 0, /* analyzing a live system */
199 SYSTEM_SNAPSHOT, /* analyzing a snapshot of a different system */
200};
201
8005924a 202struct lscpu_modifier {
a7e5300c 203 int mode; /* OUTPUT_* */
8148217b 204 int system; /* SYSTEM_* */
a7e5300c
HC
205 unsigned int hex:1, /* print CPU masks rather than CPU lists */
206 compat:1, /* use backwardly compatible format */
7afc2387
HC
207 online:1, /* print online CPUs */
208 offline:1; /* print offline CPUs */
8005924a
KZ
209};
210
7e03f383 211static int maxcpus; /* size in bits of kernel cpu mask */
5dd7507c 212
aac1e59e 213#define is_cpu_online(_d, _cpu) \
a5cfffff
TK
214 ((_d) && (_d)->online ? \
215 CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->online) : 0)
216#define is_cpu_present(_d, _cpu) \
217 ((_d) && (_d)->present ? \
218 CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->present) : 0)
aac1e59e 219
477251f8 220/*
3d27b76a 221 * IDs
477251f8
KZ
222 */
223enum {
224 COL_CPU,
225 COL_CORE,
226 COL_SOCKET,
227 COL_NODE,
228 COL_BOOK,
2b8fcb81 229 COL_CACHE,
596b8845 230 COL_POLARIZATION,
d231eea1
HC
231 COL_ADDRESS,
232 COL_CONFIGURED,
a7e5300c 233 COL_ONLINE,
44320710 234 COL_MMHZ,
477251f8
KZ
235};
236
3d27b76a
KZ
237/* column description
238 */
239struct lscpu_coldesc {
240 const char *name;
241 const char *help;
242
243 unsigned int is_abbr:1; /* name is abbreviation */
477251f8
KZ
244};
245
3d27b76a
KZ
246static struct lscpu_coldesc coldescs[] =
247{
248 [COL_CPU] = { "CPU", N_("logical CPU number"), 1 },
249 [COL_CORE] = { "CORE", N_("logical core number") },
250 [COL_SOCKET] = { "SOCKET", N_("logical socket number") },
251 [COL_NODE] = { "NODE", N_("logical NUMA node number") },
252 [COL_BOOK] = { "BOOK", N_("logical book number") },
253 [COL_CACHE] = { "CACHE", N_("shows how caches are shared between CPUs") },
254 [COL_POLARIZATION] = { "POLARIZATION", N_("CPU dispatching mode on virtual hardware") },
255 [COL_ADDRESS] = { "ADDRESS", N_("physical address of a CPU") },
256 [COL_CONFIGURED] = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU") },
44320710
SK
257 [COL_ONLINE] = { "ONLINE", N_("shows if Linux currently makes use of the CPU") },
258 [COL_MMHZ] = { "MMHZ", N_("shows the maximum mhz of the CPU") }
3d27b76a 259};
477251f8 260
583f14cc
KZ
261static int
262column_name_to_id(const char *name, size_t namesz)
477251f8 263{
329fd1c3 264 size_t i;
477251f8 265
3d27b76a
KZ
266 for (i = 0; i < ARRAY_SIZE(coldescs); i++) {
267 const char *cn = coldescs[i].name;
477251f8
KZ
268
269 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
270 return i;
271 }
272 warnx(_("unknown column: %s"), name);
273 return -1;
274}
275
5dd7507c
CQ
276/* Lookup a pattern and get the value from cpuinfo.
277 * Format is:
278 *
279 * "<pattern> : <key>"
280 */
583f14cc
KZ
281static int
282lookup(char *line, char *pattern, char **value)
5dd7507c
CQ
283{
284 char *p, *v;
285 int len = strlen(pattern);
286
287 if (!*line)
288 return 0;
289
290 /* pattern */
291 if (strncmp(line, pattern, len))
292 return 0;
293
294 /* white spaces */
295 for (p = line + len; isspace(*p); p++);
296
297 /* separator */
298 if (*p != ':')
299 return 0;
300
301 /* white spaces */
302 for (++p; isspace(*p); p++);
303
304 /* value */
305 if (!*p)
306 return 0;
307 v = p;
308
309 /* end of value */
310 len = strlen(line) - 1;
311 for (p = line + len; isspace(*(p-1)); p--);
312 *p = '\0';
313
314 *value = xstrdup(v);
315 return 1;
316}
317
f633ad4e
KZ
318/* Don't init the mode for platforms where we are not able to
319 * detect that CPU supports 64-bit mode.
320 */
321static int
8148217b 322init_mode(struct lscpu_modifier *mod)
f633ad4e
KZ
323{
324 int m = 0;
325
8148217b 326 if (mod->system == SYSTEM_SNAPSHOT)
4e740fd8
KZ
327 /* reading info from any /{sys,proc} dump, don't mix it with
328 * information about our real CPU */
329 return 0;
330
f633ad4e
KZ
331#if defined(__alpha__) || defined(__ia64__)
332 m |= MODE_64BIT; /* 64bit platforms only */
333#endif
334 /* platforms with 64bit flag in /proc/cpuinfo, define
335 * 32bit default here */
336#if defined(__i386__) || defined(__x86_64__) || \
c487c90c 337 defined(__s390x__) || defined(__s390__) || defined(__sparc_v9__)
f633ad4e
KZ
338 m |= MODE_32BIT;
339#endif
340 return m;
341}
342
5dd7507c 343static void
8148217b 344read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
5dd7507c 345{
d50363cd 346 FILE *fp = path_fopen("r", 1, _PATH_PROC_CPUINFO);
5dd7507c
CQ
347 char buf[BUFSIZ];
348 struct utsname utsbuf;
a5cfffff 349 size_t setsize;
5dd7507c
CQ
350
351 /* architecture */
352 if (uname(&utsbuf) == -1)
353 err(EXIT_FAILURE, _("error: uname failed"));
e8aa16ee 354 desc->arch = xstrdup(utsbuf.machine);
5dd7507c 355
5dd7507c
CQ
356 /* details */
357 while (fgets(buf, sizeof(buf), fp) != NULL) {
e8aa16ee
KZ
358 if (lookup(buf, "vendor", &desc->vendor)) ;
359 else if (lookup(buf, "vendor_id", &desc->vendor)) ;
e8aa16ee
KZ
360 else if (lookup(buf, "family", &desc->family)) ;
361 else if (lookup(buf, "cpu family", &desc->family)) ;
362 else if (lookup(buf, "model", &desc->model)) ;
1461094c 363 else if (lookup(buf, "model name", &desc->modelname)) ;
e8aa16ee
KZ
364 else if (lookup(buf, "stepping", &desc->stepping)) ;
365 else if (lookup(buf, "cpu MHz", &desc->mhz)) ;
f633ad4e
KZ
366 else if (lookup(buf, "flags", &desc->flags)) ; /* x86 */
367 else if (lookup(buf, "features", &desc->flags)) ; /* s390 */
c487c90c 368 else if (lookup(buf, "type", &desc->flags)) ; /* sparc64 */
9b8d4d5f 369 else if (lookup(buf, "bogomips", &desc->bogomips)) ;
abcd6368 370 else if (lookup(buf, "bogomips per cpu", &desc->bogomips)) ; /* s390 */
5dd7507c
CQ
371 else
372 continue;
373 }
c8b64f6d 374
8148217b 375 desc->mode = init_mode(mod);
f633ad4e 376
e8aa16ee
KZ
377 if (desc->flags) {
378 snprintf(buf, sizeof(buf), " %s ", desc->flags);
c8b64f6d 379 if (strstr(buf, " svm "))
8290a249 380 desc->virtflag = xstrdup("svm");
c8b64f6d 381 else if (strstr(buf, " vmx "))
8290a249 382 desc->virtflag = xstrdup("vmx");
79e8b41a 383 if (strstr(buf, " lm "))
f633ad4e
KZ
384 desc->mode |= MODE_32BIT | MODE_64BIT; /* x86_64 */
385 if (strstr(buf, " zarch "))
386 desc->mode |= MODE_32BIT | MODE_64BIT; /* s390x */
c487c90c
KZ
387 if (strstr(buf, " sun4v ") || strstr(buf, " sun4u "))
388 desc->mode |= MODE_32BIT | MODE_64BIT; /* sparc64 */
c8b64f6d
KZ
389 }
390
4581b716
KZ
391 if (desc->arch && mod->system != SYSTEM_SNAPSHOT) {
392 if (strcmp(desc->arch, "ppc64") == 0)
393 desc->mode |= MODE_32BIT | MODE_64BIT;
394 else if (strcmp(desc->arch, "ppc") == 0)
395 desc->mode |= MODE_32BIT;
396 }
397
5dd7507c 398 fclose(fp);
7e03f383
KZ
399
400 if (path_exist(_PATH_SYS_SYSTEM "/cpu/kernel_max"))
3d6e5c35 401 /* note that kernel_max is maximum index [NR_CPUS-1] */
ca01695b 402 maxcpus = path_read_s32(_PATH_SYS_SYSTEM "/cpu/kernel_max") + 1;
7e03f383 403
8148217b 404 else if (mod->system == SYSTEM_LIVE)
7e03f383
KZ
405 /* the root is '/' so we are working with data from the current kernel */
406 maxcpus = get_max_number_of_cpus();
701e2b8e
KZ
407
408 if (maxcpus <= 0)
409 /* error or we are reading some /sys snapshot instead of the
410 * real /sys, let's use any crazy number... */
a5cfffff
TK
411 maxcpus = 2048;
412
413 setsize = CPU_ALLOC_SIZE(maxcpus);
414
415 if (path_exist(_PATH_SYS_SYSTEM "/cpu/possible")) {
ca01695b 416 cpu_set_t *tmp = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/possible");
a5cfffff
TK
417 desc->ncpuspos = CPU_COUNT_S(setsize, tmp);
418 cpuset_free(tmp);
419 } else
420 err(EXIT_FAILURE, _("failed to determine number of CPUs: %s"),
421 _PATH_SYS_SYSTEM "/cpu/possible");
422
423
424 /* get mask for present CPUs */
425 if (path_exist(_PATH_SYS_SYSTEM "/cpu/present")) {
ca01695b 426 desc->present = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/present");
a5cfffff
TK
427 desc->ncpus = CPU_COUNT_S(setsize, desc->present);
428 }
aac1e59e
KZ
429
430 /* get mask for online CPUs */
e282eec2 431 if (path_exist(_PATH_SYS_SYSTEM "/cpu/online")) {
ca01695b 432 desc->online = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/online");
e282eec2
KZ
433 desc->nthreads = CPU_COUNT_S(setsize, desc->online);
434 }
a0fff77e
HC
435
436 /* get dispatching mode */
437 if (path_exist(_PATH_SYS_SYSTEM "/cpu/dispatching"))
ca01695b 438 desc->dispatching = path_read_s32(_PATH_SYS_SYSTEM "/cpu/dispatching");
a0fff77e
HC
439 else
440 desc->dispatching = -1;
5dd7507c
CQ
441}
442
c8b64f6d 443static int
140014a4 444has_pci_device(unsigned int vendor, unsigned int device)
c8b64f6d
KZ
445{
446 FILE *f;
13cf6fc8 447 unsigned int num, fn, ven, dev;
c8b64f6d
KZ
448 int res = 1;
449
d50363cd 450 f = path_fopen("r", 0, _PATH_PROC_PCIDEVS);
c8b64f6d
KZ
451 if (!f)
452 return 0;
453
454 /* for more details about bus/pci/devices format see
455 * drivers/pci/proc.c in linux kernel
456 */
457 while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
458 &num, &fn, &ven, &dev) == 4) {
459
460 if (ven == vendor && dev == device)
461 goto found;
462 }
463
464 res = 0;
465found:
466 fclose(f);
467 return res;
468}
469
470#if defined(__x86_64__) || defined(__i386__)
471
472/*
473 * This CPUID leaf returns the information about the hypervisor.
474 * EAX : maximum input value for CPUID supported by the hypervisor.
475 * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
476 */
477#define HYPERVISOR_INFO_LEAF 0x40000000
478
479static inline void
480cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
481 unsigned int *ecx, unsigned int *edx)
482{
c9239f23
MF
483 __asm__(
484#if defined(__PIC__) && defined(__i386__)
485 /* x86 PIC cannot clobber ebx -- gcc bitches */
486 "pushl %%ebx;"
487 "cpuid;"
488 "movl %%ebx, %%esi;"
489 "popl %%ebx;"
490 : "=S" (*ebx),
491#else
492 "cpuid;"
493 : "=b" (*ebx),
494#endif
495 "=a" (*eax),
c8b64f6d
KZ
496 "=c" (*ecx),
497 "=d" (*edx)
bc54770d 498 : "1" (op), "c"(0));
c8b64f6d
KZ
499}
500
501static void
e8aa16ee 502read_hypervisor_cpuid(struct lscpu_desc *desc)
c8b64f6d
KZ
503{
504 unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
505 char hyper_vendor_id[13];
506
507 memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
508
509 cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
510 memcpy(hyper_vendor_id + 0, &ebx, 4);
511 memcpy(hyper_vendor_id + 4, &ecx, 4);
512 memcpy(hyper_vendor_id + 8, &edx, 4);
513 hyper_vendor_id[12] = '\0';
514
515 if (!hyper_vendor_id[0])
516 return;
517
518 if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
e8aa16ee 519 desc->hyper = HYPER_XEN;
c8b64f6d 520 else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
e8aa16ee 521 desc->hyper = HYPER_KVM;
c8b64f6d 522 else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
e8aa16ee 523 desc->hyper = HYPER_MSHV;
99fbc877
SH
524 else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
525 desc->hyper = HYPER_VMWARE;
c8b64f6d
KZ
526}
527
528#else /* ! __x86_64__ */
529static void
6d791b4c 530read_hypervisor_cpuid(struct lscpu_desc *desc __attribute__((__unused__)))
c8b64f6d
KZ
531{
532}
533#endif
534
535static void
eff79ceb 536read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod)
c8b64f6d 537{
fb2627ce
OO
538 FILE *fd;
539
540 if (mod->system != SYSTEM_SNAPSHOT) {
eff79ceb 541 read_hypervisor_cpuid(desc);
fb2627ce
OO
542 if (!desc->hyper)
543 desc->hyper = read_hypervisor_dmi();
544 }
c8b64f6d 545
e8aa16ee 546 if (desc->hyper)
e8aa16ee 547 desc->virtype = VIRT_FULL;
c8b64f6d 548
fb2627ce 549 /* Xen para-virt or dom0 */
d50363cd 550 else if (path_exist(_PATH_PROC_XEN)) {
c8b64f6d 551 int dom0 = 0;
fb2627ce 552 fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
c8b64f6d
KZ
553
554 if (fd) {
555 char buf[256];
556
557 if (fscanf(fd, "%s", buf) == 1 &&
558 !strcmp(buf, "control_d"))
559 dom0 = 1;
560 fclose(fd);
561 }
e8aa16ee
KZ
562 desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
563 desc->hyper = HYPER_XEN;
c8b64f6d 564
fb2627ce 565 /* Xen full-virt on non-x86_64 */
c8b64f6d 566 } else if (has_pci_device(0x5853, 0x0001)) {
e8aa16ee
KZ
567 desc->hyper = HYPER_XEN;
568 desc->virtype = VIRT_FULL;
fb2627ce
OO
569
570 /* IBM PR/SM */
b8ec7bdf 571 } else if (path_exist(_PATH_PROC_SYSINFO)) {
145a3ca3 572 FILE *sysinfo_fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
b8ec7bdf
HC
573 char buf[BUFSIZ];
574
145a3ca3 575 if (!sysinfo_fd)
b3eff5dd 576 return;
b8ec7bdf 577 desc->hyper = HYPER_IBM;
10829cd7 578 desc->hypervisor = "PR/SM";
b8ec7bdf 579 desc->virtype = VIRT_FULL;
145a3ca3 580 while (fgets(buf, sizeof(buf), sysinfo_fd) != NULL) {
10829cd7
HC
581 char *str;
582
b8ec7bdf
HC
583 if (!strstr(buf, "Control Program:"))
584 continue;
585 if (!strstr(buf, "KVM"))
586 desc->hyper = HYPER_IBM;
587 else
588 desc->hyper = HYPER_KVM;
10829cd7
HC
589 str = strchr(buf, ':');
590 if (!str)
591 continue;
9d8537ff
KZ
592 xasprintf(&str, "%s", str + 1);
593
10829cd7
HC
594 /* remove leading, trailing and repeating whitespace */
595 while (*str == ' ')
596 str++;
597 desc->hypervisor = str;
598 str += strlen(str) - 1;
599 while ((*str == '\n') || (*str == ' '))
600 *(str--) = '\0';
601 while ((str = strstr(desc->hypervisor, " ")))
602 memmove(str, str + 1, strlen(str));
b8ec7bdf 603 }
145a3ca3 604 fclose(sysinfo_fd);
c8b64f6d 605 }
fb2627ce
OO
606
607 /* OpenVZ/Virtuozzo - /proc/vz dir should exist
608 * /proc/bc should not */
857d9cdf 609 else if (path_exist(_PATH_PROC_VZ) && !path_exist(_PATH_PROC_BC)) {
fb2627ce 610 desc->hyper = HYPER_PARALLELS;
857d9cdf 611 desc->virtype = VIRT_CONT;
fb2627ce
OO
612
613 /* IBM */
857d9cdf 614 } else if (desc->vendor &&
fb2627ce 615 (strcmp(desc->vendor, "PowerVM Lx86") == 0 ||
857d9cdf 616 strcmp(desc->vendor, "IBM/S390") == 0)) {
fb2627ce 617 desc->hyper = HYPER_IBM;
857d9cdf 618 desc->virtype = VIRT_FULL;
fb2627ce
OO
619
620 /* User-mode-linux */
857d9cdf 621 } else if (desc->modelname && strstr(desc->modelname, "UML")) {
fb2627ce 622 desc->hyper = HYPER_UML;
857d9cdf 623 desc->virtype = VIRT_PARA;
fb2627ce
OO
624
625 /* Linux-VServer */
857d9cdf 626 } else if (path_exist(_PATH_PROC_STATUS)) {
fb2627ce
OO
627 char buf[BUFSIZ];
628 char *val = NULL;
629
630 fd = path_fopen("r", 0, _PATH_PROC_STATUS);
631 while (fgets(buf, sizeof(buf), fd) != NULL) {
632 if (lookup(buf, "VxID", &val))
633 break;
634 }
635 fclose(fd);
636
637 if (val) {
638 while (isdigit(*val))
639 ++val;
857d9cdf 640 if (!*val) {
fb2627ce 641 desc->hyper = HYPER_VSERVER;
857d9cdf
KZ
642 desc->virtype = VIRT_CONT;
643 }
fb2627ce
OO
644 }
645 }
c8b64f6d
KZ
646}
647
455fe9a0 648/* add @set to the @ary, unnecessary set is deallocated. */
7e03f383
KZ
649static int add_cpuset_to_array(cpu_set_t **ary, int *items, cpu_set_t *set)
650{
651 int i;
652 size_t setsize = CPU_ALLOC_SIZE(maxcpus);
653
654 if (!ary)
655 return -1;
656
657 for (i = 0; i < *items; i++) {
658 if (CPU_EQUAL_S(setsize, set, ary[i]))
659 break;
660 }
661 if (i == *items) {
662 ary[*items] = set;
663 ++*items;
664 return 0;
665 }
666 CPU_FREE(set);
667 return 1;
668}
669
5dd7507c 670static void
7e03f383 671read_topology(struct lscpu_desc *desc, int num)
5dd7507c 672{
56baaa4e 673 cpu_set_t *thread_siblings, *core_siblings, *book_siblings;
7e03f383
KZ
674
675 if (!path_exist(_PATH_SYS_CPU "/cpu%d/topology/thread_siblings", num))
676 return;
5dd7507c 677
ca01695b 678 thread_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU
7e03f383 679 "/cpu%d/topology/thread_siblings", num);
ca01695b 680 core_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU
7e03f383 681 "/cpu%d/topology/core_siblings", num);
56baaa4e
HC
682 book_siblings = NULL;
683 if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_siblings", num)) {
ca01695b 684 book_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU
56baaa4e
HC
685 "/cpu%d/topology/book_siblings", num);
686 }
aac1e59e
KZ
687
688 if (!desc->coremaps) {
56baaa4e 689 int nbooks, nsockets, ncores, nthreads;
7e03f383
KZ
690 size_t setsize = CPU_ALLOC_SIZE(maxcpus);
691
692 /* threads within one core */
693 nthreads = CPU_COUNT_S(setsize, thread_siblings);
d4bfa64d
KZ
694 if (!nthreads)
695 nthreads = 1;
696
7e03f383
KZ
697 /* cores within one socket */
698 ncores = CPU_COUNT_S(setsize, core_siblings) / nthreads;
d4bfa64d
KZ
699 if (!ncores)
700 ncores = 1;
701
702 /* number of sockets within one book. Because of odd /
703 * non-present cpu maps and to keep calculation easy we make
704 * sure that nsockets and nbooks is at least 1.
32a46618 705 */
d4bfa64d
KZ
706 nsockets = desc->ncpus / nthreads / ncores;
707 if (!nsockets)
708 nsockets = 1;
709
56baaa4e 710 /* number of books */
d4bfa64d
KZ
711 nbooks = desc->ncpus / nthreads / ncores / nsockets;
712 if (!nbooks)
713 nbooks = 1;
e282eec2
KZ
714
715 /* all threads, see also read_basicinfo()
32a46618 716 * -- fallback for kernels without
e282eec2
KZ
717 * /sys/devices/system/cpu/online.
718 */
719 if (!desc->nthreads)
32a46618 720 desc->nthreads = nbooks * nsockets * ncores * nthreads;
d4bfa64d 721
a5cfffff 722 /* For each map we make sure that it can have up to ncpuspos
9d1a3a18
HC
723 * entries. This is because we cannot reliably calculate the
724 * number of cores, sockets and books on all architectures.
725 * E.g. completely virtualized architectures like s390 may
726 * have multiple sockets of different sizes.
727 */
a5cfffff
TK
728 desc->coremaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
729 desc->socketmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
08de16d0 730 if (book_siblings)
a5cfffff 731 desc->bookmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
7e03f383 732 }
5dd7507c 733
7e03f383
KZ
734 add_cpuset_to_array(desc->socketmaps, &desc->nsockets, core_siblings);
735 add_cpuset_to_array(desc->coremaps, &desc->ncores, thread_siblings);
56baaa4e
HC
736 if (book_siblings)
737 add_cpuset_to_array(desc->bookmaps, &desc->nbooks, book_siblings);
7e03f383 738}
2b8fcb81
HC
739static void
740read_polarization(struct lscpu_desc *desc, int num)
741{
742 char mode[64];
743
744 if (desc->dispatching < 0)
745 return;
746 if (!path_exist(_PATH_SYS_CPU "/cpu%d/polarization", num))
747 return;
748 if (!desc->polarization)
a5cfffff 749 desc->polarization = xcalloc(desc->ncpuspos, sizeof(int));
ca01695b 750 path_read_str(mode, sizeof(mode), _PATH_SYS_CPU "/cpu%d/polarization", num);
2b8fcb81
HC
751 if (strncmp(mode, "vertical:low", sizeof(mode)) == 0)
752 desc->polarization[num] = POLAR_VLOW;
753 else if (strncmp(mode, "vertical:medium", sizeof(mode)) == 0)
754 desc->polarization[num] = POLAR_VMEDIUM;
755 else if (strncmp(mode, "vertical:high", sizeof(mode)) == 0)
756 desc->polarization[num] = POLAR_VHIGH;
757 else if (strncmp(mode, "horizontal", sizeof(mode)) == 0)
758 desc->polarization[num] = POLAR_HORIZONTAL;
759 else
760 desc->polarization[num] = POLAR_UNKNOWN;
761}
7e03f383 762
596b8845
HC
763static void
764read_address(struct lscpu_desc *desc, int num)
765{
766 if (!path_exist(_PATH_SYS_CPU "/cpu%d/address", num))
767 return;
768 if (!desc->addresses)
a5cfffff 769 desc->addresses = xcalloc(desc->ncpuspos, sizeof(int));
ca01695b 770 desc->addresses[num] = path_read_s32(_PATH_SYS_CPU "/cpu%d/address", num);
596b8845 771}
7e03f383 772
d231eea1
HC
773static void
774read_configured(struct lscpu_desc *desc, int num)
775{
776 if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", num))
777 return;
778 if (!desc->configured)
a5cfffff 779 desc->configured = xcalloc(desc->ncpuspos, sizeof(int));
ca01695b 780 desc->configured[num] = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", num);
d231eea1
HC
781}
782
44320710
SK
783static void
784read_max_mhz(struct lscpu_desc *desc, int num)
785{
786 if (!path_exist(_PATH_SYS_CPU "/cpu%d/cpufreq/cpuinfo_max_freq", num))
787 return;
788 if (!desc->mmhz)
789 desc->mmhz = xcalloc(desc->ncpuspos, sizeof(char *));
790 xasprintf(&(desc->mmhz[num]), "%.4f",
791 (float)path_read_s32(_PATH_SYS_CPU
792 "/cpu%d/cpufreq/cpuinfo_max_freq", num) / 1000);
793}
794
7e03f383
KZ
795static int
796cachecmp(const void *a, const void *b)
797{
798 struct cpu_cache *c1 = (struct cpu_cache *) a;
799 struct cpu_cache *c2 = (struct cpu_cache *) b;
800
801 return strcmp(c2->name, c1->name);
5dd7507c
CQ
802}
803
804static void
7e03f383 805read_cache(struct lscpu_desc *desc, int num)
5dd7507c
CQ
806{
807 char buf[256];
7e03f383 808 int i;
5dd7507c 809
aac1e59e 810 if (!desc->ncaches) {
7e03f383
KZ
811 while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
812 num, desc->ncaches))
813 desc->ncaches++;
5dd7507c 814
7e03f383
KZ
815 if (!desc->ncaches)
816 return;
5dd7507c 817
08de16d0 818 desc->caches = xcalloc(desc->ncaches, sizeof(*desc->caches));
7e03f383
KZ
819 }
820 for (i = 0; i < desc->ncaches; i++) {
821 struct cpu_cache *ca = &desc->caches[i];
822 cpu_set_t *map;
823
dcdead42
HC
824 if (!path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
825 num, i))
826 continue;
7e03f383
KZ
827 if (!ca->name) {
828 int type, level;
829
830 /* cache type */
ca01695b 831 path_read_str(buf, sizeof(buf),
7e03f383
KZ
832 _PATH_SYS_CPU "/cpu%d/cache/index%d/type",
833 num, i);
834 if (!strcmp(buf, "Data"))
835 type = 'd';
836 else if (!strcmp(buf, "Instruction"))
837 type = 'i';
838 else
839 type = 0;
840
841 /* cache level */
ca01695b 842 level = path_read_s32(_PATH_SYS_CPU "/cpu%d/cache/index%d/level",
7e03f383
KZ
843 num, i);
844 if (type)
845 snprintf(buf, sizeof(buf), "L%d%c", level, type);
846 else
847 snprintf(buf, sizeof(buf), "L%d", level);
848
849 ca->name = xstrdup(buf);
850
851 /* cache size */
ca01695b 852 path_read_str(buf, sizeof(buf),
7e03f383
KZ
853 _PATH_SYS_CPU "/cpu%d/cache/index%d/size",
854 num, i);
855 ca->size = xstrdup(buf);
856 }
5dd7507c 857
7e03f383 858 /* information about how CPUs share different caches */
ca01695b 859 map = path_read_cpuset(maxcpus,
8148217b
HC
860 _PATH_SYS_CPU "/cpu%d/cache/index%d/shared_cpu_map",
861 num, i);
5dd7507c 862
08de16d0 863 if (!ca->sharedmaps)
a5cfffff 864 ca->sharedmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
7e03f383 865 add_cpuset_to_array(ca->sharedmaps, &ca->nsharedmaps, map);
5dd7507c
CQ
866 }
867}
868
869static void
e8aa16ee 870read_nodes(struct lscpu_desc *desc)
5dd7507c
CQ
871{
872 int i;
873
874 /* number of NUMA node */
7e03f383
KZ
875 while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", desc->nnodes))
876 desc->nnodes++;
877
878 if (!desc->nnodes)
879 return;
5dd7507c 880
08de16d0 881 desc->nodemaps = xcalloc(desc->nnodes, sizeof(cpu_set_t *));
5dd7507c
CQ
882
883 /* information about how nodes share different CPUs */
7e03f383 884 for (i = 0; i < desc->nnodes; i++)
ca01695b 885 desc->nodemaps[i] = path_read_cpuset(maxcpus,
5dd7507c
CQ
886 _PATH_SYS_SYSTEM "/node/node%d/cpumap",
887 i);
888}
889
e3b3a2f3
KZ
890static char *
891get_cell_data(struct lscpu_desc *desc, int cpu, int col,
892 struct lscpu_modifier *mod,
893 char *buf, size_t bufsz)
5dd7507c 894{
7e03f383 895 size_t setsize = CPU_ALLOC_SIZE(maxcpus);
e9d659ea 896 size_t idx;
e3b3a2f3
KZ
897
898 *buf = '\0';
5dd7507c 899
477251f8
KZ
900 switch (col) {
901 case COL_CPU:
e3b3a2f3 902 snprintf(buf, bufsz, "%d", cpu);
477251f8
KZ
903 break;
904 case COL_CORE:
e9d659ea
KZ
905 if (cpuset_ary_isset(cpu, desc->coremaps,
906 desc->ncores, setsize, &idx) == 0)
e3b3a2f3 907 snprintf(buf, bufsz, "%zd", idx);
477251f8
KZ
908 break;
909 case COL_SOCKET:
e9d659ea
KZ
910 if (cpuset_ary_isset(cpu, desc->socketmaps,
911 desc->nsockets, setsize, &idx) == 0)
e3b3a2f3 912 snprintf(buf, bufsz, "%zd", idx);
477251f8
KZ
913 break;
914 case COL_NODE:
e9d659ea
KZ
915 if (cpuset_ary_isset(cpu, desc->nodemaps,
916 desc->nnodes, setsize, &idx) == 0)
e3b3a2f3 917 snprintf(buf, bufsz, "%zd", idx);
477251f8
KZ
918 break;
919 case COL_BOOK:
e9d659ea
KZ
920 if (cpuset_ary_isset(cpu, desc->bookmaps,
921 desc->nbooks, setsize, &idx) == 0)
e3b3a2f3 922 snprintf(buf, bufsz, "%zd", idx);
477251f8
KZ
923 break;
924 case COL_CACHE:
e3b3a2f3
KZ
925 {
926 char *p = buf;
927 size_t sz = bufsz;
928 int j;
929
7e03f383
KZ
930 for (j = desc->ncaches - 1; j >= 0; j--) {
931 struct cpu_cache *ca = &desc->caches[j];
e9d659ea
KZ
932
933 if (cpuset_ary_isset(cpu, ca->sharedmaps,
e3b3a2f3
KZ
934 ca->nsharedmaps, setsize, &idx) == 0) {
935 int x = snprintf(p, sz, "%zd", idx);
936 if (x <= 0 || (size_t) x + 2 >= sz)
937 return NULL;
938 p += x;
939 sz -= x;
940 }
941 if (j != 0) {
942 *p++ = mod->compat ? ',' : ':';
943 *p = '\0';
944 sz++;
945 }
5dd7507c 946 }
477251f8 947 break;
e3b3a2f3 948 }
2b8fcb81 949 case COL_POLARIZATION:
ba45d8c1
KZ
950 if (desc->polarization) {
951 int x = desc->polarization[cpu];
952
e3b3a2f3 953 snprintf(buf, bufsz, "%s",
ba45d8c1
KZ
954 mod->mode == OUTPUT_PARSABLE ?
955 polar_modes[x].parsable :
956 polar_modes[x].readable);
957 }
2b8fcb81 958 break;
596b8845
HC
959 case COL_ADDRESS:
960 if (desc->addresses)
e3b3a2f3 961 snprintf(buf, bufsz, "%d", desc->addresses[cpu]);
596b8845 962 break;
d231eea1 963 case COL_CONFIGURED:
e43fc13e
HC
964 if (!desc->configured)
965 break;
966 if (mod->mode == OUTPUT_PARSABLE)
d231eea1
HC
967 snprintf(buf, bufsz,
968 desc->configured[cpu] ? _("Y") : _("N"));
e43fc13e
HC
969 else
970 snprintf(buf, bufsz,
847b982e 971 desc->configured[cpu] ? _("yes") : _("no"));
d231eea1 972 break;
a7e5300c 973 case COL_ONLINE:
e43fc13e
HC
974 if (!desc->online)
975 break;
976 if (mod->mode == OUTPUT_PARSABLE)
977 snprintf(buf, bufsz,
978 is_cpu_online(desc, cpu) ? _("Y") : _("N"));
979 else
980 snprintf(buf, bufsz,
847b982e 981 is_cpu_online(desc, cpu) ? _("yes") : _("no"));
a7e5300c 982 break;
44320710
SK
983 case COL_MMHZ:
984 if (desc->mmhz)
985 xstrncpy(buf, desc->mmhz[cpu], bufsz);
986 break;
477251f8 987 }
e3b3a2f3
KZ
988 return buf;
989}
990
991static char *
992get_cell_header(struct lscpu_desc *desc, int col,
993 struct lscpu_modifier *mod,
994 char *buf, size_t bufsz)
995{
996 *buf = '\0';
997
998 if (col == COL_CACHE) {
999 char *p = buf;
1000 size_t sz = bufsz;
1001 int i;
1002
1003 for (i = desc->ncaches - 1; i >= 0; i--) {
1004 int x = snprintf(p, sz, "%s", desc->caches[i].name);
1005 if (x <= 0 || (size_t) x + 2 > sz)
1006 return NULL;
1007 sz -= x;
1008 p += x;
1009 if (i > 0) {
1010 *p++ = mod->compat ? ',' : ':';
1011 *p = '\0';
1012 sz++;
1013 }
1014 }
1015 if (desc->ncaches)
1016 return buf;
1017 }
756d79cd 1018 snprintf(buf, bufsz, "%s", coldescs[col].name);
e3b3a2f3 1019 return buf;
477251f8
KZ
1020}
1021
1022/*
ba45d8c1 1023 * [-p] backend, we support two parsable formats:
477251f8
KZ
1024 *
1025 * 1) "compatible" -- this format is compatible with the original lscpu(1)
1026 * output and it contains fixed set of the columns. The CACHE columns are at
1027 * the end of the line and the CACHE is not printed if the number of the caches
1028 * is zero. The CACHE columns are separated by two commas, for example:
1029 *
1030 * $ lscpu --parse
1031 * # CPU,Core,Socket,Node,,L1d,L1i,L2
1032 * 0,0,0,0,,0,0,0
1033 * 1,1,0,0,,1,1,0
1034 *
1035 * 2) "user defined output" -- this format prints always all columns without
1036 * special prefix for CACHE column. If there are not CACHEs then the column is
1037 * empty and the header "Cache" is printed rather than a real name of the cache.
1038 * The CACHE columns are separated by ':'.
1039 *
1040 * $ lscpu --parse=CPU,CORE,SOCKET,NODE,CACHE
1041 * # CPU,Core,Socket,Node,L1d:L1i:L2
1042 * 0,0,0,0,0:0:0
1043 * 1,1,0,0,1:1:0
1044 */
1045static void
8005924a
KZ
1046print_parsable(struct lscpu_desc *desc, int cols[], int ncols,
1047 struct lscpu_modifier *mod)
477251f8 1048{
e3b3a2f3
KZ
1049 char buf[BUFSIZ], *data;
1050 int i;
477251f8 1051
e3b3a2f3
KZ
1052 /*
1053 * Header
1054 */
477251f8
KZ
1055 printf(_(
1056 "# The following is the parsable format, which can be fed to other\n"
1057 "# programs. Each different item in every column has an unique ID\n"
1058 "# starting from zero.\n"));
1059
1060 fputs("# ", stdout);
1061 for (i = 0; i < ncols; i++) {
3d27b76a 1062 int col = cols[i];
b9d18bc3 1063
3d27b76a 1064 if (col == COL_CACHE) {
8005924a 1065 if (mod->compat && !desc->ncaches)
477251f8 1066 continue;
8005924a 1067 if (mod->compat && i != 0)
477251f8 1068 putchar(',');
477251f8 1069 }
e3b3a2f3
KZ
1070 if (i > 0)
1071 putchar(',');
1072
3d27b76a 1073 data = get_cell_header(desc, col, mod, buf, sizeof(buf));
b9d18bc3 1074
3d27b76a
KZ
1075 if (data && * data && col != COL_CACHE &&
1076 !coldescs[col].is_abbr) {
1077 /*
1078 * For normal column names use mixed case (e.g. "Socket")
1079 */
1080 char *p = data + 1;
1081
14e8be8a
PU
1082 while (p && *p != '\0') {
1083 *p = tolower((unsigned int) *p);
1084 p++;
1085 }
3d27b76a 1086 }
e3b3a2f3 1087 fputs(data && *data ? data : "", stdout);
477251f8
KZ
1088 }
1089 putchar('\n');
1090
e3b3a2f3
KZ
1091 /*
1092 * Data
1093 */
a5cfffff 1094 for (i = 0; i < desc->ncpuspos; i++) {
e3b3a2f3
KZ
1095 int c;
1096
7afc2387
HC
1097 if (!mod->offline && desc->online && !is_cpu_online(desc, i))
1098 continue;
1099 if (!mod->online && desc->online && is_cpu_online(desc, i))
477251f8 1100 continue;
a5cfffff
TK
1101 if (desc->present && !is_cpu_present(desc, i))
1102 continue;
477251f8 1103 for (c = 0; c < ncols; c++) {
8005924a 1104 if (mod->compat && cols[c] == COL_CACHE) {
477251f8
KZ
1105 if (!desc->ncaches)
1106 continue;
1107 if (c > 0)
1108 putchar(',');
1109 }
1110 if (c > 0)
1111 putchar(',');
e3b3a2f3
KZ
1112
1113 data = get_cell_data(desc, i, cols[c], mod,
1114 buf, sizeof(buf));
1115 fputs(data && *data ? data : "", stdout);
477251f8 1116 }
5dd7507c
CQ
1117 putchar('\n');
1118 }
1119}
1120
ba45d8c1
KZ
1121/*
1122 * [-e] backend
1123 */
1124static void
1125print_readable(struct lscpu_desc *desc, int cols[], int ncols,
1126 struct lscpu_modifier *mod)
1127{
1128 int i;
1129 char buf[BUFSIZ], *data;
1130 struct tt *tt = tt_new_table(0);
1131
1132 if (!tt)
1133 err(EXIT_FAILURE, _("failed to initialize output table"));
1134
1135 for (i = 0; i < ncols; i++) {
b9d18bc3 1136 data = get_cell_header(desc, cols[i], mod, buf, sizeof(buf));
ba45d8c1
KZ
1137 tt_define_column(tt, xstrdup(data), 0, 0);
1138 }
1139
a5cfffff 1140 for (i = 0; i < desc->ncpuspos; i++) {
ba45d8c1
KZ
1141 int c;
1142 struct tt_line *line;
1143
7afc2387
HC
1144 if (!mod->offline && desc->online && !is_cpu_online(desc, i))
1145 continue;
1146 if (!mod->online && desc->online && is_cpu_online(desc, i))
ba45d8c1 1147 continue;
a5cfffff
TK
1148 if (desc->present && !is_cpu_present(desc, i))
1149 continue;
ba45d8c1
KZ
1150
1151 line = tt_add_line(tt, NULL);
1152
1153 for (c = 0; c < ncols; c++) {
1154 data = get_cell_data(desc, i, cols[c], mod,
1155 buf, sizeof(buf));
a7e5300c 1156 tt_line_set_data(line, c, data && *data ? xstrdup(data) : "-");
ba45d8c1
KZ
1157 }
1158 }
1159
1160 tt_print_table(tt);
1161}
5dd7507c
CQ
1162
1163/* output formats "<key> <value>"*/
1164#define print_s(_key, _val) printf("%-23s%s\n", _key, _val)
1165#define print_n(_key, _val) printf("%-23s%d\n", _key, _val)
1166
1167static void
4f912c6a
KZ
1168print_cpuset(const char *key, cpu_set_t *set, int hex)
1169{
1170 size_t setsize = CPU_ALLOC_SIZE(maxcpus);
1171 size_t setbuflen = 7 * maxcpus;
1172 char setbuf[setbuflen], *p;
1173
1174 if (hex) {
1175 p = cpumask_create(setbuf, setbuflen, set, setsize);
1176 printf("%-23s0x%s\n", key, p);
1177 } else {
1178 p = cpulist_create(setbuf, setbuflen, set, setsize);
1179 print_s(key, p);
1180 }
1181
1182}
1183
ba45d8c1
KZ
1184/*
1185 * default output
1186 */
4f912c6a 1187static void
8005924a 1188print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
5dd7507c 1189{
7e03f383
KZ
1190 char buf[512];
1191 int i;
aac1e59e 1192 size_t setsize = CPU_ALLOC_SIZE(maxcpus);
7e03f383 1193
e6b0611b 1194 print_s(_("Architecture:"), desc->arch);
79e8b41a 1195
f633ad4e 1196 if (desc->mode) {
2d7bee25 1197 char mbuf[64], *p = mbuf;
79e8b41a 1198
f633ad4e 1199 if (desc->mode & MODE_32BIT) {
79e8b41a
KZ
1200 strcpy(p, "32-bit, ");
1201 p += 8;
1202 }
f633ad4e 1203 if (desc->mode & MODE_64BIT) {
79e8b41a
KZ
1204 strcpy(p, "64-bit, ");
1205 p += 8;
1206 }
1207 *(p - 2) = '\0';
2d7bee25 1208 print_s(_("CPU op-mode(s):"), mbuf);
79e8b41a 1209 }
aabe2441 1210#if !defined(WORDS_BIGENDIAN)
9b8d4d5f
DB
1211 print_s(_("Byte Order:"), "Little Endian");
1212#else
1213 print_s(_("Byte Order:"), "Big Endian");
9b8d4d5f 1214#endif
4a939e04 1215 print_n(_("CPU(s):"), desc->ncpus);
4f912c6a 1216
5d4ba40d 1217 if (desc->online)
8005924a
KZ
1218 print_cpuset(mod->hex ? _("On-line CPU(s) mask:") :
1219 _("On-line CPU(s) list:"),
1220 desc->online, mod->hex);
4f912c6a 1221
5d4ba40d 1222 if (desc->online && CPU_COUNT_S(setsize, desc->online) != desc->ncpus) {
4f912c6a
KZ
1223 cpu_set_t *set;
1224
1225 /* Linux kernel provides cpuset of off-line CPUs that contains
1226 * all configured CPUs (see /sys/devices/system/cpu/offline),
1227 * but want to print real (present in system) off-line CPUs only.
1228 */
1229 set = cpuset_alloc(maxcpus, NULL, NULL);
1230 if (!set)
1231 err(EXIT_FAILURE, _("failed to callocate cpu set"));
1232 CPU_ZERO_S(setsize, set);
a5cfffff
TK
1233 for (i = 0; i < desc->ncpuspos; i++) {
1234 if (!is_cpu_online(desc, i) && is_cpu_present(desc, i))
4f912c6a
KZ
1235 CPU_SET_S(i, setsize, set);
1236 }
8005924a
KZ
1237 print_cpuset(mod->hex ? _("Off-line CPU(s) mask:") :
1238 _("Off-line CPU(s) list:"),
1239 set, mod->hex);
4f912c6a
KZ
1240 cpuset_free(set);
1241 }
5dd7507c 1242
7e03f383 1243 if (desc->nsockets) {
8648ca96
HC
1244 int cores_per_socket, sockets_per_book, books;
1245
1246 cores_per_socket = sockets_per_book = books = 0;
1247 /* s390 detects its cpu topology via /proc/sysinfo, if present.
1248 * Using simply the cpu topology masks in sysfs will not give
1249 * usable results since everything is virtualized. E.g.
1250 * virtual core 0 may have only 1 cpu, but virtual core 2 may
1251 * five cpus.
1252 * If the cpu topology is not exported (e.g. 2nd level guest)
1253 * fall back to old calculation scheme.
1254 */
1255 if (path_exist(_PATH_PROC_SYSINFO)) {
1256 FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
2d7bee25 1257 char pbuf[BUFSIZ];
8648ca96
HC
1258 int t0, t1, t2;
1259
2d7bee25
SK
1260 while (fd && fgets(pbuf, sizeof(pbuf), fd) != NULL) {
1261 if (sscanf(pbuf, "CPU Topology SW:%d%d%d%d%d%d",
8648ca96
HC
1262 &t0, &t1, &t2, &books, &sockets_per_book,
1263 &cores_per_socket) == 6)
1264 break;
1265 }
ad655c88
KZ
1266 if (fd)
1267 fclose(fd);
8648ca96 1268 }
7e03f383 1269 print_n(_("Thread(s) per core:"), desc->nthreads / desc->ncores);
8648ca96
HC
1270 print_n(_("Core(s) per socket:"),
1271 cores_per_socket ?: desc->ncores / desc->nsockets);
56baaa4e 1272 if (desc->nbooks) {
8648ca96
HC
1273 print_n(_("Socket(s) per book:"),
1274 sockets_per_book ?: desc->nsockets / desc->nbooks);
1275 print_n(_("Book(s):"), books ?: desc->nbooks);
56baaa4e 1276 } else {
8648ca96 1277 print_n(_("Socket(s):"), sockets_per_book ?: desc->nsockets);
56baaa4e 1278 }
5dd7507c 1279 }
7e03f383
KZ
1280 if (desc->nnodes)
1281 print_n(_("NUMA node(s):"), desc->nnodes);
e8aa16ee
KZ
1282 if (desc->vendor)
1283 print_s(_("Vendor ID:"), desc->vendor);
1284 if (desc->family)
1285 print_s(_("CPU family:"), desc->family);
1286 if (desc->model)
1287 print_s(_("Model:"), desc->model);
1461094c
KZ
1288 if (desc->modelname)
1289 print_s(_("Model name:"), desc->modelname);
e8aa16ee
KZ
1290 if (desc->stepping)
1291 print_s(_("Stepping:"), desc->stepping);
1292 if (desc->mhz)
1293 print_s(_("CPU MHz:"), desc->mhz);
44320710
SK
1294 if (desc->mmhz)
1295 print_s(_("CPU max MHz:"), desc->mmhz[0]);
9b8d4d5f
DB
1296 if (desc->bogomips)
1297 print_s(_("BogoMIPS:"), desc->bogomips);
e8aa16ee
KZ
1298 if (desc->virtflag) {
1299 if (!strcmp(desc->virtflag, "svm"))
5dd7507c 1300 print_s(_("Virtualization:"), "AMD-V");
e8aa16ee 1301 else if (!strcmp(desc->virtflag, "vmx"))
5dd7507c
CQ
1302 print_s(_("Virtualization:"), "VT-x");
1303 }
10829cd7
HC
1304 if (desc->hypervisor)
1305 print_s(_("Hypervisor:"), desc->hypervisor);
e8aa16ee
KZ
1306 if (desc->hyper) {
1307 print_s(_("Hypervisor vendor:"), hv_vendors[desc->hyper]);
50c6ee4c 1308 print_s(_("Virtualization type:"), _(virt_types[desc->virtype]));
c8b64f6d 1309 }
a0fff77e 1310 if (desc->dispatching >= 0)
50c6ee4c 1311 print_s(_("Dispatching mode:"), _(disp_modes[desc->dispatching]));
7e03f383 1312 if (desc->ncaches) {
2d7bee25 1313 char cbuf[512];
5dd7507c 1314
7e03f383 1315 for (i = desc->ncaches - 1; i >= 0; i--) {
2d7bee25 1316 snprintf(cbuf, sizeof(cbuf),
7e03f383 1317 _("%s cache:"), desc->caches[i].name);
2d7bee25 1318 print_s(cbuf, desc->caches[i].size);
7e03f383
KZ
1319 }
1320 }
1321
4f912c6a
KZ
1322 for (i = 0; i < desc->nnodes; i++) {
1323 snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), i);
8005924a 1324 print_cpuset(buf, desc->nodemaps[i], mod->hex);
5dd7507c
CQ
1325 }
1326}
1327
39561c70 1328static void __attribute__((__noreturn__)) usage(FILE *out)
5dd7507c 1329{
b9d18bc3
KZ
1330 size_t i;
1331
1332 fputs(USAGE_HEADER, out);
c6f095cf 1333 fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
7f1ec5e8 1334
b9d18bc3 1335 fputs(USAGE_OPTIONS, out);
c6f095cf
BS
1336 fputs(_(" -a, --all print both online and offline CPUs (default for -e)\n"), out);
1337 fputs(_(" -b, --online print online CPUs only (default for -p)\n"), out);
1338 fputs(_(" -c, --offline print offline CPUs only\n"), out);
1339 fputs(_(" -e, --extended[=<list>] print out an extended readable format\n"), out);
1340 fputs(_(" -p, --parse[=<list>] print out a parsable format\n"), out);
1341 fputs(_(" -s, --sysroot <dir> use specified directory as system root\n"), out);
1342 fputs(_(" -x, --hex print hexadecimal masks rather than lists of CPUs\n"), out);
1343 fputs(USAGE_SEPARATOR, out);
1344 fputs(USAGE_HELP, out);
1345 fputs(USAGE_VERSION, out);
b9d18bc3
KZ
1346
1347 fprintf(out, _("\nAvailable columns:\n"));
1348
3d27b76a
KZ
1349 for (i = 0; i < ARRAY_SIZE(coldescs); i++)
1350 fprintf(out, " %13s %s\n", coldescs[i].name, _(coldescs[i].help));
1351
b9d18bc3 1352 fprintf(out, _("\nFor more details see lscpu(1).\n"));
4f912c6a 1353
39561c70 1354 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
5dd7507c
CQ
1355}
1356
1357int main(int argc, char *argv[])
1358{
8005924a
KZ
1359 struct lscpu_modifier _mod = { .mode = OUTPUT_SUMMARY }, *mod = &_mod;
1360 struct lscpu_desc _desc = { .flags = 0 }, *desc = &_desc;
1361 int c, i;
3d27b76a 1362 int columns[ARRAY_SIZE(coldescs)], ncolumns = 0;
7fc12cd2 1363 int cpu_modifier_specified = 0;
5dd7507c 1364
6c7d5ae9 1365 static const struct option longopts[] = {
0ad29ff6 1366 { "all", no_argument, 0, 'a' },
23e9e95a 1367 { "online", no_argument, 0, 'b' },
7afc2387 1368 { "offline", no_argument, 0, 'c' },
47b6e8b6 1369 { "help", no_argument, 0, 'h' },
ba45d8c1 1370 { "extended", optional_argument, 0, 'e' },
477251f8 1371 { "parse", optional_argument, 0, 'p' },
47b6e8b6 1372 { "sysroot", required_argument, 0, 's' },
4f912c6a 1373 { "hex", no_argument, 0, 'x' },
44de912c 1374 { "version", no_argument, 0, 'V' },
5dd7507c
CQ
1375 { NULL, 0, 0, 0 }
1376 };
1377
8e97eb4b
KZ
1378 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
1379 { 'a','b','c' },
1380 { 'e','p' },
1381 { 0 }
1382 };
1383 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
1384
2f8f1388 1385 setlocale(LC_ALL, "");
5dd7507c
CQ
1386 bindtextdomain(PACKAGE, LOCALEDIR);
1387 textdomain(PACKAGE);
efb8854f 1388 atexit(close_stdout);
5dd7507c 1389
7afc2387 1390 while ((c = getopt_long(argc, argv, "abce::hp::s:xV", longopts, NULL)) != -1) {
8e97eb4b
KZ
1391
1392 err_exclusive_options(c, longopts, excl, excl_st);
1393
5dd7507c 1394 switch (c) {
0ad29ff6 1395 case 'a':
7afc2387 1396 mod->online = mod->offline = 1;
7fc12cd2 1397 cpu_modifier_specified = 1;
0ad29ff6 1398 break;
23e9e95a
KZ
1399 case 'b':
1400 mod->online = 1;
7fc12cd2 1401 cpu_modifier_specified = 1;
23e9e95a 1402 break;
7afc2387
HC
1403 case 'c':
1404 mod->offline = 1;
7fc12cd2 1405 cpu_modifier_specified = 1;
7afc2387 1406 break;
5dd7507c 1407 case 'h':
39561c70 1408 usage(stdout);
5dd7507c 1409 case 'p':
ba45d8c1 1410 case 'e':
477251f8
KZ
1411 if (optarg) {
1412 if (*optarg == '=')
1413 optarg++;
1414 ncolumns = string_to_idarray(optarg,
1415 columns, ARRAY_SIZE(columns),
1416 column_name_to_id);
1417 if (ncolumns < 0)
1418 return EXIT_FAILURE;
477251f8 1419 }
ba45d8c1 1420 mod->mode = c == 'p' ? OUTPUT_PARSABLE : OUTPUT_READABLE;
5dd7507c 1421 break;
47b6e8b6 1422 case 's':
ca01695b 1423 path_set_prefix(optarg);
8148217b 1424 mod->system = SYSTEM_SNAPSHOT;
47b6e8b6 1425 break;
4f912c6a 1426 case 'x':
8005924a 1427 mod->hex = 1;
4f912c6a 1428 break;
44de912c
HC
1429 case 'V':
1430 printf(_("%s from %s\n"), program_invocation_short_name,
1431 PACKAGE_STRING);
1432 return EXIT_SUCCESS;
5dd7507c 1433 default:
39561c70 1434 usage(stderr);
5dd7507c
CQ
1435 }
1436 }
7bbb7829 1437
7fc12cd2
HC
1438 if (cpu_modifier_specified && mod->mode == OUTPUT_SUMMARY) {
1439 fprintf(stderr,
1440 _("%s: options --all, --online and --offline may only "
ac56e555 1441 "be used with options --extended or --parse.\n"),
7fc12cd2
HC
1442 program_invocation_short_name);
1443 return EXIT_FAILURE;
1444 }
1445
7bbb7829
HC
1446 if (argc != optind)
1447 usage(stderr);
1448
7afc2387
HC
1449 /* set default cpu display mode if none was specified */
1450 if (!mod->online && !mod->offline) {
1451 mod->online = 1;
1452 mod->offline = mod->mode == OUTPUT_READABLE ? 1 : 0;
1453 }
5dd7507c 1454
8148217b 1455 read_basicinfo(desc, mod);
5dd7507c 1456
a5cfffff 1457 for (i = 0; i < desc->ncpuspos; i++) {
7e03f383
KZ
1458 read_topology(desc, i);
1459 read_cache(desc, i);
2b8fcb81 1460 read_polarization(desc, i);
596b8845 1461 read_address(desc, i);
d231eea1 1462 read_configured(desc, i);
44320710 1463 read_max_mhz(desc, i);
47b6e8b6 1464 }
7e03f383 1465
960bf130
KZ
1466 if (desc->caches)
1467 qsort(desc->caches, desc->ncaches,
1468 sizeof(struct cpu_cache), cachecmp);
7e03f383
KZ
1469
1470 read_nodes(desc);
eff79ceb 1471 read_hypervisor(desc, mod);
c8b64f6d 1472
8005924a 1473 switch(mod->mode) {
ba45d8c1
KZ
1474 case OUTPUT_SUMMARY:
1475 print_summary(desc, mod);
1476 break;
1477 case OUTPUT_PARSABLE:
1478 if (!ncolumns) {
1479 columns[ncolumns++] = COL_CPU;
1480 columns[ncolumns++] = COL_CORE;
1481 columns[ncolumns++] = COL_SOCKET;
1482 columns[ncolumns++] = COL_NODE;
1483 columns[ncolumns++] = COL_CACHE;
1484 mod->compat = 1;
1485 }
1486 print_parsable(desc, columns, ncolumns, mod);
1487 break;
1488 case OUTPUT_READABLE:
1489 if (!ncolumns) {
1490 /* No list was given. Just print whatever is there. */
1491 columns[ncolumns++] = COL_CPU;
1492 if (desc->nodemaps)
8005924a 1493 columns[ncolumns++] = COL_NODE;
ba45d8c1
KZ
1494 if (desc->bookmaps)
1495 columns[ncolumns++] = COL_BOOK;
1496 if (desc->socketmaps)
1497 columns[ncolumns++] = COL_SOCKET;
1498 if (desc->coremaps)
1499 columns[ncolumns++] = COL_CORE;
1500 if (desc->caches)
8005924a 1501 columns[ncolumns++] = COL_CACHE;
a7e5300c
HC
1502 if (desc->online)
1503 columns[ncolumns++] = COL_ONLINE;
d231eea1
HC
1504 if (desc->configured)
1505 columns[ncolumns++] = COL_CONFIGURED;
ba45d8c1
KZ
1506 if (desc->polarization)
1507 columns[ncolumns++] = COL_POLARIZATION;
1508 if (desc->addresses)
1509 columns[ncolumns++] = COL_ADDRESS;
44320710
SK
1510 if (desc->mmhz)
1511 columns[ncolumns++] = COL_MMHZ;
ba45d8c1
KZ
1512 }
1513 print_readable(desc, columns, ncolumns, mod);
1514 break;
8005924a 1515 }
5dd7507c 1516
cf474aac 1517 return EXIT_SUCCESS;
5dd7507c 1518}