Upload Tizen:Base source
[framework/base/util-linux-ng.git] / sys-utils / lscpu.c
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  *
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.
11  *
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.
16  *
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/>.
19  */
20
21 #include <ctype.h>
22 #include <dirent.h>
23 #include <err.h>
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>
33
34 #include "nls.h"
35
36 #define CACHE_MAX 100
37
38 /* /sys paths */
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"
45
46 int have_topology;
47 int have_cache;
48 int have_node;
49
50 /* cache(s) description */
51 struct ca_desc {
52         char    *caname;
53         char    *casize;
54         int     camap;
55 };
56
57 /* virtualization types */
58 enum {
59         VIRT_NONE       = 0,
60         VIRT_PARA,
61         VIRT_FULL
62 };
63 const char *virt_types[] = {
64         [VIRT_NONE]     = N_("none"),
65         [VIRT_PARA]     = N_("para"),
66         [VIRT_FULL]     = N_("full")
67 };
68
69 /* hypervisor vendors */
70 enum {
71         HYPER_NONE      = 0,
72         HYPER_XEN,
73         HYPER_KVM,
74         HYPER_MSHV
75 };
76 const char *hv_vendors[] = {
77         [HYPER_NONE]    = NULL,
78         [HYPER_XEN]     = "Xen",
79         [HYPER_KVM]     = "KVM",
80         [HYPER_MSHV]    = "Microsoft"
81 };
82
83
84 /* CPU(s) description */
85 struct cpu_desc {
86         /* counters */
87         int     ct_cpu;
88         int     ct_thread;
89         int     ct_core;
90         int     ct_socket;
91         int     ct_node;
92         int     ct_cache;
93
94         /* who is who */
95         char    *arch;
96         char    *vendor;
97         char    *family;
98         char    *model;
99         char    *virtflag;      /* virtualization flag (vmx, svm) */
100         int     hyper;          /* hypervisor vendor ID */
101         int     virtype;        /* VIRT_PARA|FULL|NONE ? */
102
103         /* caches */
104         struct ca_desc  cache[CACHE_MAX];
105
106         /* misc */
107         char    *mhz;
108         char    *stepping;
109         char    *flags;
110
111         /* NUMA */
112         int     *nodecpu;
113 };
114
115 char pathbuf[PATH_MAX] = "/";
116
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)));
123
124 static FILE *
125 xfopen(const char *path, const char *mode)
126 {
127         FILE *fd = fopen(path, mode);
128         if (!fd)
129                 err(EXIT_FAILURE, _("error: %s"), path);
130         return fd;
131 }
132
133 static FILE *
134 path_vfopen(const char *mode, const char *path, va_list ap)
135 {
136         vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
137         return xfopen(pathbuf, mode);
138 }
139
140 static void
141 path_scanstr(char *result, const char *path, ...)
142 {
143         FILE *fd;
144         va_list ap;
145
146         va_start(ap, path);
147         fd = path_vfopen("r", path, ap);
148         va_end(ap);
149
150         if (fscanf(fd, "%s", result) != 1) {
151                 if (ferror(fd))
152                         err(EXIT_FAILURE, _("error: %s"), pathbuf);
153                 else
154                         errx(EXIT_FAILURE, _("error parse: %s"), pathbuf);
155         }
156         fclose(fd);
157 }
158
159 static int
160 path_exist(const char *path, ...)
161 {
162         va_list ap;
163
164         va_start(ap, path);
165         vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
166         va_end(ap);
167
168         return access(pathbuf, F_OK) == 0;
169 }
170
171 char *
172 xstrdup(const char *str)
173 {
174         char *s = strdup(str);
175         if (!s)
176                 err(EXIT_FAILURE, _("error: strdup failed"));
177         return s;
178 }
179
180 /* count the set bit in a mapping file */
181 static int
182 path_sibling(const char *path, ...)
183 {
184         int c, n;
185         int result = 0;
186         char s[2];
187         FILE *fp;
188         va_list ap;
189
190         va_start(ap, path);
191         fp = path_vfopen("r", path, ap);
192         va_end(ap);
193
194         while ((c = fgetc(fp)) != EOF) {
195                 if (isxdigit(c)) {
196                         s[0] = c;
197                         s[1] = '\0';
198                         for (n = strtol(s, NULL, 16); n > 0; n /= 2) {
199                                 if (n % 2)
200                                         result++;
201                         }
202                 }
203         }
204         fclose(fp);
205
206         return result;
207 }
208
209 /* Lookup a pattern and get the value from cpuinfo.
210  * Format is:
211  *
212  *      "<pattern>   : <key>"
213  */
214 int lookup(char *line, char *pattern, char **value)
215 {
216         char *p, *v;
217         int len = strlen(pattern);
218
219         if (!*line)
220                 return 0;
221
222         /* pattern */
223         if (strncmp(line, pattern, len))
224                 return 0;
225
226         /* white spaces */
227         for (p = line + len; isspace(*p); p++);
228
229         /* separator */
230         if (*p != ':')
231                 return 0;
232
233         /* white spaces */
234         for (++p; isspace(*p); p++);
235
236         /* value */
237         if (!*p)
238                 return 0;
239         v = p;
240
241         /* end of value */
242         len = strlen(line) - 1;
243         for (p = line + len; isspace(*(p-1)); p--);
244         *p = '\0';
245
246         *value = xstrdup(v);
247         return 1;
248 }
249
250 static void
251 read_basicinfo(struct cpu_desc *cpu)
252 {
253         FILE *fp = xfopen(_PATH_PROC_CPUINFO, "r");
254         char buf[BUFSIZ];
255         struct utsname utsbuf;
256
257         /* architecture */
258         if (uname(&utsbuf) == -1)
259                 err(EXIT_FAILURE, _("error: uname failed"));
260         cpu->arch = xstrdup(utsbuf.machine);
261
262         /* count CPU(s) */
263         while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", cpu->ct_cpu))
264                 cpu->ct_cpu++;
265
266         /* details */
267         while (fgets(buf, sizeof(buf), fp) != NULL) {
268                 /* IA64 */
269                 if (lookup(buf, "vendor", &cpu->vendor)) ;
270                 else if (lookup(buf, "vendor_id", &cpu->vendor)) ;
271                 /* IA64 */
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)) ;
278                 else
279                         continue;
280         }
281
282         if (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");
288         }
289
290         fclose(fp);
291 }
292
293 static int
294 has_pci_device(int vendor, int device)
295 {
296         FILE *f;
297         int num, fn, ven, dev;
298         int res = 1;
299
300         f = fopen(_PATH_PROC_PCIDEVS, "r");
301         if (!f)
302                 return 0;
303
304          /* for more details about bus/pci/devices format see
305           * drivers/pci/proc.c in linux kernel
306           */
307         while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
308                         &num, &fn, &ven, &dev) == 4) {
309
310                 if (ven == vendor && dev == device)
311                         goto found;
312         }
313
314         res = 0;
315 found:
316         fclose(f);
317         return res;
318 }
319
320 #if defined(__x86_64__) || defined(__i386__)
321
322 /*
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.
326  */
327 #define HYPERVISOR_INFO_LEAF   0x40000000
328
329 static inline void
330 cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
331                          unsigned int *ecx, unsigned int *edx)
332 {
333         __asm__(
334 #if defined(__PIC__) && defined(__i386__)
335                 /* x86 PIC cannot clobber ebx -- gcc bitches */
336                 "pushl %%ebx;"
337                 "cpuid;"
338                 "movl %%ebx, %%esi;"
339                 "popl %%ebx;"
340                 : "=S" (*ebx),
341 #else
342                 "cpuid;"
343                 : "=b" (*ebx),
344 #endif
345                   "=a" (*eax),
346                   "=c" (*ecx),
347                   "=d" (*edx)
348                 : "0" (op), "c"(0));
349 }
350
351 static void
352 read_hypervisor_cpuid(struct cpu_desc *cpu)
353 {
354         unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
355         char hyper_vendor_id[13];
356
357         memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
358
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';
364
365         if (!hyper_vendor_id[0])
366                 return;
367
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;
374 }
375
376 #else   /* ! __x86_64__ */
377 static void
378 read_hypervisor_cpuid(struct cpu_desc *cpu)
379 {
380 }
381 #endif
382
383 static void
384 read_hypervisor(struct cpu_desc *cpu)
385 {
386         read_hypervisor_cpuid(cpu);
387
388         if (cpu->hyper)
389                 /* hvm */
390                 cpu->virtype = VIRT_FULL;
391
392         else if (!access(_PATH_PROC_XEN, F_OK)) {
393                 /* Xen para-virt or dom0 */
394                 FILE *fd = fopen(_PATH_PROC_XENCAP, "r");
395                 int dom0 = 0;
396
397                 if (fd) {
398                         char buf[256];
399
400                         if (fscanf(fd, "%s", buf) == 1 &&
401                             !strcmp(buf, "control_d"))
402                                 dom0 = 1;
403                         fclose(fd);
404                 }
405                 cpu->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
406                 cpu->hyper = HYPER_XEN;
407
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;
412         }
413 }
414
415 static void
416 read_topology(struct cpu_desc *cpu)
417 {
418         /* number of threads */
419         cpu->ct_thread = path_sibling(
420                                 _PATH_SYS_CPU0 "/topology/thread_siblings");
421
422         /* number of cores */
423         cpu->ct_core = path_sibling(
424                                 _PATH_SYS_CPU0 "/topology/core_siblings")
425                         / cpu->ct_thread;
426
427         /* number of sockets */
428         cpu->ct_socket = cpu->ct_cpu / cpu->ct_core / cpu->ct_thread;
429 }
430
431 static void
432 read_cache(struct cpu_desc *cpu)
433 {
434         char buf[256];
435         DIR *dp;
436         struct dirent *dir;
437         int level, type;
438
439         dp = opendir(_PATH_SYS_CPU0 "/cache");
440         if (dp == NULL)
441                 err(EXIT_FAILURE, _("error: %s"), _PATH_SYS_CPU0 "/cache");
442
443         while ((dir = readdir(dp)) != NULL) {
444                 if (!strcmp(dir->d_name, ".")
445                     || !strcmp(dir->d_name, ".."))
446                         continue;
447
448                 /* cache type */
449                 path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/type", dir->d_name);
450                 if (!strcmp(buf, "Data"))
451                         type = 'd';
452                 else if (!strcmp(buf, "Instruction"))
453                         type = 'i';
454                 else
455                         type = 0;
456
457                 /* cache level */
458                 path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/level", dir->d_name);
459                 level = atoi(buf);
460
461                 if (type)
462                         snprintf(buf, sizeof(buf), "L%d%c", level, type);
463                 else
464                         snprintf(buf, sizeof(buf), "L%d", level);
465
466                 cpu->cache[cpu->ct_cache].caname = xstrdup(buf);
467
468                 /* cache size */
469                 path_scanstr(buf, _PATH_SYS_CPU0 "/cache/%s/size", dir->d_name);
470                 cpu->cache[cpu->ct_cache].casize = xstrdup(buf);
471
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",
475                                 dir->d_name);
476                 cpu->ct_cache++;
477         }
478 }
479
480 static void
481 read_nodes(struct cpu_desc *cpu)
482 {
483         int i;
484
485         /* number of NUMA node */
486         while (path_exist(_PATH_SYS_SYSTEM "/node/node%d", cpu->ct_node))
487                 cpu->ct_node++;
488
489         cpu->nodecpu = (int *) malloc(cpu->ct_node * sizeof(int));
490         if (!cpu->nodecpu)
491                 err(EXIT_FAILURE, _("error: malloc failed"));
492
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",
497                                         i);
498 }
499
500 static void
501 check_system(void)
502 {
503         /* Read through sysfs. */
504         if (access(_PATH_SYS_SYSTEM, F_OK))
505                 errx(EXIT_FAILURE,
506                      _("error: /sys filesystem is not accessable."));
507
508         if (!access(_PATH_SYS_SYSTEM "/node", F_OK))
509                 have_node = 1;
510
511         if (!access(_PATH_SYS_CPU0 "/topology/thread_siblings", F_OK))
512                 have_topology = 1;
513
514         if (!access(_PATH_SYS_CPU0 "/cache", F_OK))
515                 have_cache = 1;
516 }
517
518 static void
519 print_parsable(struct cpu_desc *cpu)
520 {
521         int i, j;
522
523         printf(_(
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"));
528
529         if (have_cache) {
530                 /* separator between CPU topology and cache information */
531                 putchar(',');
532
533                 for (i = cpu->ct_cache - 1; i >= 0; i--)
534                         printf(",%s", cpu->cache[i].caname);
535         }
536         putchar('\n');
537
538         for (i = 0; i < cpu->ct_cpu; i++) {
539                 printf("%d", i);
540
541                 if (have_topology)
542                         printf(",%d,%d",
543                                 i / cpu->ct_thread,
544                                 i / cpu->ct_core / cpu->ct_thread);
545                 else
546                         printf(",,");
547
548                 if (have_node) {
549                         int c = 0;
550
551                         for (j = 0; j < cpu->ct_node; j++) {
552                                 c += cpu->nodecpu[j];
553                                 if (i < c) {
554                                         printf(",%d", j);
555                                         break;
556                                 }
557                         }
558                 } else
559                         putchar(',');
560
561                 if (have_cache) {
562                         putchar(',');
563
564                         for (j = cpu->ct_cache - 1; j >= 0; j--) {
565                                 /* If shared_cpu_map is 0, all CPUs share the same
566                                    cache. */
567                                 if (cpu->cache[j].camap == 0)
568                                         cpu->cache[j].camap = cpu->ct_core *
569                                                               cpu->ct_thread;
570
571                                 printf(",%d", i / cpu->cache[j].camap);
572                         }
573                 }
574                 putchar('\n');
575         }
576 }
577
578
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)
582
583 static void
584 print_readable(struct cpu_desc *cpu)
585 {
586         print_s("Architecture:", cpu->arch);
587         print_n("CPU(s):", cpu->ct_cpu);
588
589         if (have_topology) {
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);
593         }
594
595         if (have_node)
596                 print_n(_("NUMA node(s):"), cpu->ct_node);
597         if (cpu->vendor)
598                 print_s(_("Vendor ID:"), cpu->vendor);
599         if (cpu->family)
600                 print_s(_("CPU family:"), cpu->family);
601         if (cpu->model)
602                 print_s(_("Model:"), cpu->model);
603         if (cpu->stepping)
604                 print_s(_("Stepping:"), cpu->stepping);
605         if (cpu->mhz)
606                 print_s(_("CPU MHz:"), cpu->mhz);
607         if (cpu->virtflag) {
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");
612         }
613         if (cpu->hyper) {
614                 print_s(_("Hypervisor vendor:"), hv_vendors[cpu->hyper]);
615                 print_s(_("Virtualization type:"), virt_types[cpu->virtype]);
616         }
617         if (have_cache) {
618                 char buf[512];
619                 int i;
620
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);
625                 }
626         }
627 }
628
629 void usage(int rc)
630 {
631         printf(_("Usage: %s [option]\n"),
632                         program_invocation_short_name);
633
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"));
638         exit(rc);
639 }
640
641 static int
642 ca_compare(const void *a, const void *b)
643 {
644         struct ca_desc *cache1 = (struct ca_desc *) a;
645         struct ca_desc *cache2 = (struct ca_desc *) b;
646
647         return strcmp(cache2->caname, cache1->caname);
648 }
649
650 int main(int argc, char *argv[])
651 {
652         struct cpu_desc _cpu, *cpu = &_cpu;
653         int parsable = 0, c;
654
655         struct option longopts[] = {
656                 { "help",       no_argument,       0, 'h' },
657                 { "parse",      no_argument,       0, 'p' },
658                 { "sysroot",    required_argument, 0, 's' },
659                 { NULL,         0, 0, 0 }
660         };
661
662         setlocale(LC_MESSAGES, "");
663         bindtextdomain(PACKAGE, LOCALEDIR);
664         textdomain(PACKAGE);
665
666         while((c = getopt_long(argc, argv, "hps:", longopts, NULL)) != -1) {
667                 switch (c) {
668                 case 'h':
669                         usage(EXIT_SUCCESS);
670                 case 'p':
671                         parsable = 1;
672                         break;
673                 case 's':
674                         strncpy(pathbuf, optarg, sizeof(pathbuf));
675                         break;
676                 default:
677                         usage(EXIT_FAILURE);
678                 }
679         }
680
681         if (chdir(pathbuf) == -1)
682                 errx(EXIT_FAILURE,
683                      _("error: change working directory to %s."), pathbuf);
684
685         memset(cpu, 0, sizeof(*cpu));
686
687         check_system();
688
689         read_basicinfo(cpu);
690
691         if (have_topology)
692                 read_topology(cpu);
693         if (have_cache) {
694                 read_cache(cpu);
695                 qsort(cpu->cache, cpu->ct_cache, sizeof(struct ca_desc), ca_compare);
696         }
697         if (have_node)
698                 read_nodes(cpu);
699
700         read_hypervisor(cpu);
701
702         /* Show time! */
703         if (parsable)
704                 print_parsable(cpu);
705         else
706                 print_readable(cpu);
707
708         return EXIT_SUCCESS;
709 }