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