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