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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright 2019 Joyent, Inc.
28 */
29
30 #include <sys/types.h>
31 #include <sys/processor.h>
32 #include <sys/pset.h>
33 #include <sys/lwp.h>
34 #include <sys/priocntl.h>
35 #include <sys/fxpriocntl.h>
36 #include <time.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <inttypes.h>
40 #include <unistd.h>
41 #include <limits.h>
42 #include <string.h>
43 #include <strings.h>
44 #include <thread.h>
45 #include <errno.h>
46 #include <libintl.h>
47 #include <locale.h>
48 #include <kstat.h>
49 #include <synch.h>
50 #include <libcpc.h>
51 #include <sys/resource.h>
52
53 #include "cpucmds.h"
54 #include "statcommon.h"
55
56 static struct options {
57 int debug;
58 int dotitle;
59 int dohelp;
60 int dotick;
61 int dosoaker;
62 int doperiod;
63 char *pgmname;
64 uint_t mseconds;
65 uint_t nsamples;
66 uint_t nsets;
67 uint_t mseconds_rest;
68 cpc_setgrp_t *master;
69 } __options;
70
71 /*
72 * States for soaker threads.
73 */
74 #define SOAK_PAUSE 0
75 #define SOAK_RUN 1
76
77 struct tstate {
78 processorid_t cpuid;
79 int chip_id;
80 cpc_setgrp_t *sgrp;
81 int status;
82 thread_t tid;
83 int soak_state;
84 mutex_t soak_lock;
85 cond_t soak_cv;
86 };
87
88 static const struct options *opts = (const struct options *)&__options;
89
90 static cpc_t *cpc;
91
92 struct tstate *gstate;
93 static int ncpus;
94 static int max_chip_id;
95 static int *chip_designees; /* cpuid of CPU which counts for phs chip */
96 static int smt = 0; /* If set, cpustat needs to be SMT-aware. */
97 static pcinfo_t fxinfo = { 0, "FX", 0 }; /* FX scheduler class info */
98
99 static uint_t timestamp_fmt = NODATE;
100
101 /*ARGSUSED*/
102 static void
cpustat_errfn(const char * fn,int subcode,const char * fmt,va_list ap)103 cpustat_errfn(const char *fn, int subcode, const char *fmt, va_list ap)
104 {
105 (void) fprintf(stderr, "%s: ", opts->pgmname);
106 if (opts->debug)
107 (void) fprintf(stderr, "%s: ", fn);
108 (void) vfprintf(stderr, fmt, ap);
109 }
110
111 static int cpustat(void);
112 static int get_chipid(kstat_ctl_t *kc, processorid_t cpuid);
113 static void *soaker(void *arg);
114
115
116 #if !defined(TEXT_DOMAIN)
117 #define TEXT_DOMAIN "SYS_TEST"
118 #endif
119
120 int
main(int argc,char * argv[])121 main(int argc, char *argv[])
122 {
123 struct options *opts = &__options;
124 int c, errcnt = 0, ret;
125 cpc_setgrp_t *sgrp;
126 char *errstr;
127 double period;
128 char *endp;
129 struct rlimit rl;
130
131 (void) setlocale(LC_ALL, "");
132 (void) textdomain(TEXT_DOMAIN);
133
134 if ((opts->pgmname = strrchr(argv[0], '/')) == NULL)
135 opts->pgmname = argv[0];
136 else
137 opts->pgmname++;
138
139 /* Make sure we can open enough files */
140 rl.rlim_max = rl.rlim_cur = RLIM_INFINITY;
141 if (setrlimit(RLIMIT_NOFILE, &rl) != 0) {
142 errstr = strerror(errno);
143 (void) fprintf(stderr,
144 gettext("%s: setrlimit failed - %s\n"),
145 opts->pgmname, errstr);
146 }
147
148 if ((cpc = cpc_open(CPC_VER_CURRENT)) == NULL) {
149 errstr = strerror(errno);
150 (void) fprintf(stderr, gettext("%s: cannot access performance "
151 "counters - %s\n"), opts->pgmname, errstr);
152 return (1);
153 }
154
155 (void) cpc_seterrhndlr(cpc, cpustat_errfn);
156 strtoset_errfn = cpustat_errfn;
157
158 /*
159 * Check to see if cpustat needs to be SMT-aware.
160 */
161 smt = smt_limited_cpc_hw(cpc);
162
163 /*
164 * Establish some defaults
165 */
166 opts->mseconds = 5000;
167 opts->nsamples = UINT_MAX;
168 opts->dotitle = 1;
169 if ((opts->master = cpc_setgrp_new(cpc, smt)) == NULL) {
170 (void) fprintf(stderr, gettext("%s: out of heap\n"),
171 opts->pgmname);
172 return (1);
173 }
174
175 while ((c = getopt(argc, argv, "Dc:hntT:sp:")) != EOF && errcnt == 0)
176 switch (c) {
177 case 'D': /* enable debugging */
178 opts->debug++;
179 break;
180 case 'c': /* specify statistics */
181 if ((sgrp = cpc_setgrp_newset(opts->master,
182 optarg, &errcnt)) != NULL)
183 opts->master = sgrp;
184 break;
185 case 'n': /* no titles */
186 opts->dotitle = 0;
187 break;
188 case 'p': /* periodic behavior */
189 opts->doperiod = 1;
190 period = strtod(optarg, &endp);
191 if (*endp != '\0') {
192 (void) fprintf(stderr, gettext("%s: invalid "
193 "parameter \"%s\"\n"), opts->pgmname,
194 optarg);
195 errcnt++;
196 }
197 break;
198 case 's': /* run soaker thread */
199 opts->dosoaker = 1;
200 break;
201 case 't': /* print %tick */
202 opts->dotick = 1;
203 break;
204 case 'T':
205 if (optarg) {
206 if (*optarg == 'u')
207 timestamp_fmt = UDATE;
208 else if (*optarg == 'd')
209 timestamp_fmt = DDATE;
210 else
211 errcnt++;
212 } else {
213 errcnt++;
214 }
215 break;
216 case 'h': /* help */
217 opts->dohelp = 1;
218 break;
219 case '?':
220 default:
221 errcnt++;
222 break;
223 }
224
225 switch (argc - optind) {
226 case 0:
227 break;
228 case 2:
229 opts->nsamples = strtol(argv[optind + 1], &endp, 10);
230 if (*endp != '\0') {
231 (void) fprintf(stderr,
232 gettext("%s: invalid argument \"%s\"\n"),
233 opts->pgmname, argv[optind + 1]);
234 errcnt++;
235 break;
236 }
237 /*FALLTHROUGH*/
238 case 1:
239 opts->mseconds = (uint_t)(strtod(argv[optind], &endp) * 1000.0);
240 if (*endp != '\0') {
241 (void) fprintf(stderr,
242 gettext("%s: invalid argument \"%s\"\n"),
243 opts->pgmname, argv[optind]);
244 errcnt++;
245 }
246 break;
247 default:
248 errcnt++;
249 break;
250 }
251
252 if (opts->nsamples == 0 || opts->mseconds == 0)
253 errcnt++;
254
255 if (errcnt != 0 || opts->dohelp ||
256 (opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) {
257 (void) fprintf(opts->dohelp ? stdout : stderr, gettext(
258 "Usage:\n\t%s -c spec [-c spec]... [-p period] [-T u|d]\n"
259 "\t\t[-sntD] [interval [count]]\n\n"
260 "\t-c spec\t specify processor events to be monitored\n"
261 "\t-n\t suppress titles\n"
262 "\t-p period cycle through event list periodically\n"
263 "\t-s\t run user soaker thread for system-only events\n"
264 "\t-t\t include %s register\n"
265 "\t-T d|u\t Display a timestamp in date (d) or unix "
266 "time_t (u)\n"
267 "\t-D\t enable debug mode\n"
268 "\t-h\t print extended usage information\n\n"
269 "\tUse cputrack(1) to monitor per-process statistics.\n"),
270 opts->pgmname, CPC_TICKREG_NAME);
271 if (opts->dohelp) {
272 (void) putchar('\n');
273 (void) capabilities(cpc, stdout);
274 exit(0);
275 }
276 exit(2);
277 }
278
279 /*
280 * If the user requested periodic behavior, calculate the rest time
281 * between cycles.
282 */
283 if (opts->doperiod) {
284 opts->mseconds_rest = (uint_t)((period * 1000.0) -
285 (opts->mseconds * opts->nsets));
286 if ((int)opts->mseconds_rest < 0)
287 opts->mseconds_rest = 0;
288 if (opts->nsamples != UINT_MAX)
289 opts->nsamples *= opts->nsets;
290 }
291
292 cpc_setgrp_reset(opts->master);
293 (void) setvbuf(stdout, NULL, _IOLBF, 0);
294
295 /*
296 * By design, cpustat (regrettably) has multiple threads racing in
297 * write(2) to generate output. As there are no guarantees made with
298 * respect to the atomicity of concurrent writes on non-O_APPEND file
299 * descriptors, we must set O_APPEND on stdout to assure that no output
300 * is lost. If cpustat is rearchitected such that only one thread is
301 * generating output (which would also assure that the output is always
302 * in a consistent order), this code should be removed.
303 */
304 if (fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_APPEND) == -1) {
305 (void) fprintf(stderr, gettext("%s: cannot set output to be "
306 "append-only - %s\n"), opts->pgmname, strerror(errno));
307 return (1);
308 }
309
310 /*
311 * If no system-mode only sets were created, no soaker threads will be
312 * needed.
313 */
314 if (opts->dosoaker == 1 && cpc_setgrp_has_sysonly(opts->master) == 0)
315 opts->dosoaker = 0;
316
317 ret = cpustat();
318
319 (void) cpc_close(cpc);
320
321 return (ret);
322 }
323
324 static void
print_title(cpc_setgrp_t * sgrp)325 print_title(cpc_setgrp_t *sgrp)
326 {
327 (void) printf("%7s %3s %5s ", "time", "cpu", "event");
328 if (opts->dotick)
329 (void) printf("%9s ", CPC_TICKREG_NAME);
330 (void) printf("%s\n", cpc_setgrp_gethdr(sgrp));
331 }
332
333 static void
print_sample(processorid_t cpuid,cpc_buf_t * buf,int nreq,const char * setname,int sibling)334 print_sample(processorid_t cpuid, cpc_buf_t *buf, int nreq, const char *setname,
335 int sibling)
336 {
337 char line[1024];
338 int ccnt;
339 int i;
340 uint64_t val;
341 uint64_t tick;
342 hrtime_t hrtime;
343
344 hrtime = cpc_buf_hrtime(cpc, buf);
345 tick = cpc_buf_tick(cpc, buf);
346
347 ccnt = snprintf(line, sizeof (line), "%7.3f %3d %5s ",
348 mstimestamp(hrtime), (int)cpuid, "tick");
349 if (opts->dotick)
350 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
351 "%9" PRId64 " ", tick);
352 for (i = 0; i < nreq; i++) {
353 (void) cpc_buf_get(cpc, buf, i, &val);
354 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
355 "%9" PRId64 " ", val);
356 }
357 if (opts->nsets > 1)
358 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
359 " # %s\n", setname);
360 else
361 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, "\n");
362
363 if (sibling) {
364 /*
365 * This sample is being printed for a "sibling" CPU -- that is,
366 * a CPU which does not have its own CPC set bound. It is being
367 * measured via a set bound to another CPU sharing its physical
368 * processor.
369 */
370 int designee = chip_designees[gstate[cpuid].chip_id];
371 char *p;
372
373 if ((p = strrchr(line, '#')) == NULL)
374 p = strrchr(line, '\n');
375
376 if (p != NULL) {
377 *p = '\0';
378 ccnt = strlen(line);
379 ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
380 "# counter shared with CPU %d\n", designee);
381 }
382 }
383
384 if (timestamp_fmt != NODATE)
385 print_timestamp(timestamp_fmt);
386 if (ccnt > sizeof (line))
387 ccnt = sizeof (line);
388 if (ccnt > 0)
389 (void) write(1, line, ccnt);
390
391 /*
392 * If this CPU is the chip designee for any other CPUs, print a line for
393 * them here.
394 */
395 if (smt && (sibling == 0)) {
396 for (i = 0; i < ncpus; i++) {
397 if ((i != cpuid) && (gstate[i].cpuid != -1) &&
398 (chip_designees[gstate[i].chip_id] == cpuid))
399 print_sample(i, buf, nreq, setname, 1);
400 }
401 }
402 }
403
404 static void
print_total(int ncpus,cpc_buf_t * buf,int nreq,const char * setname)405 print_total(int ncpus, cpc_buf_t *buf, int nreq, const char *setname)
406 {
407 int i;
408 uint64_t val;
409
410 (void) printf("%7.3f %3d %5s ", mstimestamp(cpc_buf_hrtime(cpc, buf)),
411 ncpus, "total");
412 if (opts->dotick)
413 (void) printf("%9" PRId64 " ", cpc_buf_tick(cpc, buf));
414 for (i = 0; i < nreq; i++) {
415 (void) cpc_buf_get(cpc, buf, i, &val);
416 (void) printf("%9" PRId64 " ", val);
417 }
418 if (opts->nsets > 1)
419 (void) printf(" # %s", setname);
420 (void) fputc('\n', stdout);
421 }
422
423 #define NSECS_PER_MSEC 1000000ll
424 #define NSECS_PER_SEC 1000000000ll
425
426 static void *
gtick(void * arg)427 gtick(void *arg)
428 {
429 struct tstate *state = arg;
430 char *errstr;
431 uint_t nsamples;
432 uint_t sample_cnt = 1;
433 hrtime_t ht, htdelta, restdelta;
434 cpc_setgrp_t *sgrp = state->sgrp;
435 cpc_set_t *this = cpc_setgrp_getset(sgrp);
436 const char *name = cpc_setgrp_getname(sgrp);
437 cpc_buf_t **data1, **data2, **scratch;
438 cpc_buf_t *tmp;
439 int nreqs;
440 thread_t tid;
441
442 htdelta = NSECS_PER_MSEC * opts->mseconds;
443 restdelta = NSECS_PER_MSEC * opts->mseconds_rest;
444 ht = gethrtime();
445
446 /*
447 * If this CPU is SMT, we run one gtick() thread per _physical_ CPU,
448 * instead of per cpu_t. The following check returns if it detects that
449 * this cpu_t has not been designated to do the counting for this
450 * physical CPU.
451 */
452 if (smt && chip_designees[state->chip_id] != state->cpuid)
453 return (NULL);
454
455 /*
456 * If we need to run a soaker thread on this CPU, start it here.
457 */
458 if (opts->dosoaker) {
459 if (cond_init(&state->soak_cv, USYNC_THREAD, NULL) != 0)
460 goto bad;
461 if (mutex_init(&state->soak_lock, USYNC_THREAD,
462 NULL) != 0)
463 goto bad;
464 (void) mutex_lock(&state->soak_lock);
465 state->soak_state = SOAK_PAUSE;
466 if (thr_create(NULL, 0, soaker, state, 0, &tid) != 0)
467 goto bad;
468
469 while (state->soak_state == SOAK_PAUSE)
470 (void) cond_wait(&state->soak_cv,
471 &state->soak_lock);
472 (void) mutex_unlock(&state->soak_lock);
473
474 /*
475 * If the soaker needs to pause for the first set, stop it now.
476 */
477 if (cpc_setgrp_sysonly(sgrp) == 0) {
478 (void) mutex_lock(&state->soak_lock);
479 state->soak_state = SOAK_PAUSE;
480 (void) mutex_unlock(&state->soak_lock);
481 }
482 }
483 if (cpc_bind_cpu(cpc, state->cpuid, this, 0) == -1)
484 goto bad;
485
486 for (nsamples = opts->nsamples; nsamples; nsamples--, sample_cnt++) {
487 hrtime_t htnow;
488 struct timespec ts;
489
490 nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
491
492 ht += htdelta;
493 htnow = gethrtime();
494 if (ht <= htnow)
495 continue;
496 ts.tv_sec = (time_t)((ht - htnow) / NSECS_PER_SEC);
497 ts.tv_nsec = (suseconds_t)((ht - htnow) % NSECS_PER_SEC);
498
499 (void) nanosleep(&ts, NULL);
500
501 if (opts->nsets == 1) {
502 /*
503 * If we're dealing with one set, buffer usage is:
504 *
505 * data1 = most recent data snapshot
506 * data2 = previous data snapshot
507 * scratch = used for diffing data1 and data2
508 *
509 * Save the snapshot from the previous sample in data2
510 * before putting the current sample in data1.
511 */
512 tmp = *data1;
513 *data1 = *data2;
514 *data2 = tmp;
515 if (cpc_set_sample(cpc, this, *data1) != 0)
516 goto bad;
517 cpc_buf_sub(cpc, *scratch, *data1, *data2);
518
519 print_sample(state->cpuid, *scratch, nreqs, name, 0);
520 } else {
521 /*
522 * More than one set is in use (multiple -c options
523 * given). Buffer usage in this case is:
524 *
525 * data1 = total counts for this set since program began
526 * data2 = unused
527 * scratch = most recent data snapshot
528 */
529 name = cpc_setgrp_getname(sgrp);
530 nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2,
531 &scratch);
532
533 if (cpc_set_sample(cpc, this, *scratch) != 0)
534 goto bad;
535
536 cpc_buf_add(cpc, *data1, *data1, *scratch);
537
538 if (cpc_unbind(cpc, this) != 0)
539 (void) fprintf(stderr, gettext("%s: error "
540 "unbinding on cpu %d - %s\n"),
541 opts->pgmname, state->cpuid,
542 strerror(errno));
543
544 this = cpc_setgrp_nextset(sgrp);
545
546 print_sample(state->cpuid, *scratch, nreqs, name, 0);
547
548 /*
549 * If periodic behavior was requested, rest here.
550 */
551 if (opts->doperiod && opts->mseconds_rest > 0 &&
552 (sample_cnt % opts->nsets) == 0) {
553 /*
554 * Stop the soaker while the tool rests.
555 */
556 if (opts->dosoaker) {
557 (void) mutex_lock(&state->soak_lock);
558 if (state->soak_state == SOAK_RUN)
559 state->soak_state = SOAK_PAUSE;
560 (void) mutex_unlock(&state->soak_lock);
561 }
562
563 htnow = gethrtime();
564 ht += restdelta;
565 ts.tv_sec = (time_t)((ht - htnow) /
566 NSECS_PER_SEC);
567 ts.tv_nsec = (suseconds_t)((ht - htnow) %
568 NSECS_PER_SEC);
569
570 (void) nanosleep(&ts, NULL);
571 }
572
573 /*
574 * Start or stop the soaker if needed.
575 */
576 if (opts->dosoaker) {
577 (void) mutex_lock(&state->soak_lock);
578 if (cpc_setgrp_sysonly(sgrp) &&
579 state->soak_state == SOAK_PAUSE) {
580 /*
581 * Soaker is paused but the next set is
582 * sysonly: start the soaker.
583 */
584 state->soak_state = SOAK_RUN;
585 (void) cond_signal(&state->soak_cv);
586 } else if (cpc_setgrp_sysonly(sgrp) == 0 &&
587 state->soak_state == SOAK_RUN)
588 /*
589 * Soaker is running but the next set
590 * counts user events: stop the soaker.
591 */
592 state->soak_state = SOAK_PAUSE;
593 (void) mutex_unlock(&state->soak_lock);
594 }
595
596 if (cpc_bind_cpu(cpc, state->cpuid, this, 0) != 0)
597 goto bad;
598 }
599 }
600
601 if (cpc_unbind(cpc, this) != 0)
602 (void) fprintf(stderr, gettext("%s: error unbinding on"
603 " cpu %d - %s\n"), opts->pgmname,
604 state->cpuid, strerror(errno));
605
606 /*
607 * We're done, so stop the soaker if needed.
608 */
609 if (opts->dosoaker) {
610 (void) mutex_lock(&state->soak_lock);
611 if (state->soak_state == SOAK_RUN)
612 state->soak_state = SOAK_PAUSE;
613 (void) mutex_unlock(&state->soak_lock);
614 }
615
616 return (NULL);
617 bad:
618 state->status = 3;
619 errstr = strerror(errno);
620 (void) fprintf(stderr, gettext("%s: cpu%d - %s\n"),
621 opts->pgmname, state->cpuid, errstr);
622 return (NULL);
623 }
624
625 static int
cpustat(void)626 cpustat(void)
627 {
628 cpc_setgrp_t *accum;
629 cpc_set_t *start;
630 int c, i, retval;
631 int lwps = 0;
632 psetid_t mypset, cpupset;
633 char *errstr;
634 cpc_buf_t **data1, **data2, **scratch;
635 int nreqs;
636 kstat_ctl_t *kc;
637
638 ncpus = (int)sysconf(_SC_NPROCESSORS_CONF);
639 if ((gstate = calloc(ncpus, sizeof (*gstate))) == NULL) {
640 (void) fprintf(stderr, gettext(
641 "%s: out of heap\n"), opts->pgmname);
642 return (1);
643 }
644
645 max_chip_id = sysconf(_SC_CPUID_MAX);
646 if ((chip_designees = malloc(max_chip_id * sizeof (int))) == NULL) {
647 (void) fprintf(stderr, gettext(
648 "%s: out of heap\n"), opts->pgmname);
649 return (1);
650 }
651 for (i = 0; i < max_chip_id; i++)
652 chip_designees[i] = -1;
653
654 if (smt) {
655 if ((kc = kstat_open()) == NULL) {
656 (void) fprintf(stderr, gettext(
657 "%s: kstat_open() failed: %s\n"), opts->pgmname,
658 strerror(errno));
659 return (1);
660 }
661 }
662
663 if (opts->dosoaker)
664 if (priocntl(0, 0, PC_GETCID, &fxinfo) == -1) {
665 (void) fprintf(stderr, gettext(
666 "%s: couldn't get FX scheduler class: %s\n"),
667 opts->pgmname, strerror(errno));
668 return (1);
669 }
670
671 /*
672 * Only include processors that are participating in the system
673 */
674 for (c = 0, i = 0; i < ncpus; c++) {
675 switch (p_online(c, P_STATUS)) {
676 case P_ONLINE:
677 case P_NOINTR:
678 if (smt) {
679
680 gstate[i].chip_id = get_chipid(kc, c);
681 if (gstate[i].chip_id != -1 &&
682 chip_designees[gstate[i].chip_id] == -1)
683 chip_designees[gstate[i].chip_id] = c;
684 }
685
686 gstate[i++].cpuid = c;
687 break;
688 case P_OFFLINE:
689 case P_POWEROFF:
690 case P_FAULTED:
691 case P_SPARE:
692 case P_DISABLED:
693 gstate[i++].cpuid = -1;
694 break;
695 default:
696 gstate[i++].cpuid = -1;
697 (void) fprintf(stderr,
698 gettext("%s: cpu%d in unknown state\n"),
699 opts->pgmname, c);
700 break;
701 case -1:
702 break;
703 }
704 }
705
706 /*
707 * Examine the processor sets; if we're in one, only attempt
708 * to report on the set we're in.
709 */
710 if (pset_bind(PS_QUERY, P_PID, P_MYID, &mypset) == -1) {
711 errstr = strerror(errno);
712 (void) fprintf(stderr, gettext("%s: pset_bind - %s\n"),
713 opts->pgmname, errstr);
714 } else {
715 for (i = 0; i < ncpus; i++) {
716 struct tstate *this = &gstate[i];
717
718 if (this->cpuid == -1)
719 continue;
720
721 if (pset_assign(PS_QUERY,
722 this->cpuid, &cpupset) == -1) {
723 errstr = strerror(errno);
724 (void) fprintf(stderr,
725 gettext("%s: pset_assign - %s\n"),
726 opts->pgmname, errstr);
727 continue;
728 }
729
730 if (mypset != cpupset)
731 this->cpuid = -1;
732 }
733 }
734
735 if (opts->dotitle)
736 print_title(opts->master);
737 zerotime();
738
739 for (i = 0; i < ncpus; i++) {
740 struct tstate *this = &gstate[i];
741
742 if (this->cpuid == -1)
743 continue;
744 this->sgrp = cpc_setgrp_clone(opts->master);
745 if (this->sgrp == NULL) {
746 this->cpuid = -1;
747 continue;
748 }
749 if (thr_create(NULL, 0, gtick, this,
750 THR_BOUND|THR_NEW_LWP, &this->tid) == 0)
751 lwps++;
752 else {
753 (void) fprintf(stderr,
754 gettext("%s: cannot create thread for cpu%d\n"),
755 opts->pgmname, this->cpuid);
756 this->status = 4;
757 }
758 }
759
760 if (lwps != 0)
761 for (i = 0; i < ncpus; i++)
762 (void) thr_join(gstate[i].tid, NULL, NULL);
763
764 if ((accum = cpc_setgrp_clone(opts->master)) == NULL) {
765 (void) fprintf(stderr, gettext("%s: out of heap\n"),
766 opts->pgmname);
767 return (1);
768 }
769
770 retval = 0;
771 for (i = 0; i < ncpus; i++) {
772 struct tstate *this = &gstate[i];
773
774 if (this->cpuid == -1)
775 continue;
776 cpc_setgrp_accum(accum, this->sgrp);
777 cpc_setgrp_free(this->sgrp);
778 this->sgrp = NULL;
779 if (this->status != 0)
780 retval = 1;
781 }
782
783 cpc_setgrp_reset(accum);
784 start = cpc_setgrp_getset(accum);
785 do {
786 nreqs = cpc_setgrp_getbufs(accum, &data1, &data2, &scratch);
787 print_total(lwps, *data1, nreqs, cpc_setgrp_getname(accum));
788 } while (cpc_setgrp_nextset(accum) != start);
789
790 cpc_setgrp_free(accum);
791 accum = NULL;
792
793 free(gstate);
794 return (retval);
795 }
796
797 static int
get_chipid(kstat_ctl_t * kc,processorid_t cpuid)798 get_chipid(kstat_ctl_t *kc, processorid_t cpuid)
799 {
800 kstat_t *ksp;
801 kstat_named_t *k;
802
803 if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL)
804 return (-1);
805
806 if (kstat_read(kc, ksp, NULL) == -1) {
807 (void) fprintf(stderr,
808 gettext("%s: kstat_read() failed for cpu %d: %s\n"),
809 opts->pgmname, cpuid, strerror(errno));
810 return (-1);
811 }
812
813 if ((k = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")) == NULL) {
814 (void) fprintf(stderr,
815 gettext("%s: chip_id not found for cpu %d: %s\n"),
816 opts->pgmname, cpuid, strerror(errno));
817 return (-1);
818 }
819
820 return (k->value.i32);
821 }
822
823 static void *
soaker(void * arg)824 soaker(void *arg)
825 {
826 struct tstate *state = arg;
827 pcparms_t pcparms;
828 fxparms_t *fx = (fxparms_t *)pcparms.pc_clparms;
829
830 if (processor_bind(P_LWPID, P_MYID, state->cpuid, NULL) != 0)
831 (void) fprintf(stderr, gettext("%s: couldn't bind soaker "
832 "thread to cpu%d: %s\n"), opts->pgmname, state->cpuid,
833 strerror(errno));
834
835 /*
836 * Put the soaker thread in the fixed priority (FX) class so it runs
837 * at the lowest possible global priority.
838 */
839 pcparms.pc_cid = fxinfo.pc_cid;
840 fx->fx_upri = 0;
841 fx->fx_uprilim = 0;
842 fx->fx_tqsecs = fx->fx_tqnsecs = FX_TQDEF;
843
844 if (priocntl(P_LWPID, P_MYID, PC_SETPARMS, &pcparms) != 0)
845 (void) fprintf(stderr, gettext("%s: couldn't put soaker "
846 "thread in FX sched class: %s\n"), opts->pgmname,
847 strerror(errno));
848
849 /*
850 * Let the parent thread know we're ready to roll.
851 */
852 (void) mutex_lock(&state->soak_lock);
853 state->soak_state = SOAK_RUN;
854 (void) cond_signal(&state->soak_cv);
855 (void) mutex_unlock(&state->soak_lock);
856
857 for (;;) {
858 spin:
859 (void) mutex_lock(&state->soak_lock);
860 if (state->soak_state == SOAK_RUN) {
861 (void) mutex_unlock(&state->soak_lock);
862 goto spin;
863 }
864
865 while (state->soak_state == SOAK_PAUSE)
866 (void) cond_wait(&state->soak_cv,
867 &state->soak_lock);
868 (void) mutex_unlock(&state->soak_lock);
869 }
870
871 /*NOTREACHED*/
872 return (NULL);
873 }
874