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) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Routines to capture processor-dependencies in event specification.
30 #include <sys/types.h>
40 #include "libcpc_impl.h"
43 * Event specifications for Pentium performance counters are based
44 * on the content of a getsubopt-like string.
45 * The string should contain something that looks like this:
47 * pic0=<eventspec>,pic1=<eventspec>
48 * [,cmask0=<maskspec>][,cmask1=<maskspec>]
49 * [,umask0=<maskspec>][,umask1=<maskspec>]
50 * [,inv[0|1]][,noedge[0|1]]
51 * [,sys[0|1]][,nouser[0|1]]
54 * pic0=data_mem_refs,pic1=l2_ld,sys
56 * pic0=l2_ld,pic1=bus_drdy_clocks,umask1=0x20,nouser1
58 * By default, user event counting is enabled, system event counting
61 * Note that Pentium and Pentium Pro have different event specifications.
63 * The two events must be named. The names can be ascii or
64 * a decimal, octal or hexadecimal number as parsed by strtol(3C).
66 * The routine counts the number of errors encountered while parsing
67 * the string, if no errors are encountered, the event handle is
72 cpc_getusage(int cpuver
)
75 case CPC_PENTIUM_PRO_MMX
:
77 return ("pic0=<event0>,pic1=<event1> "
84 "[,cmask[0|1]=<maskspec>] "
85 "[,umask[0|1]=<maskspec>] ");
88 return ("pic0=<event0>,pic1=<event1> "
100 int (*kv_action
)(const char *,
101 const struct keyval
*, int, char *, uint32_t *);
109 eightbits(const char *fn
,
110 const struct keyval
*kv
, int cpuver
, char *value
, uint32_t *bits
)
116 __cpc_error(fn
, gettext("missing '%s' value\n"),
120 l
= strtol(value
, &eptr
, 0);
121 if (value
== eptr
|| l
< 0 || l
> UINT8_MAX
) {
122 __cpc_error(fn
, gettext("bad '%s' value\n"), kv
->kv_token
);
125 bits
[kv
->kv_regno
] |= ((uint8_t)l
& kv
->kv_mask
) << kv
->kv_shift
;
130 picbits(const char *fn
,
131 const struct keyval
*kv
, int cpuver
, char *value
, uint32_t *bits
)
136 regno
= strcmp(kv
->kv_token
, "pic0") == 0 ? 0 : 1;
139 __cpc_error(fn
, gettext("missing '%s' value\n"),
144 if (__cpc_name_to_reg(cpuver
, regno
, value
, &val8
) != 0) {
146 case CPC_PENTIUM_PRO_MMX
:
147 case CPC_PENTIUM_PRO
:
148 assert(kv
->kv_regno
== regno
);
149 __cpc_error(fn
, gettext(
150 "PerfCtr%d cannot measure '%s' on this cpu\n"),
153 case CPC_PENTIUM_MMX
:
155 assert(kv
->kv_regno
== 0);
156 __cpc_error(fn
, gettext(
157 "CTR%d cannot measure '%s' on this cpu\n"),
163 bits
[kv
->kv_regno
] |= (val8
& kv
->kv_mask
) << kv
->kv_shift
;
169 bitclr(const char *fn
,
170 const struct keyval
*kv
, int cpuver
, char *value
, uint32_t *bits
)
173 __cpc_error(fn
, gettext("bad arg to '%s'\n"), kv
->kv_token
);
176 bits
[kv
->kv_regno
] &= ~(kv
->kv_mask
<< kv
->kv_shift
);
182 bitset(const char *fn
,
183 const struct keyval
*kv
, int cpuver
, char *value
, uint32_t *bits
)
186 __cpc_error(fn
, gettext("bad arg to '%s'\n"), kv
->kv_token
);
189 bits
[kv
->kv_regno
] |= (kv
->kv_mask
<< kv
->kv_shift
);
194 nextpair(const char *fn
,
195 const struct keyval
*kv
, int cpuver
, char *value
, uint32_t *bits
)
200 __cpc_error(fn
, gettext("bad arg to '%s'\n"), kv
->kv_token
);
204 if ((rv
= kv
->kv_action(fn
, kv
, cpuver
, value
, bits
)) != 0)
207 return (kv
->kv_action(fn
, kv
, cpuver
, value
, bits
));
211 * This token table must match the keyval tables below.
214 static char * const tokens
[] = {
216 "pic0", /* takes a valid event name */
218 "pic1", /* takes a valid event name */
220 "nouser", /* disables user counts */
226 "sys", /* enables system counts */
232 "noedge", /* disable edge detect */
238 "pc", /* sets pin control high */
245 * These additional keywords are for Pentium Pro / Pentium II machines.
248 "int", /* enable interrupt on counter overflow */
254 "inv", /* invert cmask comparison */
260 "umask0", /* PerfCtr0 unit mask */
262 "umask1", /* PerfCtr1 unit mask */
264 "cmask0", /* PerfCtr0 counter mask */
266 "cmask1", /* PerfCtr1 counter mask */
270 static const struct keyval p6_keyvals
[] = {
271 { "pic0", picbits
, 0,
272 CPC_P6_PES_PIC0_MASK
, 0 },
273 { "pic1", picbits
, 1,
274 CPC_P6_PES_PIC1_MASK
, 0 },
275 { "nouser", nextpair
},
276 { "nouser0", bitclr
, 0,
277 UINT32_C(1), CPC_P6_PES_USR
},
278 { "nouser1", bitclr
, 1,
279 UINT32_C(1), CPC_P6_PES_USR
},
282 UINT32_C(1), CPC_P6_PES_OS
},
284 UINT32_C(1), CPC_P6_PES_OS
},
285 { "noedge", nextpair
},
286 { "noedge0", bitclr
, 0,
287 UINT32_C(1), CPC_P6_PES_E
},
288 { "noedge1", bitclr
, 1,
289 UINT32_C(1), CPC_P6_PES_E
},
292 UINT32_C(1), CPC_P6_PES_PC
},
294 UINT32_C(1), CPC_P6_PES_PC
},
297 UINT32_C(1), CPC_P6_PES_INT
},
299 UINT32_C(1), CPC_P6_PES_INT
},
302 UINT32_C(1), CPC_P6_PES_INV
},
304 UINT32_C(1), CPC_P6_PES_INV
},
305 { "umask0", eightbits
, 0,
306 CPC_P6_PES_UMASK_MASK
, CPC_P6_PES_UMASK_SHIFT
},
307 { "umask1", eightbits
, 1,
308 CPC_P6_PES_UMASK_MASK
, CPC_P6_PES_UMASK_SHIFT
},
309 { "cmask0", eightbits
, 0,
310 CPC_P6_PES_CMASK_MASK
, CPC_P6_PES_CMASK_SHIFT
},
311 { "cmask1", eightbits
, 1,
312 CPC_P6_PES_CMASK_MASK
, CPC_P6_PES_CMASK_SHIFT
},
316 * Note that this table -must- be an identically indexed
317 * subset of p6_keyvals.
319 static const struct keyval p5_keyvals
[] = {
320 { "pic0", picbits
, 0,
321 CPC_P5_CESR_ES0_MASK
, CPC_P5_CESR_ES0_SHIFT
},
322 { "pic1", picbits
, 0,
323 CPC_P5_CESR_ES1_MASK
, CPC_P5_CESR_ES1_SHIFT
},
324 { "nouser", nextpair
},
325 { "nouser0", bitclr
, 0,
326 UINT32_C(1), CPC_P5_CESR_USR0
},
327 { "nouser1", bitclr
, 0,
328 UINT32_C(1), CPC_P5_CESR_USR1
},
331 UINT32_C(1), CPC_P5_CESR_OS0
},
333 UINT32_C(1), CPC_P5_CESR_OS1
},
334 { "noedge", nextpair
},
335 { "noedge0", bitset
, 0,
336 UINT32_C(1), CPC_P5_CESR_CLK0
},
337 { "noedge1", bitset
, 0,
338 UINT32_C(1), CPC_P5_CESR_CLK1
},
341 UINT32_C(1), CPC_P5_CESR_PC0
},
343 UINT32_C(1), CPC_P5_CESR_PC1
},
347 #pragma init(__tablecheck)
352 uint_t ntokens
= sizeof (tokens
) / sizeof (tokens
[0]) - 1;
353 uint_t p6_nkeys
= sizeof (p6_keyvals
) / sizeof (p6_keyvals
[0]);
354 uint_t p5_nkeys
= sizeof (p5_keyvals
) / sizeof (p5_keyvals
[0]);
357 assert(ntokens
== p6_nkeys
);
358 for (n
= 0; n
< ntokens
; n
++)
359 assert(strcmp(tokens
[n
], p6_keyvals
[n
].kv_token
) == 0);
360 assert(p6_nkeys
>= p5_nkeys
);
361 for (n
= 0; n
< p5_nkeys
; n
++)
362 assert(strcmp(tokens
[n
], p5_keyvals
[n
].kv_token
) == 0);
368 cpc_strtoevent(int cpuver
, const char *spec
, cpc_event_t
*event
)
370 static const char fn
[] = "strtoevent";
376 const struct keyval
*keyvals
;
382 bzero(event
, sizeof (*event
));
383 switch (event
->ce_cpuver
= cpuver
) {
384 case CPC_PENTIUM_PRO_MMX
:
385 case CPC_PENTIUM_PRO
:
386 keyvals
= p6_keyvals
;
387 ntokens
= sizeof (p6_keyvals
) / sizeof (p6_keyvals
[0]);
388 bits
= &event
->ce_pes
[0];
390 (1u << CPC_P6_PES_USR
) | (1u << CPC_P6_PES_E
);
392 case CPC_PENTIUM_MMX
:
394 keyvals
= p5_keyvals
;
395 ntokens
= sizeof (p5_keyvals
) / sizeof (p5_keyvals
[0]);
396 bits
= &event
->ce_cesr
;
398 (1u << CPC_P5_CESR_USR0
) | (1u << CPC_P5_CESR_USR1
);
404 pic
[0] = pic
[1] = NULL
;
406 opts
= strdupa(spec
);
407 while (*opts
!= '\0') {
408 const struct keyval
*kv
;
409 int idx
= getsubopt(&opts
, tokens
, &value
);
411 if (idx
>= 0 && idx
< ntokens
) {
413 if (kv
->kv_action(fn
, kv
, cpuver
, value
, bits
) != 0) {
419 if (pic
[0] != NULL
) {
421 "repeated '%s' token\n",
427 } else if (idx
== D_pic1
) {
428 if (pic
[1] != NULL
) {
430 "repeated '%s' token\n",
437 } else if (idx
== -1) {
439 * The token given wasn't recognized.
440 * See if it was an implicit pic specification..
442 if (pic
[0] == NULL
) {
443 kv
= &keyvals
[D_pic0
];
444 if (kv
->kv_action(fn
,
445 kv
, cpuver
, value
, bits
) != 0) {
450 } else if (pic
[1] == NULL
) {
451 kv
= &keyvals
[D_pic1
];
452 if (kv
->kv_action(fn
,
453 kv
, cpuver
, value
, bits
) != 0) {
460 gettext("bad token '%s'\n"), value
);
466 idx
< sizeof (tokens
) / sizeof (tokens
[0]))
468 gettext("bad token '%s'\n"), tokens
[idx
]);
470 __cpc_error(fn
, gettext("bad token\n"));
476 if (pic
[0] == NULL
|| pic
[1] == NULL
) {
477 __cpc_error(fn
, gettext("two events must be specified\n"));
485 * Return a printable description of the control registers.
487 * This routine should always succeed (notwithstanding heap problems),
488 * but may not be able to correctly decode the registers, if, for
489 * example, a new processor is under test.
491 * The caller is responsible for free(3c)ing the string returned.
495 flagstostr(char *buf
, int flag0
, int flag1
, int defvalue
, char *tok
)
498 if (flag0
!= defvalue
) {
499 if (flag1
!= defvalue
)
500 (void) sprintf(buf
, ",%s", tok
);
502 (void) sprintf(buf
, ",%s0", tok
);
504 if (flag1
!= defvalue
)
505 (void) sprintf(buf
, ",%s1", tok
);
510 masktostr(char *buf
, uint8_t bits
, char *tok
)
514 (void) sprintf(buf
, ",%s=0x%x", tok
, bits
);
519 val8tostr(uint8_t bits
)
521 char buf
[2 + 2 + 1]; /* 0x %2x \0 */
522 (void) snprintf(buf
, sizeof (buf
), "0x%x", bits
);
523 return (strdup(buf
));
527 regtostr(int cpuver
, int regno
, uint8_t bits
)
531 if ((sname
= __cpc_reg_to_name(cpuver
, regno
, bits
)) != NULL
)
532 return (strdup(sname
));
533 return (val8tostr(bits
));
537 uint8_t cmask
, umask
, evsel
;
538 int usr
, sys
, edge
, inv
, irupt
, pc
;
543 unmake_pes(uint32_t pes
, int cpuver
, struct xpes
*xpes
)
545 xpes
->cmask
= (uint8_t)(pes
>> CPC_P6_PES_CMASK_SHIFT
);
546 xpes
->pc
= (pes
>> CPC_P6_PES_PC
) & 1u;
547 xpes
->inv
= (pes
>> CPC_P6_PES_INV
) & 1u;
548 xpes
->irupt
= (pes
>> CPC_P6_PES_INT
) & 1u;
549 xpes
->edge
= (pes
>> CPC_P6_PES_E
) & 1u;
550 xpes
->sys
= (pes
>> CPC_P6_PES_OS
) & 1u;
551 xpes
->usr
= (pes
>> CPC_P6_PES_USR
) & 1u;
552 xpes
->umask
= (uint8_t)(pes
>> CPC_P6_PES_UMASK_SHIFT
);
553 xpes
->evsel
= (uint8_t)pes
;
558 int usr
[2], sys
[2], clk
[2], pc
[2];
563 unmake_cesr(uint32_t cesr
, int cpuver
, struct xcesr
*xcesr
)
565 xcesr
->evsel
[0] = (cesr
>> CPC_P5_CESR_ES0_SHIFT
) &
566 CPC_P5_CESR_ES0_MASK
;
567 xcesr
->evsel
[1] = (cesr
>> CPC_P5_CESR_ES1_SHIFT
) &
568 CPC_P5_CESR_ES1_MASK
;
569 xcesr
->usr
[0] = (cesr
>> CPC_P5_CESR_USR0
) & 1u;
570 xcesr
->usr
[1] = (cesr
>> CPC_P5_CESR_USR1
) & 1u;
571 xcesr
->sys
[0] = (cesr
>> CPC_P5_CESR_OS0
) & 1u;
572 xcesr
->sys
[1] = (cesr
>> CPC_P5_CESR_OS1
) & 1u;
573 xcesr
->clk
[0] = (cesr
>> CPC_P5_CESR_CLK0
) & 1u;
574 xcesr
->clk
[1] = (cesr
>> CPC_P5_CESR_CLK1
) & 1u;
575 xcesr
->pc
[0] = (cesr
>> CPC_P5_CESR_PC0
) & 1u;
576 xcesr
->pc
[1] = (cesr
>> CPC_P5_CESR_PC1
) & 1u;
578 * If usr and sys are both disabled, the counter is disabled.
580 if (xcesr
->usr
[0] == 0 && xcesr
->sys
[0] == 0)
582 if (xcesr
->usr
[1] == 0 && xcesr
->sys
[1] == 0)
587 cpc_eventtostr(cpc_event_t
*event
)
591 int cpuver
= event
->ce_cpuver
;
594 case CPC_PENTIUM_PRO_MMX
:
595 case CPC_PENTIUM_PRO
:
599 unmake_pes(event
->ce_pes
[0], cpuver
, &xpes
[0]);
600 if ((pic
[0] = regtostr(cpuver
, 0, xpes
[0].evsel
)) == NULL
)
603 unmake_pes(event
->ce_pes
[1], cpuver
, &xpes
[1]);
604 if ((pic
[1] = regtostr(cpuver
, 1, xpes
[1].evsel
)) == NULL
) {
608 (void) snprintf(buffer
, sizeof (buffer
), "%s=%s,%s=%s",
609 tokens
[D_pic0
], pic
[0], tokens
[D_pic1
], pic
[1]);
612 masktostr(buffer
, xpes
[0].cmask
, tokens
[D_cmask0
]);
613 masktostr(buffer
, xpes
[1].cmask
, tokens
[D_cmask1
]);
614 masktostr(buffer
, xpes
[0].umask
, tokens
[D_umask0
]);
615 masktostr(buffer
, xpes
[1].umask
, tokens
[D_umask1
]);
617 xpes
[0].usr
, xpes
[1].usr
, 1, tokens
[D_nouser
]);
619 xpes
[0].sys
, xpes
[1].sys
, 0, tokens
[D_sys
]);
621 xpes
[0].edge
, xpes
[1].edge
, 1, tokens
[D_noedge
]);
623 xpes
[0].irupt
, xpes
[1].irupt
, 0, tokens
[D_int
]);
625 xpes
[0].inv
, xpes
[1].inv
, 0, tokens
[D_inv
]);
627 xpes
[0].pc
, xpes
[1].pc
, 0, tokens
[D_pc
]);
630 case CPC_PENTIUM_MMX
:
635 unmake_cesr(event
->ce_cesr
, cpuver
, &xcesr
);
636 if ((pic
[0] = regtostr(cpuver
, 0, xcesr
.evsel
[0])) == NULL
)
638 if ((pic
[1] = regtostr(cpuver
, 1, xcesr
.evsel
[1])) == NULL
) {
642 (void) snprintf(buffer
, sizeof (buffer
), "%s=%s,%s=%s",
643 tokens
[D_pic0
], pic
[0], tokens
[D_pic1
], pic
[1]);
647 xcesr
.usr
[0], xcesr
.usr
[1], 1, tokens
[D_nouser
]);
649 xcesr
.sys
[0], xcesr
.sys
[1], 0, tokens
[D_sys
]);
651 xcesr
.clk
[0], xcesr
.clk
[1], 0, tokens
[D_noedge
]);
653 xcesr
.pc
[0], xcesr
.pc
[1], 0, tokens
[D_pc
]);
659 return (strdup(buffer
));
663 * Utility operations on events
666 cpc_event_accum(cpc_event_t
*accum
, cpc_event_t
*event
)
668 if (accum
->ce_hrt
< event
->ce_hrt
)
669 accum
->ce_hrt
= event
->ce_hrt
;
670 accum
->ce_tsc
+= event
->ce_tsc
;
671 accum
->ce_pic
[0] += event
->ce_pic
[0];
672 accum
->ce_pic
[1] += event
->ce_pic
[1];
676 cpc_event_diff(cpc_event_t
*diff
, cpc_event_t
*left
, cpc_event_t
*right
)
678 diff
->ce_hrt
= left
->ce_hrt
;
679 diff
->ce_tsc
= left
->ce_tsc
- right
->ce_tsc
;
680 diff
->ce_pic
[0] = left
->ce_pic
[0] - right
->ce_pic
[0];
681 diff
->ce_pic
[1] = left
->ce_pic
[1] - right
->ce_pic
[1];
685 * Given a cpc_event_t and cpc_bind_event() flags,
686 * translate the cpc_event_t into the cpc_set_t format.
688 * Returns NULL on failure.
691 __cpc_eventtoset(cpc_t
*cpc
, cpc_event_t
*event
, int iflags
)
694 int cpuver
= event
->ce_cpuver
;
696 int flags
[2] = { 0, 0 };
703 if ((set
= cpc_set_create(cpc
)) == NULL
) {
707 if (iflags
& CPC_BIND_EMT_OVF
)
708 flags
[0] = flags
[1] = CPC_OVF_NOTIFY_EMT
;
711 case CPC_PENTIUM_PRO_MMX
:
712 case CPC_PENTIUM_PRO
:
716 for (i
= 0; i
< 2; i
++) {
719 unmake_pes(event
->ce_pes
[i
], cpuver
, &xpes
[i
]);
720 if ((pic
[i
] = regtostr(cpuver
, i
,
721 xpes
[i
].evsel
)) == NULL
) {
722 (void) cpc_set_destroy(cpc
, set
);
725 if (xpes
[i
].usr
== 1)
726 flags
[i
] |= CPC_COUNT_USER
;
727 if (xpes
[i
].sys
== 1)
728 flags
[i
] |= CPC_COUNT_SYSTEM
;
729 if (xpes
[i
].irupt
== 1) {
742 if (xpes
[i
].edge
== 0)
745 if ((attr
= (cpc_attr_t
*)malloc(nattrs
*
746 sizeof (cpc_attr_t
))) == NULL
) {
747 (void) cpc_set_destroy(cpc
, set
);
753 * Ensure that pic[0] in the cpc_event_t is bound to
756 attr
[0].ca_name
= "picnum";
760 attr
[j
].ca_name
= "int";
765 attr
[j
].ca_name
= "cmask";
766 attr
[j
].ca_val
= xpes
[i
].cmask
;
770 attr
[j
].ca_name
= "umask";
771 attr
[j
].ca_val
= xpes
[i
].umask
;
775 attr
[j
].ca_name
= "inv";
780 attr
[j
].ca_name
= "pc";
784 if (xpes
[i
].edge
== 0) {
785 attr
[j
].ca_name
= "noedge";
790 if (cpc_set_add_request(cpc
, set
, pic
[i
],
791 event
->ce_pic
[i
], flags
[i
], nattrs
, attr
) == -1) {
792 (void) cpc_set_destroy(cpc
, set
);
802 case CPC_PENTIUM_MMX
:
806 unmake_cesr(event
->ce_cesr
, cpuver
, &xcesr
);
808 for (i
= 0; i
< 2; i
++) {
811 if ((pic
[i
] = regtostr(cpuver
, i
, xcesr
.evsel
[i
]))
813 (void) cpc_set_destroy(cpc
, set
);
817 if (xcesr
.usr
[i
] == 1)
818 flags
[i
] |= CPC_COUNT_USER
;
819 if (xcesr
.sys
[i
] == 1)
820 flags
[i
] |= CPC_COUNT_SYSTEM
;
821 if (xcesr
.clk
[i
] == 1)
823 if (xcesr
.pc
[i
] == 1)
826 if ((attr
= (cpc_attr_t
*)malloc(nattrs
*
827 sizeof (cpc_attr_t
))) == NULL
) {
828 (void) cpc_set_destroy(cpc
, set
);
834 * Ensure that pic[0] in the cpc_event_t is bound to
837 attr
[0].ca_name
= "picnum";
840 if (xcesr
.clk
[i
] == 1) {
841 attr
[j
].ca_name
= "noedge";
846 if (xcesr
.pc
[i
] == 1) {
847 attr
[j
].ca_name
= "pc";
852 if (cpc_set_add_request(cpc
, set
, pic
[i
],
853 event
->ce_pic
[i
], flags
[i
], nattrs
, attr
) == -1) {
854 (void) cpc_set_destroy(cpc
, set
);
866 (void) cpc_set_destroy(cpc
, set
);