1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * This implements psrinfo(1M), a utility to report various information
  18  * about processors, cores, and threads (virtual cpus).  This is mostly
  19  * intended for human consumption - this utility doesn't do much more than
  20  * simply process kstats for human readability.
  21  *
  22  * All the relevant kstats are in the cpu_info kstat module.
  23  */
  24 
  25 #include <stdio.h>
  26 #include <stdlib.h>
  27 #include <unistd.h>
  28 #include <string.h>
  29 #include <kstat.h>
  30 #include <libintl.h>
  31 #include <locale.h>
  32 #include <libgen.h>
  33 #include <ctype.h>
  34 #include <errno.h>
  35 
  36 #define _(x)    gettext(x)
  37 #if XGETTEXT
  38 /* These CPU states are here for benefit of xgettext */
  39 _("on-line")
  40 _("off-line")
  41 _("faulted")
  42 _("powered-off")
  43 _("no-intr")
  44 _("spare")
  45 _("unknown")
  46 #endif
  47 
  48 /*
  49  * We deal with sorted linked lists, where the sort key is usually the
  50  * cpu id, core id, or chip id.  We generalize this with simple node.
  51  */
  52 struct link {
  53         long            l_id;
  54         struct link     *l_next;
  55         void            *l_ptr;
  56 };
  57 
  58 /*
  59  * A physical chip.  A chip can contain multiple cores and virtual cpus.
  60  */
  61 struct pchip {
  62         struct link     p_link;
  63         int             p_ncore;
  64         int             p_nvcpu;
  65         struct link     *p_cores;
  66         struct link     *p_vcpus;
  67         int             p_doit;
  68 };
  69 
  70 struct core {
  71         struct link     c_link;
  72         struct link     c_link_pchip;
  73 
  74         int             c_nvcpu;
  75         int             c_doit;
  76 
  77         struct pchip    *c_pchip;
  78         struct link     *c_vcpus;
  79 };
  80 
  81 struct vcpu {
  82         struct link     v_link;
  83 
  84         struct link     v_link_core;
  85         struct link     v_link_pchip;
  86 
  87         int             v_doit;
  88 
  89         struct pchip    *v_pchip;
  90         struct core     *v_core;
  91 
  92         char            *v_state;
  93         long            v_state_begin;
  94         char            *v_cpu_type;
  95         char            *v_fpu_type;
  96         long            v_clock_mhz;
  97         long            v_pchip_id;     /* 1 per socket */
  98         char            *v_impl;
  99         char            *v_brand;
 100         long            v_core_id;      /* n per chip_id */
 101 };
 102 
 103 static struct link *pchips = NULL;
 104 static struct link *cores = NULL;
 105 static struct link *vcpus = NULL;
 106 
 107 static const char *cmdname;
 108 
 109 static void
 110 usage(char *msg)
 111 {
 112         if (msg != NULL)
 113                 (void) fprintf(stderr, "%s: %s\n", cmdname, msg);
 114         (void) fprintf(stderr, _("usage: \n" \
 115             "\t%s [-v] [-p] [processor_id ...]\n" \
 116             "\t%s -s [-p] processor_id\n"), cmdname, cmdname);
 117         exit(2);
 118 }
 119 
 120 /* like perror, but includes the command name */
 121 static void
 122 die(const char *msg)
 123 {
 124         (void) fprintf(stderr, "%s: %s: %s\n", cmdname, msg, strerror(errno));
 125         exit(2);
 126 }
 127 
 128 static char *
 129 mystrdup(const char *src)
 130 {
 131         char *dst;
 132 
 133         if ((dst = strdup(src)) == NULL)
 134                 die(_("strdup() failed"));
 135         return (dst);
 136 }
 137 
 138 static void *
 139 zalloc(size_t size)
 140 {
 141         void *ptr;
 142 
 143         if ((ptr = calloc(1, size)) == NULL)
 144                 die(_("calloc() failed"));
 145         return (ptr);
 146 }
 147 
 148 /*
 149  * Insert a new node on a list, at the insertion point given.
 150  */
 151 static void
 152 ins_link(struct link **ins, struct link *item)
 153 {
 154         item->l_next = *ins;
 155         *ins = item;
 156 }
 157 
 158 /*
 159  * Find an id on a sorted list.  If the requested id is not found,
 160  * then the insertpt will be set (if not null) to the location where
 161  * a new node should be inserted with ins_link (see above).
 162  */
 163 static void *
 164 find_link(void *list, int id, struct link ***insertpt)
 165 {
 166         struct link **ins = list;
 167         struct link *l;
 168 
 169         while ((l = *ins) != NULL) {
 170                 if (l->l_id == id)
 171                         return (l->l_ptr);
 172                 if (l->l_id > id)
 173                         break;
 174                 ins = &l->l_next;
 175         }
 176         if (insertpt != NULL)
 177                 *insertpt = ins;
 178         return (NULL);
 179 }
 180 
 181 /*
 182  * Print the linked list of ids in parens, taking care to collapse
 183  * ranges, so instead of (0 1 2 3) it should print (0-3).
 184  */
 185 static void
 186 print_links(struct link *l)
 187 {
 188         int     start = -1;
 189         int     end = 0;
 190 
 191         (void) printf(" (");
 192         while (l != NULL) {
 193                 if (start < 0) {
 194                         start = l->l_id;
 195                 }
 196                 end = l->l_id;
 197                 if ((l->l_next == NULL) ||
 198                     (l->l_next->l_id > (l->l_id + 1))) {
 199                         /* end of the contiguous group */
 200                         if (start == end) {
 201                                 (void) printf("%d", start);
 202                         } else {
 203                                 (void) printf("%d-%d", start, end);
 204                         }
 205                         if (l->l_next)
 206                                 (void) printf(" ");
 207                         start = -1;
 208                 }
 209                 l = l->l_next;
 210         }
 211         (void) printf(")");
 212 }
 213 
 214 static const char *
 215 timestr(long t)
 216 {
 217         static char buffer[256];
 218         (void) strftime(buffer, sizeof (buffer), _("%m/%d/%Y %T"),
 219             localtime(&t));
 220         return (buffer);
 221 }
 222 
 223 static void
 224 print_vp(int nspec)
 225 {
 226         struct pchip *chip;
 227         struct core *core;
 228         struct vcpu *vcpu;
 229         struct link *l1, *l2;
 230         int len;
 231         for (l1 = pchips; l1; l1 = l1->l_next) {
 232 
 233                 chip = l1->l_ptr;
 234 
 235                 if ((nspec != 0) && (chip->p_doit == 0))
 236                         continue;
 237 
 238                 vcpu = chip->p_vcpus->l_ptr;
 239 
 240                 /*
 241                  * Note that some of the way these strings are broken up are
 242                  * to accommodate the legacy translations so that we won't
 243                  * have to retranslate for this utility.
 244                  */
 245                 if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
 246                         (void) printf(_("%s has %d virtual %s"),
 247                             _("The physical processor"),
 248                             chip->p_nvcpu,
 249                             chip->p_nvcpu > 1 ?
 250                             _("processors") :
 251                             _("processor"));
 252                 } else {
 253                         (void) printf(_("%s has %d %s and %d virtual %s"),
 254                             _("The physical processor"),
 255                             chip->p_ncore, _("cores"),
 256                             chip->p_nvcpu,
 257                             chip->p_nvcpu > 1 ?
 258                             _("processors") : _("processor"));
 259                 }
 260 
 261                 print_links(chip->p_vcpus);
 262                 (void) putchar('\n');
 263 
 264                 if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
 265                         if (strlen(vcpu->v_impl)) {
 266                                 (void) printf("  %s\n", vcpu->v_impl);
 267                         }
 268                         if (((len = strlen(vcpu->v_brand)) != 0) &&
 269                             (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
 270                                 (void) printf("\t%s", vcpu->v_brand);
 271                         (void) putchar('\n');
 272                 } else {
 273                         for (l2 = chip->p_cores; l2; l2 = l2->l_next) {
 274                                 core = l2->l_ptr;
 275                                 (void) printf(_("  %s has %d virtual %s"),
 276                                     _("The core"),
 277                                     core->c_nvcpu,
 278                                     chip->p_nvcpu > 1 ?
 279                                     _("processors") : _("processor"));
 280                                 print_links(core->c_vcpus);
 281                                 (void) putchar('\n');
 282                         }
 283                         if (strlen(vcpu->v_impl)) {
 284                                 (void) printf("    %s\n", vcpu->v_impl);
 285                         }
 286                         if (((len = strlen(vcpu->v_brand)) != 0) &&
 287                             (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
 288                                 (void) printf("      %s\n", vcpu->v_brand);
 289                 }
 290         }
 291 }
 292 
 293 static void
 294 print_ps(void)
 295 {
 296         int online = 1;
 297         struct pchip *p;
 298         struct vcpu *v;
 299         struct link *l;
 300 
 301         /*
 302          * Report "1" if all cpus colocated on the same chip are online.
 303          */
 304         for (l = pchips; l != NULL; l = l->l_next) {
 305                 p = l->l_ptr;
 306                 if (p->p_doit)
 307                         break;
 308         }
 309         if (p == NULL)
 310                 return; /* should never happen! */
 311         for (l = p->p_vcpus; l != NULL; l = l->l_next) {
 312                 v = l->l_ptr;
 313                 if (strcmp(v->v_state, "on-line") != 0) {
 314                         online = 0;
 315                         break;
 316                 }
 317         }
 318 
 319         (void) printf("%d\n", online);
 320 }
 321 
 322 static void
 323 print_s(void)
 324 {
 325         struct link *l;
 326 
 327         /*
 328          * Find the processor (there will be only one) that we selected,
 329          * and report whether or not it is online.
 330          */
 331         for (l = vcpus; l != NULL; l = l->l_next) {
 332                 struct vcpu *v = l->l_ptr;
 333                 if (v->v_doit) {
 334                         (void) printf("%d\n",
 335                             strcmp(v->v_state, "on-line") == 0 ? 1 : 0);
 336                         return;
 337                 }
 338         }
 339 }
 340 
 341 static void
 342 print_p(int nspec)
 343 {
 344         struct          link *l1, *l2;
 345         int             online = 0;
 346 
 347         /*
 348          * Print the number of physical packages with at least one processor
 349          * online.
 350          */
 351         for (l1 = pchips; l1 != NULL; l1 = l1->l_next) {
 352                 struct pchip *p = l1->l_ptr;
 353                 if ((nspec == 0) || (p->p_doit)) {
 354 
 355                         for (l2 = p->p_vcpus; l2 != NULL; l2 = l2->l_next) {
 356                                 struct vcpu *v = l2->l_ptr;
 357                                 if (strcmp(v->v_state, "on-line") == 0) {
 358                                         online++;
 359                                         break;
 360                                 }
 361                         }
 362                 }
 363         }
 364         (void) printf("%d\n", online);
 365 }
 366 
 367 static void
 368 print_v(int nspec)
 369 {
 370         struct link     *l;
 371 
 372         for (l = vcpus; l != NULL; l = l->l_next) {
 373                 struct vcpu *v = l->l_ptr;
 374 
 375                 if ((nspec != 0) && (!v->v_doit))
 376                         continue;
 377                 (void) printf(_("Status of virtual processor %d as of: "),
 378                     l->l_id);
 379                 (void) printf("%s\n", timestr(time(NULL)));
 380                 (void) printf(_("  %s since %s.\n"),
 381                     _(v->v_state), timestr(v->v_state_begin));
 382                 if (v->v_clock_mhz) {
 383                         (void) printf(
 384                             _("  The %s processor operates at %llu MHz,\n"),
 385                             v->v_cpu_type, (unsigned long long)v->v_clock_mhz);
 386                 } else {
 387                         (void) printf(
 388                             _("  The %s processor operates at " \
 389                             "an unknown frequency,\n"), v->v_cpu_type);
 390                 }
 391                 switch (*v->v_fpu_type) {
 392                 case '\0':
 393                         (void) printf(
 394                             _("\tand has no floating point processor.\n"));
 395                         break;
 396                 case 'a': case 'A':
 397                 case 'e': case 'E':
 398                 case 'i': case 'I':
 399                 case 'o': case 'O':
 400                 case 'u': case 'U':
 401                 case 'y': case 'Y':
 402                         (void) printf(
 403                             _("\tand has an %s floating point processor.\n"),
 404                             v->v_fpu_type);
 405                         break;
 406                 default:
 407                         (void) printf(
 408                             _("\tand has a %s floating point processor.\n"),
 409                             v->v_fpu_type);
 410                         break;
 411                 }
 412         }
 413 }
 414 
 415 static void
 416 print_normal(int nspec)
 417 {
 418         struct link     *l;
 419         struct vcpu     *v;
 420 
 421         for (l = vcpus; l != NULL; l = l->l_next) {
 422                 v = l->l_ptr;
 423                 if ((nspec == 0) || (v->v_doit)) {
 424                         (void) printf(_("%d\t%-8s  since %s\n"),
 425                             l->l_id, _(v->v_state), timestr(v->v_state_begin));
 426                 }
 427         }
 428 }
 429 
 430 int
 431 main(int argc, char **argv)
 432 {
 433         kstat_ctl_t     *kc;
 434         kstat_t         *ksp;
 435         kstat_named_t   *knp;
 436         struct vcpu     *vc;
 437         struct core     *core;
 438         struct pchip    *chip;
 439         struct link     **ins;
 440         char            *s;
 441         int             nspec;
 442         int             optc;
 443         int             opt_s = 0;
 444         int             opt_p = 0;
 445         int             opt_v = 0;
 446         int             ex = 0;
 447 
 448         cmdname = basename(argv[0]);
 449 
 450 
 451         (void) setlocale(LC_ALL, "");
 452 #if !defined(TEXT_DOMAIN)
 453 #define TEXT_DOMAIN     "SYS_TEST"
 454 #endif
 455         (void) textdomain(TEXT_DOMAIN);
 456 
 457         /* collect the kstats */
 458         if ((kc = kstat_open()) == NULL)
 459                 die(_("kstat_open() failed"));
 460 
 461         if ((ksp = kstat_lookup(kc, "cpu_info", -1, NULL)) == NULL)
 462                 die(_("kstat_lookup() failed"));
 463 
 464         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
 465 
 466                 if (strcmp(ksp->ks_module, "cpu_info") != 0)
 467                         continue;
 468                 if (kstat_read(kc, ksp, NULL) == NULL)
 469                         die(_("kstat_read() failed"));
 470 
 471                 vc = find_link(&vcpus, ksp->ks_instance, &ins);
 472                 if (vc == NULL) {
 473                         vc = zalloc(sizeof (struct vcpu));
 474                         vc->v_link.l_id = ksp->ks_instance;
 475                         vc->v_link_core.l_id = ksp->ks_instance;
 476                         vc->v_link_pchip.l_id = ksp->ks_instance;
 477                         vc->v_link.l_ptr = vc;
 478                         vc->v_link_core.l_ptr = vc;
 479                         vc->v_link_pchip.l_ptr = vc;
 480                         ins_link(ins, &vc->v_link);
 481                 }
 482 
 483                 if ((knp = kstat_data_lookup(ksp, "state")) != NULL) {
 484                         vc->v_state = mystrdup(knp->value.c);
 485                 } else {
 486                         vc->v_state = "unknown";
 487                 }
 488 
 489                 if ((knp = kstat_data_lookup(ksp, "cpu_type")) != NULL) {
 490                         vc->v_cpu_type = mystrdup(knp->value.c);
 491                 }
 492                 if ((knp = kstat_data_lookup(ksp, "fpu_type")) != NULL) {
 493                         vc->v_fpu_type = mystrdup(knp->value.c);
 494                 }
 495 
 496                 if ((knp = kstat_data_lookup(ksp, "state_begin")) != NULL) {
 497                         vc->v_state_begin = knp->value.l;
 498                 }
 499 
 500                 if ((knp = kstat_data_lookup(ksp, "clock_MHz")) != NULL) {
 501                         vc->v_clock_mhz = knp->value.l;
 502                 }
 503 
 504                 if ((knp = kstat_data_lookup(ksp, "brand")) == NULL) {
 505                         vc->v_brand = _("(unknown)");
 506                 } else {
 507                         vc->v_brand = mystrdup(knp->value.str.addr.ptr);
 508                 }
 509 
 510                 if ((knp = kstat_data_lookup(ksp, "implementation")) == NULL) {
 511                         vc->v_impl = _("(unknown)");
 512                 } else {
 513                         vc->v_impl = mystrdup(knp->value.str.addr.ptr);
 514                 }
 515                 /*
 516                  * Legacy code removed the chipid and cpuid fields... we
 517                  * do the same for compatibility.  Note that the original
 518                  * pattern is a bit strange, and we have to emulate this because
 519                  * on SPARC we *do* emit these.  The original pattern we are
 520                  * emulating is: $impl =~ s/(cpuid|chipid)\s*\w+\s+//;
 521                  */
 522                 if ((s = strstr(vc->v_impl, "chipid")) != NULL) {
 523                         char *x = s + strlen("chipid");
 524                         while (isspace(*x))
 525                                 x++;
 526                         if ((!isalnum(*x)) && (*x != '_'))
 527                                 goto nochipid;
 528                         while (isalnum(*x) || (*x == '_'))
 529                                 x++;
 530                         if (!isspace(*x))
 531                                 goto nochipid;
 532                         while (isspace(*x))
 533                                 x++;
 534                         (void) strcpy(s, x);
 535                 }
 536 nochipid:
 537                 if ((s = strstr(vc->v_impl, "cpuid")) != NULL) {
 538                         char *x = s + strlen("cpuid");
 539                         while (isspace(*x))
 540                                 x++;
 541                         if ((!isalnum(*x)) && (*x != '_'))
 542                                 goto nocpuid;
 543                         while (isalnum(*x) || (*x == '_'))
 544                                 x++;
 545                         if (!isspace(*x))
 546                                 goto nocpuid;
 547                         while (isspace(*x))
 548                                 x++;
 549                         (void) strcpy(s, x);
 550                 }
 551 nocpuid:
 552 
 553                 if ((knp = kstat_data_lookup(ksp, "chip_id")) != NULL)
 554                         vc->v_pchip_id = knp->value.l;
 555                 chip = find_link(&pchips, vc->v_pchip_id, &ins);
 556                 if (chip == NULL) {
 557                         chip = zalloc(sizeof (struct pchip));
 558                         chip->p_link.l_id = vc->v_pchip_id;
 559                         chip->p_link.l_ptr = chip;
 560                         ins_link(ins, &chip->p_link);
 561                 }
 562                 vc->v_pchip = chip;
 563 
 564                 if ((knp = kstat_data_lookup(ksp, "core_id")) != NULL)
 565                         vc->v_core_id = knp->value.l;
 566                 core = find_link(&cores, vc->v_core_id, &ins);
 567                 if (core == NULL) {
 568                         core = zalloc(sizeof (struct core));
 569                         core->c_link.l_id = vc->v_core_id;
 570                         core->c_link.l_ptr = core;
 571                         core->c_link_pchip.l_id = vc->v_core_id;
 572                         core->c_link_pchip.l_ptr = core;
 573                         core->c_pchip = chip;
 574                         ins_link(ins, &core->c_link);
 575                         chip->p_ncore++;
 576                         (void) find_link(&chip->p_cores, core->c_link.l_id,
 577                             &ins);
 578                         ins_link(ins, &core->c_link_pchip);
 579                 }
 580                 vc->v_core = core;
 581 
 582 
 583 
 584                 /* now put other linkages in place */
 585                 (void) find_link(&chip->p_vcpus, vc->v_link.l_id, &ins);
 586                 ins_link(ins, &vc->v_link_pchip);
 587                 chip->p_nvcpu++;
 588 
 589                 (void) find_link(&core->c_vcpus, vc->v_link.l_id, &ins);
 590                 ins_link(ins, &vc->v_link_core);
 591                 core->c_nvcpu++;
 592         }
 593 
 594         (void) kstat_close(kc);
 595 
 596         nspec = 0;
 597 
 598         while ((optc = getopt(argc, argv, "pvs")) != EOF) {
 599                 switch (optc) {
 600                 case 's':
 601                         opt_s = 1;
 602                         break;
 603                 case 'p':
 604                         opt_p = 1;
 605                         break;
 606                 case 'v':
 607                         opt_v = 1;
 608                         break;
 609                 default:
 610                         usage(NULL);
 611                 }
 612         }
 613 
 614         while (optind < argc) {
 615                 long id;
 616                 char *eptr;
 617                 struct link *l;
 618                 id = strtol(argv[optind], &eptr, 10);
 619                 l = find_link(&vcpus, id, NULL);
 620                 if ((*eptr != '\0') || (l == NULL)) {
 621                         (void) fprintf(stderr,
 622                             _("%s: processor %s: Invalid argument\n"),
 623                             cmdname, argv[optind]);
 624                         ex = 2;
 625                 } else {
 626                         ((struct vcpu *)l->l_ptr)->v_doit = 1;
 627                         ((struct vcpu *)l->l_ptr)->v_pchip->p_doit = 1;
 628                         ((struct vcpu *)l->l_ptr)->v_core->c_doit = 1;
 629                 }
 630                 nspec++;
 631                 optind++;
 632         }
 633 
 634         if (opt_s && opt_v) {
 635                 usage(_("options -s and -v are mutually exclusive"));
 636         }
 637         if (opt_s && nspec != 1) {
 638                 usage(_("must specify exactly one processor if -s used"));
 639         }
 640         if (opt_v && opt_p) {
 641                 print_vp(nspec);
 642         } else if (opt_s && opt_p) {
 643                 print_ps();
 644         } else if (opt_p) {
 645                 print_p(nspec);
 646         } else if (opt_v) {
 647                 print_v(nspec);
 648         } else if (opt_s) {
 649                 print_s();
 650         } else {
 651                 print_normal(nspec);
 652         }
 653 
 654         return (ex);
 655 }