1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <unistd.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <errno.h> 34 #include <time.h> 35 #include <kstat.h> 36 #include <sys/processor.h> /* for processorid_t */ 37 38 #include <libintl.h> 39 #include <locale.h> 40 41 #ifndef TEXT_DOMAIN 42 #define TEXT_DOMAIN "SYS_TEST" 43 #endif 44 45 #define NCPUSTATES 6 /* on-line, off-line, no-intr, faulted, */ 46 /* spare, power-off */ 47 48 /* 49 * Possible states that a cpu may be in, and their corresponding 50 * localized versions. 51 */ 52 static struct { 53 const char *state; /* State returned in kstat. */ 54 const char *lstate; /* Localized version of the state. */ 55 } cpu_states[NCPUSTATES]; 56 57 static const char cmdname[] = "psrinfo"; 58 59 #define CORES_PER_CHIP_MAX 256 /* ABEN (Arbitrarily big-enough number) */ 60 static int chip_count = 0; 61 static int max_chip_id; 62 static struct chip { 63 int visible; 64 int online; 65 int core_count; 66 char impl[128]; 67 char brand[128]; 68 processorid_t cores[CORES_PER_CHIP_MAX]; 69 } *chips; 70 71 static void cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity, 72 int phys_view, int visible); 73 static void print_cpuid_list(processorid_t *ids, int count); 74 75 static void 76 usage(char *msg) 77 { 78 if (msg != NULL) 79 (void) fprintf(stderr, "%s: %s\n", cmdname, msg); 80 (void) fprintf(stderr, 81 gettext("usage: \n\t%s [-v] [-p] [processor_id ...]\n" 82 "\t%s -s [-p] processor_id\n"), cmdname, cmdname); 83 exit(2); 84 } 85 86 int 87 main(int argc, char *argv[]) 88 { 89 kstat_ctl_t *kc; 90 kstat_t *ksp; 91 int c; 92 processorid_t cpu; 93 int verbosity = 1; /* 0 = silent, 1 = normal, 3 = verbose */ 94 int phys_view = 0; 95 int errors = 0; 96 char *errptr; 97 98 (void) setlocale(LC_ALL, ""); 99 (void) textdomain(TEXT_DOMAIN); 100 101 while ((c = getopt(argc, argv, "psv")) != EOF) { 102 switch (c) { 103 case 'v': 104 verbosity |= 2; 105 break; 106 case 's': 107 verbosity &= ~1; 108 break; 109 case 'p': 110 phys_view = 1; 111 break; 112 default: 113 usage(NULL); 114 } 115 } 116 117 argc -= optind; 118 argv += optind; 119 120 if (verbosity == 2) 121 usage(gettext("options -s and -v are mutually exclusive")); 122 123 if (verbosity == 0 && argc != 1) 124 usage(gettext("must specify exactly one processor if -s used")); 125 126 if ((kc = kstat_open()) == NULL) { 127 (void) fprintf(stderr, gettext("%s: kstat_open() failed: %s\n"), 128 cmdname, strerror(errno)); 129 exit(1); 130 } 131 132 /* 133 * Build localized cpu state table. 134 */ 135 cpu_states[0].state = PS_ONLINE; 136 cpu_states[0].lstate = gettext(PS_ONLINE); 137 cpu_states[1].state = PS_POWEROFF; 138 cpu_states[1].lstate = gettext(PS_POWEROFF); 139 cpu_states[2].state = PS_NOINTR; 140 cpu_states[2].lstate = gettext(PS_NOINTR); 141 cpu_states[3].state = PS_FAULTED; 142 cpu_states[3].lstate = gettext(PS_FAULTED); 143 cpu_states[4].state = PS_SPARE; 144 cpu_states[4].lstate = gettext(PS_SPARE); 145 cpu_states[5].state = PS_OFFLINE; 146 cpu_states[5].lstate = gettext(PS_OFFLINE); 147 148 if (phys_view) { 149 /* 150 * Note that we assume that MAX_CHIPID is <= MAX_CPUID. 151 * If this becomes untrue, a new sysconf() would be warranted. 152 */ 153 max_chip_id = sysconf(_SC_CPUID_MAX); 154 chips = calloc(max_chip_id + 1, sizeof (struct chip)); 155 if (chips == NULL) { 156 perror("calloc"); 157 exit(1); 158 } 159 } 160 161 /* 162 * In the physical view, we want to display all the core info or 163 * none, even when the user specifies a range of CPUIDs. So for 164 * "psrinfo -pv <range>" or "psrinfo -ps <range>", we inventory 165 * every cpu_info kstat, and *then* we go through the user 166 * specified processors merely flipping on their "visible" 167 * flags. 168 */ 169 170 if (argc == 0 || (phys_view && (verbosity != 1))) { 171 /* 172 * No processors specified. Report on all of them. 173 * Or do a complete inventory in preparation for a 174 * specified list of physical processors. See note 175 * immediately above. 176 */ 177 processorid_t maxcpu = -1; 178 179 for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) 180 if (strcmp(ksp->ks_module, "cpu_info") == 0 && 181 ksp->ks_instance > maxcpu) 182 maxcpu = ksp->ks_instance; 183 184 /* 185 * N.B. We're assuming that instance number == cpuid. 186 * A number of things break if this isn't true. In 187 * particular, print_cpuid_list() assumes that c->cores[] 188 * is numerically sorted, which is true only because we 189 * add each virtual processor in cpuid order here. 190 */ 191 for (cpu = 0; cpu <= maxcpu; cpu++) 192 if (ksp = kstat_lookup(kc, "cpu_info", cpu, NULL)) 193 cpu_info(kc, ksp, verbosity, phys_view, 194 argc == 0); 195 } 196 197 if (argc != 0) { 198 /* 199 * Report on specified processors. 200 */ 201 for (; argc > 0; argv++, argc--) { 202 if (strchr(*argv, '-') == NULL) { 203 /* individual processor id */ 204 char cpubuf[20]; 205 206 (void) sprintf(cpubuf, "cpu_info%.10s", *argv); 207 if (ksp = kstat_lookup(kc, "cpu_info", -1, 208 cpubuf)) 209 cpu_info(kc, ksp, verbosity, 210 phys_view, 1); 211 else { 212 (void) fprintf(stderr, 213 gettext("%s: processor %s: %s\n"), 214 cmdname, *argv, strerror(EINVAL)); 215 errors = 2; 216 } 217 } else { 218 /* range of processors */ 219 int first, last; 220 int found = 0; 221 222 if (verbosity == 0) { 223 usage(gettext("must specify exactly " 224 "one processor if -s used")); 225 } 226 first = (int)strtol(*argv, &errptr, 10); 227 if (*errptr++ != '-') { 228 (void) fprintf(stderr, 229 gettext("%s: invalid processor " 230 "range %s\n"), cmdname, *argv); 231 errors = 2; 232 continue; 233 } 234 last = (int)strtol(errptr, &errptr, 10); 235 if ((errptr != NULL && *errptr != '\0') || 236 last < first || first < 0) { 237 (void) fprintf(stderr, 238 gettext("%s: invalid processor " 239 "range %s\n"), cmdname, *argv); 240 errors = 2; 241 continue; 242 } 243 for (cpu = first; cpu <= last; cpu++) { 244 if (ksp = kstat_lookup(kc, "cpu_info", 245 cpu, NULL)) { 246 found = 1; 247 cpu_info(kc, ksp, verbosity, 248 phys_view, 1); 249 } 250 } 251 if (!found) { 252 (void) fprintf(stderr, 253 gettext("%s: no processors in " 254 "range %d-%d\n"), cmdname, 255 first, last); 256 } 257 } 258 } 259 } 260 261 if (phys_view) { 262 int i; 263 264 switch (verbosity) { 265 case 0: 266 /* 267 * Print "1" if all the cores on this chip are 268 * online. "0" otherwise. 269 */ 270 for (i = 0; i <= max_chip_id; i++) { 271 struct chip *c = &chips[i]; 272 273 if (!c->visible) 274 continue; 275 (void) printf("%d\n", 276 c->online == c->core_count); 277 exit(0); 278 } 279 break; 280 case 1: 281 /* 282 * Print the number of unique chips represented by 283 * all the cores specified on the command line 284 * (or, with no args, all the cores in the system). 285 */ 286 (void) printf("%d\n", chip_count); 287 break; 288 case 3: 289 /* 290 * Print a report on each chip. 291 */ 292 for (i = 0; i <= max_chip_id; i++) { 293 struct chip *c = &chips[i]; 294 295 if (!c->visible) 296 continue; 297 298 (void) printf(gettext("The physical " 299 "processor has %d virtual %s ("), 300 c->core_count, 301 c->core_count == 1 ? 302 gettext("processor") : 303 gettext("processors")); 304 print_cpuid_list(c->cores, c->core_count); 305 (void) printf(")\n"); 306 307 (void) printf(" %s\n", c->impl); 308 309 /* 310 * If the "brand" has already been embedded 311 * at the front of the "impl" string, don't 312 * print it out again .. otherwise give it 313 * a fresh line to bask upon .. 314 */ 315 if (strncmp(c->impl, c->brand, 316 strlen(c->brand)) != 0) 317 (void) printf("\t%s\n", c->brand); 318 } 319 break; 320 } 321 } 322 323 return (errors); 324 } 325 326 #define GETLONG(name) ((kstat_named_t *)kstat_data_lookup(ksp, name))->value.l 327 #define GETSTR(name) ((kstat_named_t *)kstat_data_lookup(ksp, name))->value.c 328 #define GETLONGSTR(name) \ 329 KSTAT_NAMED_STR_PTR((kstat_named_t *)kstat_data_lookup(ksp, name)) 330 331 /* 332 * Utility function to retrieve the localized version of the cpu state string. 333 */ 334 static const char * 335 get_cpu_state(const char *state) 336 { 337 int i; 338 339 for (i = 0; i < NCPUSTATES; i++) 340 if (strcmp(cpu_states[i].state, state) == 0) 341 return (cpu_states[i].lstate); 342 return (gettext("(unknown)")); 343 } 344 345 static void 346 cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity, int phys_view, 347 int visible) 348 { 349 char curtime[40], start[40]; 350 processorid_t cpu_id = ksp->ks_instance; 351 time_t now = time(NULL); 352 353 if (kstat_read(kc, ksp, NULL) == -1) { 354 (void) fprintf(stderr, 355 gettext("%s: kstat_read() failed for cpu %d: %s\n"), 356 cmdname, cpu_id, strerror(errno)); 357 exit(1); 358 } 359 360 if (phys_view) { 361 kstat_named_t *k = 362 (kstat_named_t *)kstat_data_lookup(ksp, "chip_id"); 363 struct chip *c; 364 365 if (k == NULL) { 366 (void) fprintf(stderr, 367 gettext("%s: Physical processor view " 368 "not supported\n"), 369 cmdname); 370 exit(1); 371 } 372 373 c = &chips[k->value.i32]; 374 375 if (visible && c->core_count != c->visible) { 376 /* 377 * We've already inventoried this chip. And the user 378 * specified a range of CPUIDs. So, now we just 379 * need to note that this is one of the chips to 380 * display. 381 */ 382 c->visible++; 383 return; 384 } 385 386 if (c->core_count == 0) { 387 char *str; 388 389 str = GETLONGSTR("implementation"); 390 (void) strlcpy(c->impl, str ? str : "(unknown)", 391 sizeof (c->impl)); 392 393 str = GETLONGSTR("brand"); 394 (void) strlcpy(c->brand, str ? str : "(unknown)", 395 sizeof (c->brand)); 396 397 chip_count++; 398 } 399 400 c->cores[c->core_count] = cpu_id; 401 c->core_count++; 402 c->online += strcmp(GETSTR("state"), "on-line") == 0; 403 404 if (visible) 405 c->visible++; 406 return; 407 } 408 409 (void) strftime(start, sizeof (start), gettext("%m/%d/%Y %T"), 410 localtime((time_t *)&GETLONG("state_begin"))); 411 (void) strftime(curtime, sizeof (curtime), gettext("%m/%d/%Y %T"), 412 localtime(&now)); 413 414 if (verbosity == 0) { 415 (void) printf("%d\n", strcmp(GETSTR("state"), "on-line") == 0); 416 return; 417 } 418 if (verbosity == 1) { 419 (void) printf(gettext("%d\t%-8s since %s\n"), cpu_id, 420 get_cpu_state(GETSTR("state")), start); 421 return; 422 } 423 424 (void) printf(gettext("Status of virtual processor %d as of: %s\n"), 425 cpu_id, curtime); 426 (void) printf(gettext(" %s since %s.\n"), 427 get_cpu_state(GETSTR("state")), start); 428 429 if (GETLONG("clock_MHz") != 0) 430 (void) printf(gettext(" The %s processor operates at %d MHz"), 431 GETSTR("cpu_type"), GETLONG("clock_MHz")); 432 else 433 (void) printf(gettext(" The %s processor operates at an " 434 "unknown frequency"), GETSTR("cpu_type")); 435 436 if (GETSTR("fpu_type")[0] == '\0') 437 (void) printf(gettext(",\n\tand has no floating point " 438 "processor.\n")); 439 else if (strchr("aeiouy", GETSTR("fpu_type")[0])) 440 (void) printf(gettext(",\n\tand has an %s floating point " 441 "processor.\n"), GETSTR("fpu_type")); 442 else 443 (void) printf(gettext(",\n\tand has a %s floating point " 444 "processor.\n"), GETSTR("fpu_type")); 445 } 446 447 448 449 static void 450 print_cpuid_list(processorid_t *ids, int count) 451 { 452 int i; 453 char *separator = ""; 454 455 for (i = 0; i < count; ++i) { 456 /* 457 * if we're not at the ends, and the two adjacent numbers 458 * are sequential, then don't print this number. 459 */ 460 if (i > 0 && i < count-1 && 461 ids[i]-1 == ids[i-1] && 462 ids[i]+1 == ids[i+1]) { 463 separator = "-"; 464 continue; 465 } 466 467 (void) printf("%s%d", separator, ids[i]); 468 separator = " "; 469 } 470 } 471