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.
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.
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]
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
34 * Takes a string and converts it to a cpc_set_t.
36 * While processing the string using getsubopt(), we will use an array of
37 * requests to hold the data, and a proprietary representation of attributes
38 * which allow us to avoid a realloc()/bcopy() dance every time we come across
41 * Not until after the string has been processed in its entirety do we
42 * allocate and specify a request set properly.
46 * Leave enough room in token strings for picn, nousern, or sysn where n is
51 typedef struct __tmp_attr
{
54 struct __tmp_attr
*next
;
57 typedef struct __tok_info
{
62 typedef struct __request_t
{
63 char cr_event
[CPC_MAX_EVENT_LEN
];
65 uint_t cr_nattrs
; /* # CPU-specific attrs */
68 static void strtoset_cleanup(void);
69 static void smt_special(int picnum
);
70 static void *emalloc(size_t n
);
73 * Clients of cpc_strtoset may set this to specify an error handler during
76 cpc_errhndlr_t
*strtoset_errfn
= NULL
;
78 static request_t
*reqs
;
82 static tmp_attr_t
**attrs
;
85 static tok_info_t
*tok_info
;
86 static int (*(*tok_funcs
))(int, char *);
87 static char **attrlist
; /* array of ptrs to toks in attrlistp */
93 strtoset_err(const char *fmt
, ...)
97 if (strtoset_errfn
== NULL
)
101 (*strtoset_errfn
)("cpc_strtoset", -1, fmt
, ap
);
107 event_walker(void *arg
, uint_t picno
, const char *event
)
109 if (strncmp(arg
, event
, CPC_MAX_EVENT_LEN
) == 0)
114 event_valid(int picnum
, char *event
)
121 cpc_walk_events_pic(cpc
, picnum
, event
, event_walker
);
126 cpc_walk_generic_events_pic(cpc
, picnum
, event
, event_walker
);
132 * Before assuming this is an invalid event, see if we have been given
134 * Check the second argument of strtol() to ensure invalid events
135 * beginning with number do not go through.
139 (void) strtol(event
, &end_event
, 0);
140 if ((errno
== 0) && (*end_event
== '\0')) {
142 * Success - this is a valid raw code in hex, decimal, or octal.
153 * An unknown token was encountered; check here if it is an implicit event
154 * name. We allow users to omit the "picn=" portion of the event spec, and
155 * assign such events to available pics in order they are returned from
156 * getsubopt(3C). We start our search for an available pic _after_ the highest
157 * picnum to be assigned. This ensures that the event spec can never be out of
158 * order; i.e. if the event string is "eventa,eventb" we must ensure that the
159 * picnum counting eventa is less than the picnum counting eventb.
162 find_event(char *event
)
167 * Event names cannot have '=' in them. If present here, it means we
168 * have encountered an unknown token (foo=bar, for example).
170 if (strchr(event
, '=') != NULL
)
174 * Find the first unavailable pic, after which we must start our search.
176 for (i
= ncounters
- 1; i
>= 0; i
--) {
177 if (reqs
[i
].cr_event
[0] != '\0')
181 * If the last counter has been assigned, we cannot place this event.
183 if (i
== ncounters
- 1)
187 * If none of the counters have been assigned yet, i is -1 and we will
188 * begin our search at 0. Else we begin our search at the counter after
189 * the last one currently assigned.
193 for (; i
< ncounters
; i
++) {
194 if (event_valid(i
, event
) == 0)
198 (void) strncpy(reqs
[i
].cr_event
, event
, CPC_MAX_EVENT_LEN
);
206 pic(int tok
, char *val
)
208 int picnum
= tok_info
[tok
].picnum
;
210 * Make sure the each pic only appears in the spec once.
212 if (reqs
[picnum
].cr_event
[0] != '\0') {
213 strtoset_err(gettext("repeated 'pic%d' token\n"), picnum
);
217 if (val
== NULL
|| val
[0] == '\0') {
218 strtoset_err(gettext("missing 'pic%d' value\n"), picnum
);
222 if (event_valid(picnum
, val
) == 0) {
223 strtoset_err(gettext("pic%d cannot measure event '%s' on this "
224 "cpu\n"), picnum
, val
);
229 (void) strncpy(reqs
[picnum
].cr_event
, val
, CPC_MAX_EVENT_LEN
);
234 * We explicitly ignore any value provided for these tokens, as their
235 * mere presence signals us to turn on or off the relevant flags.
239 flag(int tok
, char *val
)
242 int picnum
= tok_info
[tok
].picnum
;
245 * If picnum is -1, this flag should be applied to all reqs.
247 for (i
= (picnum
== -1) ? 0 : picnum
; i
< ncounters
; i
++) {
248 if (strcmp(tok_info
[tok
].name
, "nouser") == 0)
249 reqs
[i
].cr_flags
&= ~CPC_COUNT_USER
;
250 else if (strcmp(tok_info
[tok
].name
, "sys") == 0)
251 reqs
[i
].cr_flags
|= CPC_COUNT_SYSTEM
;
263 doattr(int tok
, char *val
)
266 int picnum
= tok_info
[tok
].picnum
;
271 * If picnum is -1, this attribute should be applied to all reqs.
273 for (i
= (picnum
== -1) ? 0 : picnum
; i
< ncounters
; i
++) {
274 tmp
= (tmp_attr_t
*)emalloc(sizeof (tmp_attr_t
));
275 tmp
->name
= tok_info
[tok
].name
;
277 tmp
->val
= strtoll(val
, &endptr
, 0);
279 strtoset_err(gettext("invalid value '%s' for "
280 "attribute '%s'\n"), val
, tmp
->name
);
286 * No value was provided for this attribute,
287 * so specify a default value of 1.
291 tmp
->next
= attrs
[i
];
304 attr_count_walker(void *arg
, const char *attr
)
307 * We don't allow picnum to be specified by the user.
309 if (strncmp(attr
, "picnum", 7) == 0)
315 cpc_count_attrs(cpc_t
*cpc
)
319 cpc_walk_attrs(cpc
, &nattrs
, attr_count_walker
);
325 attr_walker(void *arg
, const char *attr
)
329 if (strncmp(attr
, "picnum", 7) == 0)
332 if ((attrlist
[(*i
)++] = strdup(attr
)) == NULL
) {
333 strtoset_err(gettext("no memory available\n"));
339 cpc_strtoset(cpc_t
*cpcin
, const char *spec
, int smt
)
342 cpc_attr_t
*req_attrs
;
354 ncounters
= cpc_npic(cpc
);
356 reqs
= (request_t
*)emalloc(ncounters
* sizeof (request_t
));
358 attrs
= (tmp_attr_t
**)emalloc(ncounters
* sizeof (tmp_attr_t
*));
360 for (i
= 0; i
< ncounters
; i
++) {
361 reqs
[i
].cr_event
[0] = '\0';
362 reqs
[i
].cr_flags
= CPC_COUNT_USER
;
364 * Each pic will have at least one attribute: the physical pic
365 * assignment via the "picnum" attribute. Set that up here for
368 reqs
[i
].cr_nattrs
= 1;
369 attrs
[i
] = emalloc(sizeof (tmp_attr_t
));
370 attrs
[i
]->name
= "picnum";
372 attrs
[i
]->next
= NULL
;
376 * Build up a list of acceptable tokens.
378 * Permitted tokens are
387 * Where n is a counter number, and attr is any attribute supported by
388 * the current processor.
390 * If a token appears without a counter number, it applies to all
391 * counters in the request set.
393 * The number of tokens is:
396 * generic flags: 2 * ncounters (nouser, sys)
397 * attrs: nattrs * ncounters
398 * attrs with no picnum: nattrs
399 * generic flags with no picnum: 2 (nouser, sys)
400 * NULL token to signify end of list to getsubopt(3C).
402 * Matching each token's index in the token table is a function which
403 * process that token; these are in tok_funcs.
407 * Count the number of valid attributes.
408 * Set up the attrlist array to point to the attributes in attrlistp.
410 nattrs
= cpc_count_attrs(cpc
);
411 attrlist
= (char **)emalloc(nattrs
* sizeof (char *));
414 cpc_walk_attrs(cpc
, &i
, attr_walker
);
416 ntoks
= ncounters
+ (2 * ncounters
) + (nattrs
* ncounters
) + nattrs
+ 3;
417 toks
= (char **)emalloc(ntoks
* sizeof (char *));
418 tok_info
= (tok_info_t
*)emalloc(ntoks
* sizeof (tok_info_t
));
420 tok_funcs
= (int (**)(int, char *))emalloc(ntoks
*
421 sizeof (int (*)(char *)));
423 for (i
= 0; i
< ntoks
; i
++) {
429 for (i
= 0; i
< ncounters
; i
++) {
430 toks
[x
] = (char *)emalloc(TOK_SIZE
);
431 (void) snprintf(toks
[x
], TOK_SIZE
, "pic%d", i
);
432 tok_info
[x
].name
= "pic";
433 tok_info
[i
].picnum
= i
;
438 for (i
= 0; i
< ncounters
; i
++) {
439 toks
[x
] = (char *)emalloc(TOK_SIZE
);
440 (void) snprintf(toks
[x
], TOK_SIZE
, "nouser%d", i
);
441 tok_info
[x
].name
= "nouser";
442 tok_info
[x
].picnum
= i
;
447 for (i
= 0; i
< ncounters
; i
++) {
448 toks
[x
] = (char *)emalloc(TOK_SIZE
);
449 (void) snprintf(toks
[x
], TOK_SIZE
, "sys%d", i
);
450 tok_info
[x
].name
= "sys";
451 tok_info
[x
].picnum
= i
;
455 for (j
= 0; j
< nattrs
; j
++) {
456 toklen
= strlen(attrlist
[j
]) + 3;
457 for (i
= 0; i
< ncounters
; i
++) {
458 toks
[x
] = (char *)emalloc(toklen
);
459 (void) snprintf(toks
[x
], toklen
, "%s%d", attrlist
[j
],
461 tok_info
[x
].name
= attrlist
[j
];
462 tok_info
[x
].picnum
= i
;
463 tok_funcs
[x
] = doattr
;
468 * Now create a token for this attribute with no picnum; if used
469 * it will be applied to all reqs.
471 toks
[x
] = (char *)emalloc(toklen
);
472 (void) snprintf(toks
[x
], toklen
, "%s", attrlist
[j
]);
473 tok_info
[x
].name
= attrlist
[j
];
474 tok_info
[x
].picnum
= -1;
475 tok_funcs
[x
] = doattr
;
480 tok_info
[x
].name
= "nouser";
481 tok_info
[x
].picnum
= -1;
486 tok_info
[x
].name
= "sys";
487 tok_info
[x
].picnum
= -1;
493 opts
= strdupa(spec
);
494 while (*opts
!= '\0') {
495 int idx
= getsubopt(&opts
, toks
, &val
);
498 if (find_event(val
) == 0) {
499 strtoset_err(gettext("bad token '%s'\n"), val
);
505 if (tok_funcs
[idx
](idx
, val
) == -1)
510 * The string has been processed. Now count how many PICs were used,
511 * create a request set, and specify each request properly.
514 if ((set
= cpc_set_create(cpc
)) == NULL
) {
515 strtoset_err(gettext("no memory available\n"));
519 for (i
= 0; i
< ncounters
; i
++) {
520 if (reqs
[i
].cr_event
[0] == '\0')
524 * If the caller wishes to measure events on the physical CPU,
525 * we need to add SMT attributes to each request.
530 req_attrs
= (cpc_attr_t
*)emalloc(reqs
[i
].cr_nattrs
*
531 sizeof (cpc_attr_t
));
534 for (tmp
= attrs
[i
]; tmp
!= NULL
; tmp
= tmp
->next
) {
535 req_attrs
[j
].ca_name
= tmp
->name
;
536 req_attrs
[j
].ca_val
= tmp
->val
;
540 if (cpc_set_add_request(cpc
, set
, reqs
[i
].cr_event
, 0,
541 reqs
[i
].cr_flags
, reqs
[i
].cr_nattrs
, req_attrs
) == -1) {
543 (void) cpc_set_destroy(cpc
, set
);
545 gettext("cpc_set_add_request() failed: %s\n"),
564 strtoset_cleanup(void)
569 for (i
= 0; i
< nattrs
; i
++)
573 for (i
= 0; i
< ncounters
; i
++) {
574 for (tmp
= attrs
[i
]; tmp
!= NULL
; tmp
= p
) {
581 for (i
= 0; i
< ntoks
- 3; i
++)
583 * We free all but the last three tokens: "nouser", "sys", NULL
593 * The following is called to modify requests so that they count events on
594 * behalf of a physical processor, instead of a logical processor. It duplicates
595 * the request flags for the sibling processor (i.e. if the request counts user
596 * events, add an attribute to count user events on the sibling processor also).
599 smt_special(int picnum
)
603 if (reqs
[picnum
].cr_flags
& CPC_COUNT_USER
) {
604 attr
= (tmp_attr_t
*)emalloc(sizeof (tmp_attr_t
));
605 attr
->name
= "count_sibling_usr";
607 attr
->next
= attrs
[picnum
];
608 attrs
[picnum
] = attr
;
609 reqs
[picnum
].cr_nattrs
++;
612 if (reqs
[picnum
].cr_flags
& CPC_COUNT_SYSTEM
) {
613 attr
= (tmp_attr_t
*)emalloc(sizeof (tmp_attr_t
));
614 attr
->name
= "count_sibling_sys";
616 attr
->next
= attrs
[picnum
];
617 attrs
[picnum
] = attr
;
618 reqs
[picnum
].cr_nattrs
++;
623 * If we ever fail to get memory, we print an error message and exit.
631 strtoset_err(gettext("no memory available\n"));