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 2004 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 74 static void 75 usage(char *msg) 76 { 77 if (msg != NULL) 78 (void) fprintf(stderr, "%s: %s\n", cmdname, msg); 79 (void) fprintf(stderr, 80 gettext("usage: \n\t%s [-v] [-p] [processor_id ...]\n" 81 "\t%s -s [-p] processor_id\n"), cmdname, cmdname); 82 exit(2); 83 } 84 85 int 86 main(int argc, char *argv[]) 87 { 88 kstat_ctl_t *kc; 89 kstat_t *ksp; 90 int c; 91 processorid_t cpu; 92 int verbosity = 1; /* 0 = silent, 1 = normal, 3 = verbose */ 93 int phys_view = 0; 94 int errors = 0; 95 char *errptr; 96 97 (void) setlocale(LC_ALL, ""); 98 (void) textdomain(TEXT_DOMAIN); 99 100 while ((c = getopt(argc, argv, "psv")) != EOF) { 101 switch (c) { 102 case 'v': 103 verbosity |= 2; 104 break; 105 case 's': 106 verbosity &= ~1; 107 break; 108 case 'p': 109 phys_view = 1; 110 break; 111 default: 112 usage(NULL); 113 } 114 } 115 116 argc -= optind; 117 argv += optind; 118 119 if (verbosity == 2) 120 usage(gettext("options -s and -v are mutually exclusive")); 121 122 if (verbosity == 0 && argc != 1) 123 usage(gettext("must specify exactly one processor if -s used")); 124 125 if ((kc = kstat_open()) == NULL) { 126 (void) fprintf(stderr, gettext("%s: kstat_open() failed: %s\n"), 127 cmdname, strerror(errno)); 128 exit(1); 129 } 130 131 /* 132 * Build localized cpu state table. 133 */ 134 cpu_states[0].state = PS_ONLINE; 135 cpu_states[0].lstate = gettext(PS_ONLINE); 136 cpu_states[1].state = PS_POWEROFF; 137 cpu_states[1].lstate = gettext(PS_POWEROFF); 138 cpu_states[2].state = PS_NOINTR; 139 cpu_states[2].lstate = gettext(PS_NOINTR); 140 cpu_states[3].state = PS_FAULTED; 141 cpu_states[3].lstate = gettext(PS_FAULTED); 142 cpu_states[4].state = PS_SPARE; 143 cpu_states[4].lstate = gettext(PS_SPARE); 144 cpu_states[5].state = PS_OFFLINE; 145 cpu_states[5].lstate = gettext(PS_OFFLINE); 146 147 if (phys_view) { 148 /* 149 * Note that we assume that MAX_CHIPID is <= MAX_CPUID. 150 * If this becomes untrue, a new sysconf() would be warranted. 151 */ 152 max_chip_id = sysconf(_SC_CPUID_MAX); 153 chips = calloc(max_chip_id + 1, sizeof (struct chip)); 154 if (chips == NULL) { 155 perror("calloc"); 156 exit(1); 157 } 158 } 159 160 /* 161 * In the physical view, we want to display all the core info or 162 * none, even when the user specifies a range of CPUIDs. So for 163 * "psrinfo -pv <range>" or "psrinfo -ps <range>", we inventory 164 * every cpu_info kstat, and *then* we go through the user 165 * specified processors merely flipping on their "visible" 166 * flags. 167 */ 168 169 if (argc == 0 || (phys_view && (verbosity != 1))) { 170 /* 171 * No processors specified. Report on all of them. 172 * Or do a complete inventory in preparation for a 173 * specified list of physical processors. See note 174 * immediately above. 175 */ 176 processorid_t maxcpu = -1; 177 178 for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) 179 if (strcmp(ksp->ks_module, "cpu_info") == 0 && 180 ksp->ks_instance > maxcpu) 181 maxcpu = ksp->ks_instance; 182 183 for (cpu = 0; cpu <= maxcpu; cpu++) 184 if (ksp = kstat_lookup(kc, "cpu_info", cpu, NULL)) 185 cpu_info(kc, ksp, verbosity, phys_view, 186 argc == 0); 187 } 188 189 if (argc != 0) { 190 /* 191 * Report on specified processors. 192 */ 193 for (; argc > 0; argv++, argc--) { 194 if (strchr(*argv, '-') == NULL) { 195 /* individual processor id */ 196 char cpubuf[20]; 197 198 (void) sprintf(cpubuf, "cpu_info%.10s", *argv); 199 if (ksp = kstat_lookup(kc, "cpu_info", -1, 200 cpubuf)) 201 cpu_info(kc, ksp, verbosity, 202 phys_view, 1); 203 else { 204 (void) fprintf(stderr, 205 gettext("%s: processor %s: %s\n"), 206 cmdname, *argv, strerror(EINVAL)); 207 errors = 2; 208 } 209 } else { 210 /* range of processors */ 211 int first, last; 212 int found = 0; 213 214 if (verbosity == 0) { 215 usage(gettext("must specify exactly " 216 "one processor if -s used")); 217 } 218 first = (int)strtol(*argv, &errptr, 10); 219 if (*errptr++ != '-') { 220 (void) fprintf(stderr, 221 gettext("%s: invalid processor " 222 "range %s\n"), cmdname, *argv); 223 errors = 2; 224 continue; 225 } 226 last = (int)strtol(errptr, &errptr, 10); 227 if ((errptr != NULL && *errptr != '\0') || 228 last < first || first < 0) { 229 (void) fprintf(stderr, 230 gettext("%s: invalid processor " 231 "range %s\n"), cmdname, *argv); 232 errors = 2; 233 continue; 234 } 235 for (cpu = first; cpu <= last; cpu++) { 236 if (ksp = kstat_lookup(kc, "cpu_info", 237 cpu, NULL)) { 238 found = 1; 239 cpu_info(kc, ksp, verbosity, 240 phys_view, 1); 241 } 242 } 243 if (!found) { 244 (void) fprintf(stderr, 245 gettext("%s: no processors in " 246 "range %d-%d\n"), cmdname, 247 first, last); 248 } 249 } 250 } 251 } 252 253 if (phys_view) { 254 int i; 255 256 switch (verbosity) { 257 case 0: 258 /* 259 * Print "1" if all the cores on this chip are 260 * online. "0" otherwise. 261 */ 262 for (i = 0; i <= max_chip_id; i++) { 263 struct chip *c = &chips[i]; 264 265 if (!c->visible) 266 continue; 267 (void) printf("%d\n", 268 c->online == c->core_count); 269 exit(0); 270 } 271 break; 272 case 1: 273 /* 274 * Print the number of unique chips represented by 275 * all the cores specified on the command line 276 * (or, with no args, all the cores in the system). 277 */ 278 (void) printf("%d\n", chip_count); 279 break; 280 case 3: 281 /* 282 * Print a report on each chip. 283 */ 284 for (i = 0; i <= max_chip_id; i++) { 285 int j; 286 struct chip *c = &chips[i]; 287 288 if (!c->visible) 289 continue; 290 291 (void) printf(gettext("The physical " 292 "processor has %d virtual %s ("), 293 c->core_count, 294 c->core_count == 1 ? 295 gettext("processor") : 296 gettext("processors")); 297 for (j = 0; j < c->core_count; j++) { 298 if (j > 0) 299 (void) printf(", "); 300 (void) printf("%d", c->cores[j]); 301 } 302 (void) printf(")\n"); 303 304 (void) printf(" %s\n", c->impl); 305 306 /* 307 * If the "brand" has already been embedded 308 * at the front of the "impl" string, don't 309 * print it out again .. otherwise give it 310 * a fresh line to bask upon .. 311 */ 312 if (strncmp(c->impl, c->brand, 313 strlen(c->brand)) != 0) 314 (void) printf("\t%s\n", c->brand); 315 } 316 break; 317 } 318 } 319 320 return (errors); 321 } 322 323 #define GETLONG(name) ((kstat_named_t *)kstat_data_lookup(ksp, name))->value.l 324 #define GETSTR(name) ((kstat_named_t *)kstat_data_lookup(ksp, name))->value.c 325 #define GETLONGSTR(name) \ 326 KSTAT_NAMED_STR_PTR((kstat_named_t *)kstat_data_lookup(ksp, name)) 327 328 /* 329 * Utility function to retrieve the localized version of the cpu state string. 330 */ 331 static const char * 332 get_cpu_state(const char *state) 333 { 334 int i; 335 336 for (i = 0; i < NCPUSTATES; i++) 337 if (strcmp(cpu_states[i].state, state) == 0) 338 return (cpu_states[i].lstate); 339 return (gettext("(unknown)")); 340 } 341 342 static void 343 cpu_info(kstat_ctl_t *kc, kstat_t *ksp, int verbosity, int phys_view, 344 int visible) 345 { 346 char curtime[40], start[40]; 347 processorid_t cpu_id = ksp->ks_instance; 348 time_t now = time(NULL); 349 350 if (kstat_read(kc, ksp, NULL) == -1) { 351 (void) fprintf(stderr, 352 gettext("%s: kstat_read() failed for cpu %d: %s\n"), 353 cmdname, cpu_id, strerror(errno)); 354 exit(1); 355 } 356 357 if (phys_view) { 358 kstat_named_t *k = 359 (kstat_named_t *)kstat_data_lookup(ksp, "chip_id"); 360 struct chip *c; 361 362 if (k == NULL) { 363 (void) fprintf(stderr, 364 gettext("%s: Physical processor view " 365 "not supported\n"), 366 cmdname); 367 exit(1); 368 } 369 370 c = &chips[k->value.i32]; 371 372 if (visible && c->core_count != c->visible) { 373 /* 374 * We've already inventoried this chip. And the user 375 * specified a range of CPUIDs. So, now we just 376 * need to note that this is one of the chips to 377 * display. 378 */ 379 c->visible++; 380 return; 381 } 382 383 if (c->core_count == 0) { 384 char *str; 385 386 str = GETLONGSTR("implementation"); 387 (void) strlcpy(c->impl, str ? str : "(unknown)", 388 sizeof (c->impl)); 389 390 str = GETLONGSTR("brand"); 391 (void) strlcpy(c->brand, str ? str : "(unknown)", 392 sizeof (c->brand)); 393 394 chip_count++; 395 } 396 397 c->cores[c->core_count] = cpu_id; 398 c->core_count++; 399 c->online += strcmp(GETSTR("state"), "on-line") == 0; 400 401 if (visible) 402 c->visible++; 403 return; 404 } 405 406 (void) strftime(start, sizeof (start), gettext("%m/%d/%Y %T"), 407 localtime((time_t *)&GETLONG("state_begin"))); 408 (void) strftime(curtime, sizeof (curtime), gettext("%m/%d/%Y %T"), 409 localtime(&now)); 410 411 if (verbosity == 0) { 412 (void) printf("%d\n", strcmp(GETSTR("state"), "on-line") == 0); 413 return; 414 } 415 if (verbosity == 1) { 416 (void) printf(gettext("%d\t%-8s since %s\n"), cpu_id, 417 get_cpu_state(GETSTR("state")), start); 418 return; 419 } 420 421 (void) printf(gettext("Status of virtual processor %d as of: %s\n"), 422 cpu_id, curtime); 423 (void) printf(gettext(" %s since %s.\n"), 424 get_cpu_state(GETSTR("state")), start); 425 426 if (GETLONG("clock_MHz") != 0) 427 (void) printf(gettext(" The %s processor operates at %d MHz"), 428 GETSTR("cpu_type"), GETLONG("clock_MHz")); 429 else 430 (void) printf(gettext(" The %s processor operates at an " 431 "unknown frequency"), GETSTR("cpu_type")); 432 433 if (GETSTR("fpu_type")[0] == '\0') 434 (void) printf(gettext(",\n\tand has no floating point " 435 "processor.\n")); 436 else if (strchr("aeiouy", GETSTR("fpu_type")[0])) 437 (void) printf(gettext(",\n\tand has an %s floating point " 438 "processor.\n"), GETSTR("fpu_type")); 439 else 440 (void) printf(gettext(",\n\tand has a %s floating point " 441 "processor.\n"), GETSTR("fpu_type")); 442 } 443