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 } 656