2 * lscpu - CPU architecture information helper
4 * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
5 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 #include <sys/utsname.h>
39 #define _PATH_SYS_SYSTEM "sys/devices/system"
40 #define _PATH_SYS_CPU0 _PATH_SYS_SYSTEM "/cpu/cpu0"
41 #define _PATH_PROC_XEN "proc/xen"
42 #define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
43 #define _PATH_PROC_CPUINFO "proc/cpuinfo"
44 #define _PATH_PROC_PCIDEVS "proc/bus/pci/devices"
50 /* cache(s) description */
57 /* virtualization types */
63 const char *virt_types[] = {
64 [VIRT_NONE] = N_("none"),
65 [VIRT_PARA] = N_("para"),
66 [VIRT_FULL] = N_("full")
69 /* hypervisor vendors */
76 const char *hv_vendors[] = {
80 [HYPER_MSHV] = "Microsoft"
84 /* CPU(s) description */
99 char *virtflag; /* virtualization flag (vmx, svm) */
100 int hyper; /* hypervisor vendor ID */
101 int virtype; /* VIRT_PARA|FULL|NONE ? */
104 struct ca_desc cache[CACHE_MAX];
115 char pathbuf[PATH_MAX] = "/";
117 static void path_scanstr(char *result, const char *path, ...)
118 __attribute__ ((__format__ (__printf__, 2, 3)));
119 static int path_exist(const char *path, ...)
120 __attribute__ ((__format__ (__printf__, 1, 2)));
121 static int path_sibling(const char *path, ...)
122 __attribute__ ((__format__ (__printf__, 1, 2)));
125 xfopen(const char *path, const char *mode)
127 FILE *fd = fopen(path, mode);
129 err(EXIT_FAILURE, _("error: %s"), path);
134 path_vfopen(const char *mode, const char *path, va_list ap)
136 vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
137 return xfopen(pathbuf, mode);
141 path_scanstr(char *result, const char *path, ...)
147 fd = path_vfopen("r", path, ap);
150 if (fscanf(fd, "%s", result) != 1) {
152 err(EXIT_FAILURE, _("error: %s"), pathbuf);
154 errx(EXIT_FAILURE, _("error parse: %s"), pathbuf);
160 path_exist(const char *path, ...)
165 vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
168 return access(pathbuf, F_OK) == 0;
172 xstrdup(const char *str)
174 char *s = strdup(str);
176 err(EXIT_FAILURE, _("error: strdup failed"));
180 /* count the set bit in a mapping file */
182 path_sibling(const char *path, ...)
191 fp = path_vfopen("r", path, ap);
194 while ((c = fgetc(fp)) != EOF) {
198 for (n = strtol(s, NULL, 16); n > 0; n /= 2) {
209 /* Lookup a pattern and get the value from cpuinfo.
212 * "<pattern> : <key>"
214 int lookup(char *line, char *pattern, char **value)
217 int len = strlen(pattern);
223 if (strncmp(line, pattern, len))
227 for (p = line + len; isspace(*p); p++);
234 for (++p; isspace(*p); p++);
242 len = strlen(line) - 1;
243 for (p = line + len; isspace(*(p-1)); p--);
251 read_basicinfo(struct cpu_desc *cpu)
253 FILE *fp = xfopen(_PATH_PROC_CPUINFO, "r");
255 struct utsname utsbuf;
258 if (uname(&utsbuf) == -1)
259 err(EXIT_FAILURE, _("error: uname failed"));
260 cpu->arch = xstrdup(utsbuf.machine);
263 while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", cpu->ct_cpu))
267 while (fgets(buf, sizeof(buf), fp) != NULL) {
269 if (lookup(buf, "vendor", &cpu->vendor)) ;
270 else if (lookup(buf, "vendor_id", &cpu->vendor)) ;
272 else if (lookup(buf, "family", &cpu->family)) ;
273 else if (lookup(buf, "cpu family", &cpu->family)) ;
274 else if (lookup(buf, "model", &cpu->model)) ;
275 else if (lookup(buf, "stepping", &cpu->stepping)) ;
276 else if (lookup(buf, "cpu MHz", &cpu->mhz)) ;
277 else if (lookup(buf, "flags", &cpu->flags)) ;
283 snprintf(buf, sizeof(buf), " %s ", cpu->flags);
284 if (strstr(buf, " svm "))
285 cpu->virtflag = strdup("svm");
286 else if (strstr(buf, " vmx "))
287 cpu->virtflag = strdup("vmx");
294 has_pci_device(int vendor, int device)
297 int num, fn, ven, dev;
300 f = fopen(_PATH_PROC_PCIDEVS, "r");
304 /* for more details about bus/pci/devices format see
305 * drivers/pci/proc.c in linux kernel
307 while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
308 &num, &fn, &ven, &dev) == 4) {
310 if (ven == vendor && dev == device)
320 #if defined(__x86_64__) || defined(__i386__)
323 * This CPUID leaf returns the information about the hypervisor.
324 * EAX : maximum input value for CPUID supported by the hypervisor.
325 * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
327 #define HYPERVISOR_INFO_LEAF 0x40000000
330 cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
331 unsigned int *ecx, unsigned int *edx)
334 #if defined(__PIC__) && defined(__i386__)
335 /* x86 PIC cannot clobber ebx -- gcc bitches */
352 read_hypervisor_cpuid(struct cpu_desc *cpu)
354 unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
355 char hyper_vendor_id[13];
357 memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
359 cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
360 memcpy(hyper_vendor_id + 0, &ebx, 4);
361 memcpy(hyper_vendor_id + 4, &ecx, 4);
362 memcpy(hyper_vendor_id + 8, &edx, 4);
363 hyper_vendor_id[12] = '\0';
365 if (!hyper_vendor_id[0])
368 if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
369 cpu->hyper = HYPER_XEN;
370 else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
371 cpu->hyper = HYPER_KVM;
372 else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
373 cpu->hyper = HYPER_MSHV;
376 #else /* ! __x86_64__ */
378 read_hypervisor_cpuid(struct cpu_desc *cpu)
384 read_hypervisor(struct cpu_desc *cpu)
386 read_hypervisor_cpuid(cpu);
390 cpu->virtype = VIRT_FULL;
392 else if (!access(_PATH_PROC_XEN, F_OK)) {
393 /* Xen para-virt or dom0 */
394 FILE *fd = fopen(_PATH_PROC_XENCAP, "r");
400 if (fscanf(fd, "%s", buf) == 1 &&
401 !strcmp(buf, "control_d"))
405 cpu->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
406 cpu->hyper = HYPER_XEN;
408 } else if (has_pci_device(0x5853, 0x0001)) {
409 /* Xen full-virt on non-x86_64 */
410 cpu->hyper = HYPER_XEN;
411 cpu->virtype = VIRT_FULL;
416 read_topology(struct cpu_desc *cpu)
418 /* number of threads */
419 cpu->ct_thread = path_sibling(
420 _PATH_SYS_CPU0 "/topology/thread_siblings");
422 /* number of cores */
423 cpu->ct_core = path_sibling(
424 _PATH_SYS_CPU0 "/topology/core_siblings")
427 /* number of sockets */
428 cpu->ct_socket = cpu->ct_cpu / cpu->ct_core / cpu->ct_thread;
432 read_cache(struct cpu_desc *cpu)
439 dp = opendir(_PATH_SYS_CPU0 "/cache");
441 err(EXIT_FAILURE, _("error: %s"), _PATH_SYS_CPU0 "/cache");
443 while ((dir = readdir(dp)) != NULL) {
444 if (!strcmp(dir->d_name, ".")
445 || !strcmp(dir->d_name, ".."))
449 path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/type", dir->d_name);
450 if (!strcmp(buf, "Data"))
452 else if (!strcmp(buf, "Instruction"))
458 path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/level", dir->d_name);
462 snprintf(buf, sizeof(buf), "L%d%c", level, type);
464 snprintf(buf, sizeof(buf), "L%d", level);
466 cpu->cache[cpu->ct_cache].caname = xstrdup(buf);
469 path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/size", dir->d_name);
470 cpu->cache[cpu->ct_cache].casize = xstrdup(buf);
472 /* information about how CPUs share different caches */
473 cpu->cache[cpu->ct_cache].camap = path_sibling(
474 _PATH_SYS_CPU0 "/cache/%s/shared_cpu_map",
481 read_nodes(struct cpu_desc *cpu)
485 /* number of NUMA node */
486 while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", cpu->ct_node))
489 cpu->nodecpu = (int *) malloc(cpu->ct_node * sizeof(int));
491 err(EXIT_FAILURE, _("error: malloc failed"));
493 /* information about how nodes share different CPUs */
494 for (i = 0; i < cpu->ct_node; i++)
495 cpu->nodecpu[i] = path_sibling(
496 _PATH_SYS_SYSTEM "/node/node%d/cpumap",
503 /* Read through sysfs. */
504 if (access(_PATH_SYS_SYSTEM, F_OK))
506 _("error: /sys filesystem is not accessable."));
508 if (!access(_PATH_SYS_SYSTEM "/node", F_OK))
511 if (!access(_PATH_SYS_CPU0 "/topology/thread_siblings", F_OK))
514 if (!access(_PATH_SYS_CPU0 "/cache", F_OK))
519 print_parsable(struct cpu_desc *cpu)
524 "# The following is the parsable format, which can be fed to other\n"
525 "# programs. Each different item in every column has an unique ID\n"
526 "# starting from zero.\n"
527 "# CPU,Core,Socket,Node"));
530 /* separator between CPU topology and cache information */
533 for (i = cpu->ct_cache - 1; i >= 0; i--)
534 printf(",%s", cpu->cache[i].caname);
538 for (i = 0; i < cpu->ct_cpu; i++) {
544 i / cpu->ct_core / cpu->ct_thread);
551 for (j = 0; j < cpu->ct_node; j++) {
552 c += cpu->nodecpu[j];
564 for (j = cpu->ct_cache - 1; j >= 0; j--) {
565 /* If shared_cpu_map is 0, all CPUs share the same
567 if (cpu->cache[j].camap == 0)
568 cpu->cache[j].camap = cpu->ct_core *
571 printf(",%d", i / cpu->cache[j].camap);
579 /* output formats "<key> <value>"*/
580 #define print_s(_key, _val) printf("%-23s%s\n", _key, _val)
581 #define print_n(_key, _val) printf("%-23s%d\n", _key, _val)
584 print_readable(struct cpu_desc *cpu)
586 print_s("Architecture:", cpu->arch);
587 print_n("CPU(s):", cpu->ct_cpu);
590 print_n(_("Thread(s) per core:"), cpu->ct_thread);
591 print_n(_("Core(s) per socket:"), cpu->ct_core);
592 print_n(_("CPU socket(s):"), cpu->ct_socket);
596 print_n(_("NUMA node(s):"), cpu->ct_node);
598 print_s(_("Vendor ID:"), cpu->vendor);
600 print_s(_("CPU family:"), cpu->family);
602 print_s(_("Model:"), cpu->model);
604 print_s(_("Stepping:"), cpu->stepping);
606 print_s(_("CPU MHz:"), cpu->mhz);
608 if (!strcmp(cpu->virtflag, "svm"))
609 print_s(_("Virtualization:"), "AMD-V");
610 else if (!strcmp(cpu->virtflag, "vmx"))
611 print_s(_("Virtualization:"), "VT-x");
614 print_s(_("Hypervisor vendor:"), hv_vendors[cpu->hyper]);
615 print_s(_("Virtualization type:"), virt_types[cpu->virtype]);
621 for (i = cpu->ct_cache - 1; i >= 0; i--) {
622 snprintf(buf, sizeof(buf),
623 _("%s cache:"), cpu->cache[i].caname);
624 print_s(buf, cpu->cache[i].casize);
631 printf(_("Usage: %s [option]\n"),
632 program_invocation_short_name);
634 puts(_( "CPU architecture information helper\n\n"
635 " -h, --help usage information\n"
636 " -p, --parse print out in parsable instead of printable format.\n"
637 " -s, --sysroot use the directory as a new system root.\n"));
642 ca_compare(const void *a, const void *b)
644 struct ca_desc *cache1 = (struct ca_desc *) a;
645 struct ca_desc *cache2 = (struct ca_desc *) b;
647 return strcmp(cache2->caname, cache1->caname);
650 int main(int argc, char *argv[])
652 struct cpu_desc _cpu, *cpu = &_cpu;
655 struct option longopts[] = {
656 { "help", no_argument, 0, 'h' },
657 { "parse", no_argument, 0, 'p' },
658 { "sysroot", required_argument, 0, 's' },
662 setlocale(LC_MESSAGES, "");
663 bindtextdomain(PACKAGE, LOCALEDIR);
666 while((c = getopt_long(argc, argv, "hps:", longopts, NULL)) != -1) {
674 strncpy(pathbuf, optarg, sizeof(pathbuf));
681 if (chdir(pathbuf) == -1)
683 _("error: change working directory to %s."), pathbuf);
685 memset(cpu, 0, sizeof(*cpu));
695 qsort(cpu->cache, cpu->ct_cache, sizeof(struct ca_desc), ca_compare);
700 read_hypervisor(cpu);