- Implement sampling modes and logging support in hwpmc(4).

- Separate MI and MD parts of hwpmc(4) and allow sharing of
  PMC implementations across different architectures.
  Add support for P4 (EMT64) style PMCs to the amd64 code.

- New pmcstat(8) options: -E (exit time counts) -W (counts
  every context switch), -R (print log file).

- pmc(3) API changes, improve our ability to keep ABI compatibility
  in the future.  Add more 'alias' names for commonly used events.

- bug fixes & documentation.
This commit is contained in:
Joseph Koshy 2005-06-09 19:45:09 +00:00
parent a3f2d84279
commit f263522a45
52 changed files with 7060 additions and 3028 deletions

View File

@ -40,6 +40,8 @@
..
firewire
..
hwpmc
..
ic
..
ieee488

View File

@ -36,7 +36,7 @@ LDIRS= bsm cam geom net net80211 netatalk netatm netgraph netinet netinet6 \
pccard posix4 sys vm
LSUBDIRS= cam/scsi \
dev/acpica dev/an dev/bktr dev/firewire \
dev/acpica dev/an dev/bktr dev/firewire dev/hwpmc \
dev/ic dev/iicbus ${_dev_ieee488} dev/ofw \
dev/pbio dev/ppbus dev/smbus dev/usb dev/wi dev/utopia \
fs/devfs fs/fdescfs fs/fifofs fs/msdosfs fs/ntfs fs/nullfs \

View File

@ -2,12 +2,12 @@
LIB= pmc
SRCS= libpmc.c
INCS= pmc.h
SRCS= libpmc.c pmclog.c
INCS= pmc.h pmclog.h
WARNS?= 6
MAN= pmc.3
MAN= pmc.3 pmclog.3
MLINKS+= \
pmc.3 pmc_allocate.3 \
@ -19,6 +19,7 @@ MLINKS+= \
pmc.3 pmc_disable.3 \
pmc.3 pmc_enable.3 \
pmc.3 pmc_event_names_of_class.3 \
pmc.3 pmc_flush_logfile.3 \
pmc.3 pmc_get_driver_stats.3 \
pmc.3 pmc_init.3 \
pmc.3 pmc_name_of_capability.3 \
@ -38,6 +39,13 @@ MLINKS+= \
pmc.3 pmc_stop.3 \
pmc.3 pmc_width.3 \
pmc.3 pmc_write.3 \
pmc.3 pmc_writelog.3 \
pmc.3 pmc_x86_get_msr.3
MLINKS+= \
pmclog.3 pmclog_open.3 \
pmclog.3 pmclog_close.3 \
pmclog.3 pmclog_feed.3 \
pmclog.3 pmclog_read.3
.include <bsd.lib.mk>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
.\" Copyright (c) 2003 Joseph Koshy. All rights reserved.
.\" Copyright (c) 2003-2005 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@ -36,7 +36,9 @@
.Nm pmc_disable ,
.Nm pmc_enable ,
.Nm pmc_event_names_of_class ,
.Nm pmc_flush_logfile ,
.Nm pmc_get_driver_stats ,
.Nm pmc_get_msr ,
.Nm pmc_init ,
.Nm pmc_name_of_capability ,
.Nm pmc_name_of_class ,
@ -53,9 +55,9 @@
.Nm pmc_set ,
.Nm pmc_start ,
.Nm pmc_stop ,
.Nm pmc_write ,
.Nm pmc_width ,
.Nm pmc_x86_get_msr
.Nm pmc_write ,
.Nm pmc_writelog
.Nd programming API for using hardware performance monitoring counters
.Sh LIBRARY
.Lb libpmc
@ -79,7 +81,7 @@
.Ft int
.Fn pmc_configure_logfile "int fd"
.Ft int
.Fn pmc_cpuinfo "const struct pmc_op_getcpuinfo **cpu_info"
.Fn pmc_cpuinfo "const struct pmc_cpuinfo **cpu_info"
.Ft int
.Fo pmc_detach
.Fa "pmc_id_t pmcid"
@ -96,7 +98,11 @@
.Fa "int *nevents"
.Fc
.Ft int
.Fn pmc_get_driver_stats "struct pmc_op_getdriverstats *gms"
.Fn pmc_flush_logfile "void"
.Ft int
.Fn pmc_get_driver_stats "struct pmc_driverstats *gms"
.Ft int
.Fn pmc_get_msr "pmc_id_t pmc" "uint32_t *msr"
.Ft int
.Fn pmc_init "void"
.Ft "const char *"
@ -118,7 +124,7 @@
.Ft int
.Fn pmc_npmc "uint32_t cpu"
.Ft int
.Fn pmc_pmcinfo "uint32_t cpu" "struct pmc_op_getpmcinfo **pmc_info"
.Fn pmc_pmcinfo "uint32_t cpu" "struct pmc_pmcinfo **pmc_info"
.Ft int
.Fn pmc_read "pmc_id_t pmc" "pmc_value_t *value"
.Ft int
@ -134,9 +140,9 @@
.Ft int
.Fn pmc_write "pmc_id_t pmc" "pmc_value_t value"
.Ft int
.Fn pmc_width "pmc_id_t pmc" "uint32_t *width"
.Fn pmc_writelog "uint32_t userdata"
.Ft int
.Fn pmc_x86_get_msr "int pmc" "uint32_t *msr"
.Fn pmc_width "pmc_id_t pmc" "uint32_t *width"
.Sh DESCRIPTION
These functions implement a high-level library for using the
system's hardware performance counters.
@ -276,9 +282,24 @@ The
.Fn pmc_configure_logfile
function causes the
.Xr hwpmc 4
driver to log system wide performance data to file corresponding
driver to log performance data to file corresponding
to the process' file handle
.Fa fd .
If argument
.Fa fd
is -1, then any previously configured logging is reset
and all data queued to be written are discarded.
.Pp
The
.Fn pmc_flush_logfile
function will send all data queued inside the
.Xr hwpmc 4
driver to the configured log file before returning.
The
.Fn pmc_writelog
function will append a log entry containing the argument
.Fa userdata
to the log file.
.Pp
.Fn pmc_set
configures an sampling PMC
@ -307,8 +328,19 @@ module is unloaded using
processes that have PMCs allocated to them will be sent a
SIGBUS signal.
.It SIGIO
Attempting to read a PMC that is not currently attached to a running
process will cause a SIGIO signal to be sent to the reader.
The
.Xr hwpmc 4
driver will send a PMC owning process a SIGIO signal if:
.Bl -bullet
.It
If any process-mode PMC allocated by it loses all its
target processes.
.It
If the driver encounters an error when writing log data to a
configured log file.
This error may be retrieved by a subsequent call to
.Fn pmc_flush_logfile .
.El
.El
.Ss CONVENIENCE FUNCTIONS
.Fn pmc_ncpu
@ -321,10 +353,18 @@ returns the number of PMCs supported on CPU
sets argument
.Fa cpu_info
to point to a structure with information about the system's CPUs.
Function
.Fn pmc_pmcinfo
returns information about the current state of CPU
.Fa cpu Ap s
PMCs.
This function sets argument
.Fa *pmc_info
to point to a memory area allocated with
.Xr calloc 3 .
The caller is expected to
.Fn free
the area when done.
.Pp
The functions
.Fn pmc_name_of_capability ,
@ -370,7 +410,7 @@ is the index of the PMC to be operated on.
Only the super-user is allowed to enable and disable PMCs.
.Ss X86 ARCHITECTURE SPECIFIC API
The
.Fn pmc_x86_get_msr
.Fn pmc_get_msr
function returns the processor model specific register number
associated with
.Fa pmc .
@ -3096,25 +3136,39 @@ was unrecognized for this cpu type.
.Pp
Calls to
.Fn pmc_attach ,
.Fn pmc_configure_logfile ,
.Fn pmc_detach ,
.Fn pmc_release ,
.Fn pmc_start ,
.Fn pmc_stop ,
.Fn pmc_disable ,
.Fn pmc_enable ,
.Fn pmc_get_driver_stats ,
.Fn pmc_get_msr ,
.Fn pmc_read ,
.Fn pmc_write ,
.Fn pmc_release ,
.Fn pmc_rw ,
.Fn pmc_set ,
.Fn pmc_configure_logfile ,
.Fn pmc_get_driver_stats ,
.Fn pmc_enable ,
.Fn pmc_disable ,
.Fn pmc_start ,
.Fn pmc_stop ,
.Fn pmc_write ,
and
.Fn pmc_x86_get_msr
.Fn pmc_writelog
may fail with the errors described in
.Xr hwpmc 4 .
.Pp
If a log file was configured using
.Fn pmc_configure_logfile
and the
.Xr hwpmc 4
driver encountered an error while logging data to it, then
logging will be stopped and a subsequent call to
.Fn pmc_flush_logfile
will fail with the error code seen by the
.Xr hwpmc 4
driver.
.Sh SEE ALSO
.Xr modfind 2 ,
.Xr modstat 2 ,
.Xr calloc 3 ,
.Xr pmclog 3 ,
.Xr hwpmc 4 ,
.Xr pmccontrol 8 ,
.Xr pmcreport 8 ,
@ -3126,12 +3180,6 @@ The information returned by
and possibly
.Fn pmc_npmc
should really be available all the time, through a better designed
interface.
.Pp
The API for
.Fn pmc_cpuinfo
and
.Fn pmc_pmcinfo
expose too much of the underlying
interface and not just when
.Xr hwpmc 4
driver's internals to userland.
is present in the kernel.

View File

@ -31,6 +31,39 @@
#include <sys/pmc.h>
/*
* Driver statistics.
*/
struct pmc_driverstats {
int pm_intr_ignored; /* #interrupts ignored */
int pm_intr_processed; /* #interrupts processed */
int pm_intr_bufferfull; /* #interrupts with ENOSPC */
int pm_syscalls; /* #syscalls */
int pm_syscall_errors; /* #syscalls with errors */
int pm_buffer_requests; /* #buffer requests */
int pm_buffer_requests_failed; /* #failed buffer requests */
int pm_log_sweeps; /* #sample buffer processing passes */
};
/*
* CPU information.
*/
struct pmc_cpuinfo {
enum pmc_cputype pm_cputype; /* the kind of CPU */
uint32_t pm_ncpu; /* number of CPUs */
uint32_t pm_npmc; /* #PMCs per CPU */
uint32_t pm_nclass; /* #classes of PMCs */
struct pmc_classinfo pm_classes[PMC_CLASS_MAX];
};
/*
* Current PMC state.
*/
struct pmc_pmcinfo {
int32_t pm_cpu; /* CPU number */
struct pmc_info pm_pmcs[]; /* NPMC structs */
};
/*
* Prototypes
*/
@ -40,10 +73,12 @@ int pmc_allocate(const char *_ctrspec, enum pmc_mode _mode, uint32_t _flags,
int pmc_attach(pmc_id_t _pmcid, pid_t _pid);
int pmc_capabilities(pmc_id_t _pmc, uint32_t *_caps);
int pmc_configure_logfile(int _fd);
int pmc_flush_logfile(void);
int pmc_detach(pmc_id_t _pmcid, pid_t _pid);
int pmc_disable(int _cpu, int _pmc);
int pmc_enable(int _cpu, int _pmc);
int pmc_get_driver_stats(struct pmc_op_getdriverstats *_gms);
int pmc_get_driver_stats(struct pmc_driverstats *_gms);
int pmc_get_msr(pmc_id_t _pmc, uint32_t *_msr);
int pmc_init(void);
int pmc_read(pmc_id_t _pmc, pmc_value_t *_value);
int pmc_release(pmc_id_t _pmc);
@ -53,11 +88,12 @@ int pmc_start(pmc_id_t _pmc);
int pmc_stop(pmc_id_t _pmc);
int pmc_width(pmc_id_t _pmc, uint32_t *_width);
int pmc_write(pmc_id_t _pmc, pmc_value_t _value);
int pmc_writelog(uint32_t _udata);
int pmc_ncpu(void);
int pmc_npmc(int _cpu);
int pmc_cpuinfo(const struct pmc_op_getcpuinfo **_cpu_info);
int pmc_pmcinfo(int _cpu, struct pmc_op_getpmcinfo **_pmc_info);
int pmc_cpuinfo(const struct pmc_cpuinfo **_cpu_info);
int pmc_pmcinfo(int _cpu, struct pmc_pmcinfo **_pmc_info);
const char *pmc_name_of_capability(uint32_t _c);
const char *pmc_name_of_class(enum pmc_class _pc);
@ -70,12 +106,4 @@ const char *pmc_name_of_state(enum pmc_state _ps);
int pmc_event_names_of_class(enum pmc_class _cl, const char ***_eventnames,
int *_nevents);
/*
* Architecture specific extensions
*/
#if __i386__ || __amd64__
int pmc_x86_get_msr(pmc_id_t _pmc, uint32_t *_msr);
#endif
#endif

276
lib/libpmc/pmclog.3 Normal file
View File

@ -0,0 +1,276 @@
.\" Copyright (c) 2005 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $FreeBSD$
.\"
.Dd Jun 1, 2005
.Os
.Dt PMCLOG 3
.Sh NAME
.Nm pmclog_open ,
.Nm pmclog_close ,
.Nm pmclog_read ,
.Nm pmclog_feed
.Nd parse event log data generated by
.Xr hwpmc 4
.Sh LIBRARY
.Lb libpmc
.Sh SYNOPSIS
.In pmclog.h
.Ft "void *"
.Fn pmclog_open "int fd"
.Ft void
.Fn pmclog_close "void *cookie"
.Ft int
.Fn pmclog_read "void *cookie" "struct pmclog_ev *ev"
.Ft int
.Fn pmclog_feed "void *cookie" "char *data" "int len"
.Sh DESCRIPTION
These functions provide a way for application programs to extract
events from an event stream generated by
.Xr hwpmc 4 .
.Pp
A new event log parser is allocated using
.Fn pmclog_open .
Argument
.Fa fd
may be a file descriptor opened for reading if the event stream is
present in a file, or the constant
.Dv PMCLOG_FD_NONE
for an event stream present in memory.
This function returns a cookie that is passed into the other functions
in this API set.
.Pp
Function
.Fn pmclog_read
returns the next available event in the event stream associated with
argument
.Fa cookie .
Argument
.Fa ev
points to an event descriptor that which will contain the result of a
successfully parsed event.
.Pp
An event descriptor returned by
.Fn pmclog_read
has the following structure:
.Bd -literal
struct pmclog_ev {
enum pmclog_state pl_state; /* parser state after 'get_event()' */
off_t pl_offset; /* byte offset in stream */
size_t pl_count; /* count of records so far */
struct timespec pl_ts; /* log entry timestamp */
enum pmclog_type pl_type; /* log entry kind */
union { /* log entry data */
struct pmclog_ev_allocate pl_a;
struct pmclog_ev_proccsw pl_c;
struct pmclog_ev_dropnotify pl_d;
struct pmclog_ev_procexit pl_e;
struct pmclog_ev_initialize pl_i;
struct pmclog_ev_pcsample pl_s;
struct pmclog_ev_pmcattach pl_t;
struct pmclog_ev_userdata pl_u;
struct pmclog_ev_procexec pl_x;
} pl_u;
};
.Ed
.Pp
The current state of the parser is recorded in
.Va pl_state .
This field can take on the following values:
.Bl -tag -width "PMCLOG_REQUIRE_DATA" -compact
.It Dv PMCLOG_EOF
.Pq For file based parsers only
An end-of-file condition was encountered on the configured file
descriptor.
.It Dv PMCLOG_ERROR
An error occurred during parsing.
.It Dv PMCLOG_OK
A complete event record was read into
.Fa "*ev" .
.It Dv PMCLOG_REQUIRE_DATA
There was insufficient data in the event stream to assemble a complete
event record.
For memory based parsers, more data can be fed to the
parser using function
.Fn pmclog_feed .
For file based parsers, function
.Fn pmclog_read
may be retried when data is available on the configured file
descriptor.
.El
.Pp
The rest of the event structure is valid only if field
.Va pl_state
contains
.Dv PMCLOG_OK .
Field
.Va pl_offset
contains the offset of the current record in the byte stream.
Field
.Va pl_count
contains the serial number of this event.
Field
.Va pl_ts
contains a timestamp with the system time when the event occurred.
Field
.Va pl_type
denotes the kind of the event returned in argument
.Fa *ev
and is one of the following:
.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXX -compact
.It Dv PMCLOG_TYPE_DROPNOTIFY
a marker indicating that
.Xr hwpmc 4
had to drop data due to a resource constraint.
.It Dv PMCLOG_TYPE_INITIALIZE
an initialization record.
This is usually the first record in a log file.
.It Dv PMCLOG_TYPE_PCSAMPLE
A record containing an instruction pointer sample.
.It Dv PMCLOG_TYPE_PMCALLOCATE
A record describing a PMC allocation operation.
.It Dv PMCLOG_TYPE_PMCATTACH
A record describing a PMC attach operation.
.It Dv PMCLOG_TYPE_PROCCSW
A record describing a PMC reading at the time of a process context switch.
.It Dv PMCLOG_TYPE_PROCEXIT
A record describing the accumulated PMC reading for a process at the
time of
.Xr _exit 2 .
.It Dv PMCLOG_TYPE_PROCEXEC
A record describing an
.Xr execve 2
by a target process.
.It Dv PMCLOG_TYPE_USERDATA
A record containing user data.
.El
.Pp
Function
.Fn pmclog_feed
is used with parsers configured to parse memory based event streams.
It is intended to be called when function
.Fn pmclog_read
indicates the need for more data by a returning
.Dv PMCLOG_REQUIRE_DATA
in field
.Va pl_state
of its event structure argument.
Argument
.Fa data
points to the start of a memory buffer containing fresh event data.
Argument
.Fa len
indicates the number of bytes of data available.
The memory range
.Bq data , data+len
must remain valid till the next time
.Fn pmclog_read
returns an error.
It is an error to use
.Fn pmclog_feed
on a parser configured to parse file data.
.Pp
Function
.Fn pmclog_close
releases the internal state allocated by a prior call
to
.Fn pmclog_open .
.Sh RETURN VALUES
Function
.Fn pmclog_open
will return a non-NULL value if successful or NULL otherwise.
.Pp
Function
.Fn pmclog_read
will return 0 in case a complete event record was successfully read,
or will return -1 and will set the
.Va pl_state
field of the event record to the appropriate code in case of an error.
.Pp
Function
.Fn pmclog_feed
will return 0 on success or -1 in case of failure.
.Sh EXAMPLES
A template for using the log file parsing API is shown below in psuedocode:
.Bd -literal
void *parser; /* cookie */
struct pmclog_ev ev; /* parsed event */
int fd; /* file descriptor */
fd = open(filename, O_RDONLY); /* open log file */
parser = pmclog_open(fd); /* initialize parser */
if (parser == NULL)
--handle an out of memory error--;
/* read and parse data */
while (pmclog_read(parser, &ev) == 0) {
assert(ev.pl_state == PMCLOG_OK);
/* process the event */
switch (ev.pl_type) {
case PMCLOG_TYPE_ALLOCATE:
--process a pmc allocation record--
break;
case PMCLOG_TYPE_PROCCSW:
--process a thread context switch record--
break;
case PMCLOG_TYPE_PCSAMPLE:
--process a PC sample--
break;
--and so on--
}
}
/* examine parser state */
switch (ev.pl_state) {
case PMCLOG_EOF:
--normal termination--
break;
case PMCLOG_ERROR:
--look at errno here--
break;
case PMCLOG_REQUIRE_DATA:
--arrange for more data to be available for parsing--
break;
default:
assert(0);
/*NOTREACHED*/
}
pmclog_close(parser); /* cleanup */
.Ed
.Sh ERRORS
A call to
.Fn pmclog_init_parser
may fail with any of the errors returned by
.Xr malloc 3 .
.Pp
A call to
.Fn pmclog_read
for a file based parser may fail with any of the errors returned by
.Xr read 2 .
.Sh SEE ALSO
.Xr read 2 ,
.Xr malloc 3 ,
.Xr pmc 3 ,
.Xr hwpmc 4

532
lib/libpmc/pmclog.c Normal file
View File

@ -0,0 +1,532 @@
/*-
* Copyright (c) 2005 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/pmc.h>
#include <sys/pmclog.h>
#include <assert.h>
#include <errno.h>
#include <pmc.h>
#include <pmclog.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <machine/pmc_mdep.h>
#define PMCLOG_BUFFER_SIZE 4096
/*
* API NOTES
*
* The pmclog(3) API is oriented towards parsing an event stream in
* "realtime", i.e., from an data source that may or may not preserve
* record boundaries -- for example when the data source is elsewhere
* on a network. The API allows data to be fed into the parser zero
* or more bytes at a time.
*
* The state for a log file parser is maintained in a 'struct
* pmclog_parse_state'. Parser invocations are done by calling
* 'pmclog_read()'; this function will inform the caller when a
* complete event is parsed.
*
* The parser first assembles a complete log file event in an internal
* work area (see "ps_saved" below). Once a complete log file event
* is read, the parser then parses it and converts it to an event
* descriptor usable by the client. We could possibly avoid this two
* step process by directly parsing the input log to set fields in the
* event record. However the parser's state machine would get
* insanely complicated, and this code is unlikely to be used in
* performance critical paths.
*/
enum pmclog_parser_state {
PL_STATE_NEW_RECORD, /* in-between records */
PL_STATE_EXPECTING_HEADER, /* header being read */
PL_STATE_PARTIAL_RECORD, /* header present but not the record */
PL_STATE_ERROR /* parsing error encountered */
};
struct pmclog_parse_state {
enum pmclog_parser_state ps_state;
enum pmc_cputype ps_arch; /* log file architecture */
uint32_t ps_version; /* hwpmc version */
int ps_initialized; /* whether initialized */
int ps_count; /* count of records processed */
off_t ps_offset; /* stream byte offset */
union pmclog_entry ps_saved; /* saved partial log entry */
int ps_svcount; /* #bytes saved */
int ps_fd; /* active fd or -1 */
char *ps_buffer; /* scratch buffer if fd != -1 */
char *ps_data; /* current parse pointer */
size_t ps_len; /* length of buffered data */
};
#define PMCLOG_HEADER_FROM_SAVED_STATE(PS) \
(* ((uint32_t *) &(PS)->ps_saved))
#define PMCLOG_INITIALIZE_READER(LE,A) LE = (uint32_t *) &(A)
#define PMCLOG_READ32(LE,V) do { \
(V) = *(LE)++; \
} while (0)
#define PMCLOG_READ64(LE,V) do { \
uint64_t _v; \
_v = (uint64_t) *(LE)++; \
_v |= ((uint64_t) *(LE)++) << 32; \
(V) = _v; \
} while (0)
#define PMCLOG_READSTRING(LE,DST,LEN) strlcpy((DST), (char *) (LE), (LEN))
/*
* Assemble a log record from '*len' octets starting from address '*data'.
* Update 'data' and 'len' to reflect the number of bytes consumed.
*
* '*data' is potentially an unaligned address and '*len' octets may
* not be enough to complete a event record.
*/
static enum pmclog_parser_state
pmclog_get_record(struct pmclog_parse_state *ps, char **data, ssize_t *len)
{
int avail, copylen, recordsize, used;
uint32_t h;
const int HEADERSIZE = sizeof(uint32_t);
char *src, *dst;
if ((avail = *len) <= 0)
return (ps->ps_state = PL_STATE_ERROR);
src = *data;
h = used = 0;
if (ps->ps_state == PL_STATE_NEW_RECORD)
ps->ps_svcount = 0;
dst = (char *) &ps->ps_saved + ps->ps_svcount;
switch (ps->ps_state) {
case PL_STATE_NEW_RECORD:
/*
* Transitions:
*
* Case A: avail < headersize
* -> 'expecting header'
*
* Case B: avail >= headersize
* B.1: avail < recordsize
* -> 'partial record'
* B.2: avail >= recordsize
* -> 'new record'
*/
copylen = avail < HEADERSIZE ? avail : HEADERSIZE;
bcopy(src, dst, copylen);
ps->ps_svcount = used = copylen;
if (copylen < HEADERSIZE) {
ps->ps_state = PL_STATE_EXPECTING_HEADER;
goto done;
}
src += copylen;
dst += copylen;
h = PMCLOG_HEADER_FROM_SAVED_STATE(ps);
recordsize = PMCLOG_HEADER_TO_LENGTH(h);
if (recordsize <= 0)
goto error;
if (recordsize <= avail) { /* full record available */
bcopy(src, dst, recordsize - copylen);
ps->ps_svcount = used = recordsize;
goto done;
}
/* header + a partial record is available */
bcopy(src, dst, avail - copylen);
ps->ps_svcount = used = avail;
ps->ps_state = PL_STATE_PARTIAL_RECORD;
break;
case PL_STATE_EXPECTING_HEADER:
/*
* Transitions:
*
* Case C: avail+saved < headersize
* -> 'expecting header'
*
* Case D: avail+saved >= headersize
* D.1: avail+saved < recordsize
* -> 'partial record'
* D.2: avail+saved >= recordsize
* -> 'new record'
* (see PARTIAL_RECORD handling below)
*/
if (avail + ps->ps_svcount < HEADERSIZE) {
bcopy(src, dst, avail);
ps->ps_svcount += avail;
used = avail;
break;
}
used = copylen = HEADERSIZE - ps->ps_svcount;
bcopy(src, dst, copylen);
src += copylen;
dst += copylen;
avail -= copylen;
ps->ps_svcount += copylen;
/*FALLTHROUGH*/
case PL_STATE_PARTIAL_RECORD:
/*
* Transitions:
*
* Case E: avail+saved < recordsize
* -> 'partial record'
*
* Case F: avail+saved >= recordsize
* -> 'new record'
*/
h = PMCLOG_HEADER_FROM_SAVED_STATE(ps);
recordsize = PMCLOG_HEADER_TO_LENGTH(h);
if (recordsize <= 0)
goto error;
if (avail + ps->ps_svcount < recordsize) {
copylen = avail;
ps->ps_state = PL_STATE_PARTIAL_RECORD;
} else {
copylen = recordsize - ps->ps_svcount;
ps->ps_state = PL_STATE_NEW_RECORD;
}
bcopy(src, dst, copylen);
ps->ps_svcount += copylen;
used += copylen;
break;
default:
goto error;
}
done:
*data += used;
*len -= used;
return ps->ps_state;
error:
ps->ps_state = PL_STATE_ERROR;
return ps->ps_state;
}
/*
* Get an event from the stream pointed to by '*data'. '*len'
* indicates the number of bytes available to parse. Arguments
* '*data' and '*len' are updated to indicate the number of bytes
* consumed.
*/
static int
pmclog_get_event(void *cookie, char **data, ssize_t *len,
struct pmclog_ev *ev)
{
int evlen, pathlen;
uint32_t h, *le;
enum pmclog_parser_state e;
struct pmclog_parse_state *ps;
ps = (struct pmclog_parse_state *) cookie;
assert(ps->ps_state != PL_STATE_ERROR);
if ((e = pmclog_get_record(ps,data,len)) == PL_STATE_ERROR) {
ev->pl_state = PMCLOG_ERROR;
return -1;
}
if (e != PL_STATE_NEW_RECORD) {
ev->pl_state = PMCLOG_REQUIRE_DATA;
return -1;
}
PMCLOG_INITIALIZE_READER(le, ps->ps_saved);
PMCLOG_READ32(le,h);
if (!PMCLOG_HEADER_CHECK_MAGIC(h)) {
ps->ps_state = PL_STATE_ERROR;
ev->pl_state = PMCLOG_ERROR;
return -1;
}
/* copy out the time stamp */
PMCLOG_READ32(le,ev->pl_ts.tv_sec);
PMCLOG_READ32(le,ev->pl_ts.tv_nsec);
evlen = PMCLOG_HEADER_TO_LENGTH(h);
#define PMCLOG_GET_PATHLEN(P,E,TYPE) do { \
(P) = (E) - offsetof(struct TYPE, pl_pathname); \
if ((P) > PATH_MAX || (P) < 0) \
goto error; \
} while (0)
switch (ev->pl_type = PMCLOG_HEADER_TO_TYPE(h)) {
case PMCLOG_TYPE_CLOSELOG:
case PMCLOG_TYPE_DROPNOTIFY:
/* nothing to do */
break;
case PMCLOG_TYPE_INITIALIZE:
PMCLOG_READ32(le,ev->pl_u.pl_i.pl_version);
PMCLOG_READ32(le,ev->pl_u.pl_i.pl_arch);
ps->ps_version = ev->pl_u.pl_i.pl_version;
ps->ps_arch = ev->pl_u.pl_i.pl_arch;
ps->ps_initialized = 1;
break;
case PMCLOG_TYPE_MAPPINGCHANGE:
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_mappingchange);
PMCLOG_READ32(le,ev->pl_u.pl_m.pl_type);
PMCLOG_READADDR(le,ev->pl_u.pl_m.pl_start);
PMCLOG_READADDR(le,ev->pl_u.pl_m.pl_end);
PMCLOG_READ32(le,ev->pl_u.pl_m.pl_pid);
PMCLOG_READSTRING(le, ev->pl_u.pl_m.pl_pathname, pathlen);
break;
case PMCLOG_TYPE_PCSAMPLE:
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pid);
PMCLOG_READADDR(le,ev->pl_u.pl_s.pl_pc);
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pmcid);
break;
case PMCLOG_TYPE_PMCALLOCATE:
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_pmcid);
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_event);
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_flags);
if ((ev->pl_u.pl_a.pl_evname =
pmc_name_of_event(ev->pl_u.pl_a.pl_event)) == NULL)
goto error;
break;
case PMCLOG_TYPE_PMCATTACH:
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_pmcattach);
PMCLOG_READ32(le,ev->pl_u.pl_t.pl_pmcid);
PMCLOG_READ32(le,ev->pl_u.pl_t.pl_pid);
PMCLOG_READSTRING(le,ev->pl_u.pl_t.pl_pathname,pathlen);
break;
case PMCLOG_TYPE_PMCDETACH:
PMCLOG_READ32(le,ev->pl_u.pl_d.pl_pmcid);
PMCLOG_READ32(le,ev->pl_u.pl_d.pl_pid);
break;
case PMCLOG_TYPE_PROCCSW:
PMCLOG_READ32(le,ev->pl_u.pl_c.pl_pmcid);
PMCLOG_READ64(le,ev->pl_u.pl_c.pl_value);
PMCLOG_READ32(le,ev->pl_u.pl_c.pl_pid);
break;
case PMCLOG_TYPE_PROCEXEC:
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec);
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pid);
PMCLOG_READSTRING(le,ev->pl_u.pl_x.pl_pathname,pathlen);
break;
case PMCLOG_TYPE_PROCEXIT:
PMCLOG_READ32(le,ev->pl_u.pl_e.pl_pmcid);
PMCLOG_READ64(le,ev->pl_u.pl_e.pl_value);
PMCLOG_READ32(le,ev->pl_u.pl_e.pl_pid);
break;
case PMCLOG_TYPE_PROCFORK:
PMCLOG_READ32(le,ev->pl_u.pl_f.pl_oldpid);
PMCLOG_READ32(le,ev->pl_u.pl_f.pl_newpid);
break;
case PMCLOG_TYPE_SYSEXIT:
PMCLOG_READ32(le,ev->pl_u.pl_se.pl_pid);
break;
case PMCLOG_TYPE_USERDATA:
PMCLOG_READ32(le,ev->pl_u.pl_u.pl_userdata);
break;
default: /* unknown record type */
ps->ps_state = PL_STATE_ERROR;
ev->pl_state = PMCLOG_ERROR;
return -1;
}
ev->pl_offset = (ps->ps_offset += evlen);
ev->pl_count = (ps->ps_count += 1);
ev->pl_state = PMCLOG_OK;
return 0;
error:
ev->pl_state = PMCLOG_ERROR;
ps->ps_state = PL_STATE_ERROR;
return -1;
}
/*
* Extract and return the next event from the byte stream.
*
* Returns 0 and sets the event's state to PMCLOG_OK in case an event
* was successfully parsed. Otherwise this function returns -1 and
* sets the event's state to one of PMCLOG_REQUIRE_DATA (if more data
* is needed) or PMCLOG_EOF (if an EOF was seen) or PMCLOG_ERROR if
* a parse error was encountered.
*/
int
pmclog_read(void *cookie, struct pmclog_ev *ev)
{
ssize_t nread;
struct pmclog_parse_state *ps;
ps = (struct pmclog_parse_state *) cookie;
if (ps->ps_state == PL_STATE_ERROR) {
ev->pl_state = PMCLOG_ERROR;
return -1;
}
/*
* If there isn't enough data left for a new event try and get
* more data.
*/
if (ps->ps_len == 0) {
ev->pl_state = PMCLOG_REQUIRE_DATA;
/*
* If we have a valid file descriptor to read from, attempt
* to read from that. This read may return with an error,
* (which may be EAGAIN or other recoverable error), or
* can return EOF.
*/
if (ps->ps_fd != PMCLOG_FD_NONE) {
nread = read(ps->ps_fd, ps->ps_buffer,
PMCLOG_BUFFER_SIZE);
if (nread <= 0) {
ev->pl_state = nread < 0 ? PMCLOG_ERROR :
PMCLOG_EOF;
return -1;
}
ps->ps_len = nread;
ps->ps_data = ps->ps_buffer;
} else
return -1;
}
assert(ps->ps_len > 0);
/*
* Retrieve one event from the byte stream.
*/
return pmclog_get_event(ps, &ps->ps_data, &ps->ps_len, ev);
}
/*
* Feed data to a memory based parser.
*
* The memory area pointed to by 'data' needs to be valid till the
* next error return from pmclog_next_event().
*/
int
pmclog_feed(void *cookie, char *data, int len)
{
struct pmclog_parse_state *ps;
ps = (struct pmclog_parse_state *) cookie;
if (len < 0 || /* invalid length */
ps->ps_buffer || /* called for a file parser */
ps->ps_len != 0) /* unnecessary call */
return -1;
ps->ps_data = data;
ps->ps_len = len;
return 0;
}
/*
* Allocate and initialize parser state.
*/
void *
pmclog_open(int fd)
{
struct pmclog_parse_state *ps;
if ((ps = (struct pmclog_parse_state *) malloc(sizeof(*ps))) == NULL)
return NULL;
ps->ps_state = PL_STATE_NEW_RECORD;
ps->ps_arch = -1;
ps->ps_initialized = 0;
ps->ps_count = 0;
ps->ps_offset = (off_t) 0;
bzero(&ps->ps_saved, sizeof(ps->ps_saved));
ps->ps_svcount = 0;
ps->ps_fd = fd;
ps->ps_data = NULL;
ps->ps_buffer = NULL;
ps->ps_len = 0;
/* allocate space for a work area */
if (ps->ps_fd != PMCLOG_FD_NONE) {
if ((ps->ps_buffer = malloc(PMCLOG_BUFFER_SIZE)) == NULL)
return NULL;
}
return ps;
}
/*
* Free up parser state.
*/
void
pmclog_close(void *cookie)
{
struct pmclog_parse_state *ps;
ps = (struct pmclog_parse_state *) cookie;
if (ps->ps_buffer)
free(ps->ps_buffer);
free(ps);
}

146
lib/libpmc/pmclog.h Normal file
View File

@ -0,0 +1,146 @@
/*-
* Copyright (c) 2005 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _PMCLOG_H_
#define _PMCLOG_H_
#include <sys/pmclog.h>
enum pmclog_state {
PMCLOG_OK,
PMCLOG_EOF,
PMCLOG_REQUIRE_DATA,
PMCLOG_ERROR
};
struct pmclog_ev_dropnotify {
};
struct pmclog_ev_closelog {
};
struct pmclog_ev_initialize {
uint32_t pl_version;
uint32_t pl_arch;
};
struct pmclog_ev_mappingchange {
uint32_t pl_type;
pid_t pl_pid;
uintfptr_t pl_start;
uintfptr_t pl_end;
char pl_pathname[PATH_MAX];
};
struct pmclog_ev_pcsample {
uintfptr_t pl_pc;
pid_t pl_pid;
pmc_id_t pl_pmcid;
};
struct pmclog_ev_pmcallocate {
uint32_t pl_event;
const char * pl_evname;
uint32_t pl_flags;
pmc_id_t pl_pmcid;
};
struct pmclog_ev_pmcattach {
pmc_id_t pl_pmcid;
pid_t pl_pid;
char pl_pathname[PATH_MAX];
};
struct pmclog_ev_pmcdetach {
pmc_id_t pl_pmcid;
pid_t pl_pid;
};
struct pmclog_ev_proccsw {
pid_t pl_pid;
pmc_id_t pl_pmcid;
pmc_value_t pl_value;
};
struct pmclog_ev_procexec {
pid_t pl_pid;
char pl_pathname[PATH_MAX];
};
struct pmclog_ev_procexit {
uint32_t pl_pid;
pmc_id_t pl_pmcid;
pmc_value_t pl_value;
};
struct pmclog_ev_procfork {
pid_t pl_oldpid;
pid_t pl_newpid;
};
struct pmclog_ev_sysexit {
pid_t pl_pid;
};
struct pmclog_ev_userdata {
uint32_t pl_userdata;
};
struct pmclog_ev {
enum pmclog_state pl_state; /* state after 'get_event()' */
off_t pl_offset; /* byte offset in stream */
size_t pl_count; /* count of records so far */
struct timespec pl_ts; /* log entry timestamp */
enum pmclog_type pl_type; /* type of log entry */
union { /* log entry data */
struct pmclog_ev_closelog pl_cl;
struct pmclog_ev_dropnotify pl_dn;
struct pmclog_ev_initialize pl_i;
struct pmclog_ev_mappingchange pl_m;
struct pmclog_ev_pcsample pl_s;
struct pmclog_ev_pmcallocate pl_a;
struct pmclog_ev_pmcattach pl_t;
struct pmclog_ev_pmcdetach pl_d;
struct pmclog_ev_proccsw pl_c;
struct pmclog_ev_procexec pl_x;
struct pmclog_ev_procexit pl_e;
struct pmclog_ev_procfork pl_f;
struct pmclog_ev_sysexit pl_se;
struct pmclog_ev_userdata pl_u;
} pl_u;
};
#define PMCLOG_FD_NONE (-1)
void *pmclog_open(int _fd);
int pmclog_feed(void *_cookie, char *_data, int _len);
int pmclog_read(void *_cookie, struct pmclog_ev *_ev);
void pmclog_close(void *_cookie);
#endif

View File

@ -186,18 +186,48 @@ The
.Ic PMC_OP_PMCALLOCATE
operation supports the following flags that modify the behavior
of an allocated PMC:
.Bl -tag -width indent
.Bl -tag -width indent -compact
.It Dv PMC_F_DESCENDANTS
This flag is valid only for a PMC being allocated in process-private
This modifier is valid only for a PMC being allocated in process-private
mode.
It signifies that the PMC will track hardware events for its
target process and the target's current and future descendants.
.It Dv PMC_F_KGMON
This modifier is valid only for a PMC being allocated in system-wide
sampling mode.
It signifies that the PMC's sampling interrupt is to be used to drive
kernel profiling via
.Xr kgmon 8 .
.It Dv PMC_F_LOG_PROCCSW
This modifier is valid only for a PMC being allocated in process-private
mode.
When this modifier is present, at every process context switch time,
.Nm
will append a record containing the count of the hardware events
seen by the process to the configured log file.
.It Dv PMC_F_LOG_PROCEXIT
This modifier is valid only for a PMC being allocated in process-private
mode.
With this modifier present,
.Nm
will maintain per-process counts for each target process attached to
a PMC.
At process exit time, a record containing the target process' pid and
the accumulated per-process count for that process will be written to the
configured log file.
.El
Modifiers
.Dv PMC_F_LOG_PROCEXIT
and
.Dv PMC_F_LOG_PROCCSW
may be used in combination with modifier
.Dv PMC_F_DESCENDANTS
to track the behaviour of complex pipelines of processes.
.Ss SIGNALS
The
.Nm
driver may deliver signals to processes that have allocated PMCs:
.Bl -tag -width indent
.Bl -tag -width "XXXXXXXX" -compact
.It Bq SIGIO
A
.Ic PMC_OP_PMCRW
@ -226,6 +256,12 @@ driver supports the following operations:
.Bl -tag -width indent
.It Ic PMC_OP_CONFIGURELOG
Configure a log file for sampling mode PMCs.
.It Ic PMC_OP_FLUSHLOG
Transfer buffered log data inside
.Nm
to a configured output file.
This operation returns to the caller after the write operation
has returned.
.It Ic PMC_OP_GETCPUINFO
Retrieve information about the number of CPUs on the system and
the number of hardware performance monitoring counters available per-CPU.
@ -316,9 +352,11 @@ The behavior of
.Nm
is influenced by the following
.Xr sysctl 8
and
.Xr loader 8
tunables:
.Bl -tag -width indent
.It Va kern.hwpmc.debugflags
.It Va kern.hwpmc.debugflags Pq string, read-write
(Only available if the
.Nm
driver was compiled with
@ -326,22 +364,34 @@ driver was compiled with
Control the verbosity of debug messages from the
.Nm
driver.
.It Va kern.hwpmc.hashsize
.It Va kern.hwpmc.hashsize Pq integer, read-only
The number of rows in the hash-tables used to keep track of owner and
target processes.
.It Va kern.hwpmc.mtxpoolsize
The default is 16.
.It Va kern.hwpmc.logbuffersize Pq integer, read-only
The size in kilobytes of each log buffer used by
.Nm Ap s
logging function.
The default buffers size is 4KB.
.It Va kern.hwpmc.mtxpoolsize Pq integer, read-only
The size of the spin mutex pool used by the PMC driver.
.It Va kern.hwpmc.pcpubuffersize
The size of the per-cpu hash table used when performing system-wide
statistical profiling.
.It Va security.bsd.unprivileged_syspmcs
The default is 32.
.It Va kern.hwpmc.nbuffers Pq integer, read-only
The number of log buffers used by
.Nm
for logging.
The default is 16.
.It Va kern.hwpmc.nsamples Pq integer, read-only
The number of entries in the per-cpu ring buffer used during sampling.
The default is 16.
.It Va security.bsd.unprivileged_syspmcs Pq boolean, read-write
If set to non-zero, allow unprivileged processes to allocate system-wide
PMCs.
The default value is 0.
.It Va security.bsd.unprivileged_proc_debug
.It Va security.bsd.unprivileged_proc_debug Pq boolean, read-write
If set to 0, the
.Nm
driver will only allow privileged process to attach PMCs to other
driver will only allow privileged processes to attach PMCs to other
processes.
.El
.Pp
@ -361,11 +411,9 @@ Set the
tunable
.Va "security.bsd.unprivileged_syspmcs"
to 0.
.Pp
This ensures that unprivileged processes cannot allocate system-wide
PMCs and thus cannot observe the hardware behavior of the system
as a whole.
.Pp
This tunable may also be set at boot time using
.Xr loader 8 ,
or with
@ -379,7 +427,6 @@ Set the
tunable
.Va "security.bsd.unprivileged_proc_debug"
to 0.
.Pp
This will ensure that an unprivileged process cannot attach a PMC
to any process other than itself and thus cannot observe the hardware
behavior of other processes with the same credentials.
@ -390,6 +437,9 @@ System administrators should note that on IA-32 platforms
makes the content of the IA-32 TSC counter available to all processes
via the RDTSC instruction.
.Sh IMPLEMENTATION NOTES
.Ss SMP Symmetry
The kernel driver requires all physical CPUs in an SMP system to have
identical performance monitoring counter hardware.
.Ss i386 TSC Handling
Historically, on the x86 architecture,
.Fx
@ -398,9 +448,6 @@ read the TSC using the RDTSC instruction.
The
.Nm
driver preserves this semantic.
.Pp
TSCs are treated as shared, read-only counters and hence are
only allowed to be allocated in system-wide counting mode.
.Ss Intel P4/HTT Handling
On CPUs with HTT support, Intel P4 PMCs are capable of qualifying
only a subset of hardware events on a per-logical CPU basis.
@ -410,6 +457,44 @@ PMCs, then the
driver will reject allocation requests for process-private PMCs that
request counting of hardware events that cannot be counted separately
for each logical CPU.
.Ss Intel Pentium-Pro Handling
Writing a value to the PMC MSRs found ing Intel Pentium-Pro style PMCs
(found in
.Tn "Intel Pentium Pro" ,
.Tn "Pentium II" ,
.Tn "Pentium III" ,
.Tn "Pentium M"
and
.Tn "Celeron"
processors) will replicate bit 31 of the
value being written into the upper 8 bits of the MSR,
bringing down the usable width of these PMCs to 31 bits.
For process-virtual PMCs, the
.Nm
driver implements a workaround in software and makes the corrected 64
bit count available via the
.Ic PMC_OP_RW
operation.
Processes that intend to use RDPMC instructions directly or
that intend to write values larger than 2^31 into these PMCs with
.Ic PMC_OP_RW
need to be aware of this hardware limitation.
.Sh DIAGNOSTICS
.Bl -diag
.It hwpmc: tunable hashsize=%d must be greater than zero.
A negative value was supplied for tunable
.Va kern.hwpmc.hashsize .
.It hwpmc: tunable logbuffersize=%d must be greater than zero.
A negative value was supplied for tunable
.Va kern.hwpmc.logbuffersize .
.It hwpmc: tunable nlogbuffers=%d must be greater than zero.
A negative value was supplied for tunable
.Va kern.hwpmc.nlogbuffers .
.It hwpmc: tunable nsamples=%d out of range.
The value for tunable
.Va kern.hwpmc.nsamples
was negative or greater than 65535.
.El
.Sh ERRORS
An command issued to the
.Nm
@ -567,7 +652,11 @@ An
operation was issued on a PMC whose MSR has been retrieved using
.Ic PMC_OP_PMCX86GETMSR .
.It Bq Er ESRCH
A process issued a PMC operation request without having allocated any PMCs.
A process issued a PMC operation request without having allocated any
PMCs.
.It Bq Er ESRCH
A process issued a PMC operation request after the PMC was detached
from all of its target processes.
.It Bq Er ESRCH
A
.Ic PMC_OP_PMCATTACH
@ -580,9 +669,6 @@ operation is not being monitored by the
driver.
.El
.Sh BUGS
The kernel driver requires all CPUs in an SMP system to be symmetric
with respect to their performance monitoring counter resources.
.Pp
The driver samples the state of the kernel's logical processor support
at the time of initialization (i.e., at module load time).
On CPUs supporting logical processors, the driver could misbehave if
@ -591,6 +677,7 @@ driver is active.
.Sh SEE ALSO
.Xr kenv 1 ,
.Xr pmc 3 ,
.Xr kgmon 8 ,
.Xr kldload 8 ,
.Xr pmccontrol 8 ,
.Xr pmcstat 8 ,

View File

@ -7,4 +7,17 @@
#ifndef _MACHINE_PMC_MDEP_H_
#define _MACHINE_PMC_MDEP_H_
union pmc_md_op_pmcallocate {
uint64_t __pad[4];
};
/* Logging */
#define PMCLOG_READADDR PMCLOG_READ64
#define PMCLOG_EMITADDR PMCLOG_EMIT64
#if _KERNEL
union pmc_md_pmc {
};
#endif
#endif /* !_MACHINE_PMC_MDEP_H_ */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2003, Joseph Koshy
* Copyright (c) 2003-2005 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -31,46 +31,33 @@
#ifndef _MACHINE_PMC_MDEP_H
#define _MACHINE_PMC_MDEP_H 1
#include <machine/specialreg.h>
#include <dev/hwpmc/hwpmc_amd.h>
#include <dev/hwpmc/hwpmc_piv.h>
/* AMD K7 PMCs */
union pmc_md_op_pmcallocate {
struct pmc_md_amd_op_pmcallocate pm_amd;
struct pmc_md_p4_op_pmcallocate pm_p4;
uint64_t __pad[4];
};
#define K8_NPMCS 5 /* 1 TSC + 4 PMCs */
/* Logging */
#define PMCLOG_READADDR PMCLOG_READ64
#define PMCLOG_EMITADDR PMCLOG_EMIT64
#define K8_PMC_COUNTERMASK 0xFF000000
#define K8_PMC_TO_COUNTER(x) (((x) << 24) & K8_PMC_COUNTERMASK)
#define K8_PMC_INVERT (1 << 23)
#define K8_PMC_ENABLE (1 << 22)
#define K8_PMC_INT (1 << 20)
#define K8_PMC_PC (1 << 19)
#define K8_PMC_EDGE (1 << 18)
#define K8_PMC_OS (1 << 17)
#define K8_PMC_USR (1 << 16)
#ifdef _KERNEL
#define K8_PMC_UNITMASK_M 0x10
#define K8_PMC_UNITMASK_O 0x08
#define K8_PMC_UNITMASK_E 0x04
#define K8_PMC_UNITMASK_S 0x02
#define K8_PMC_UNITMASK_I 0x01
#define K8_PMC_UNITMASK_MOESI 0x1F
union pmc_md_pmc {
struct pmc_md_amd_pmc pm_amd;
struct pmc_md_p4_pmc pm_p4;
};
#define K8_PMC_UNITMASK 0xFF00
#define K8_PMC_EVENTMASK 0x00FF
#define K8_PMC_TO_UNITMASK(x) (((x) << 8) & K8_PMC_UNITMASK)
#define K8_PMC_TO_EVENTMASK(x) ((x) & 0xFF)
#define K8_VALID_BITS (K8_PMC_COUNTERMASK | K8_PMC_INVERT | \
K8_PMC_ENABLE | K8_PMC_INT | K8_PMC_PC | K8_PMC_EDGE | K8_PMC_OS | \
K8_PMC_USR | K8_PMC_UNITMASK | K8_PMC_EVENTMASK)
#ifdef _KERNEL
struct pmc;
/*
* Prototypes
*/
#if defined(__amd64__)
struct pmc_mdep *pmc_amd_initialize(void);
#endif /* defined(__i386__) */
void pmc_x86_lapic_enable_pmc_interrupt(void);
#endif /* _KERNEL */
#endif
#endif /* _MACHINE_PMC_MDEP_H */

View File

@ -7,4 +7,18 @@
#ifndef _MACHINE_PMC_MDEP_H_
#define _MACHINE_PMC_MDEP_H_
union pmc_md_op_pmcallocate {
uint64_t __pad[4];
};
/* Logging */
#define PMCLOG_READADDR PMCLOG_READ32
#define PMCLOG_EMITADDR PMCLOG_EMIT32
#if _KERNEL
union pmc_md_pmc {
};
#endif
#endif /* !_MACHINE_PMC_MDEP_H_ */

View File

@ -1070,6 +1070,7 @@ gnu/ext2fs/ext2_vnops.c optional ext2fs
# Support for hardware performance monitoring counters
#
dev/hwpmc/hwpmc_mod.c optional hwpmc
dev/hwpmc/hwpmc_logging.c optional hwpmc
#
# isdn4bsd device drivers
#

View File

@ -179,6 +179,7 @@ dev/fb/splash.c optional splash
dev/fb/vga.c optional vga
dev/fdc/fdc.c optional fdc
dev/fdc/fdc_isa.c optional fdc isa
dev/hwpmc/hwpmc_alpha.c optional hwpmc
dev/kbd/atkbd.c optional atkbd
dev/kbd/atkbdc.c optional atkbdc
dev/kbd/kbd.c optional atkbd

View File

@ -147,6 +147,8 @@ dev/fdc/fdc_acpi.c optional fdc
dev/fdc/fdc_isa.c optional fdc isa
dev/fdc/fdc_pccard.c optional fdc pccard
dev/hwpmc/hwpmc_amd.c optional hwpmc
dev/hwpmc/hwpmc_piv.c optional hwpmc
dev/hwpmc/hwpmc_x86.c optional hwpmc
dev/kbd/atkbd.c optional atkbd
dev/kbd/atkbdc.c optional atkbdc
dev/kbd/kbd.c optional atkbd

View File

@ -46,6 +46,7 @@ arm/arm/vm_machdep.c standard
arm/fpe-arm/armfpe_glue.S optional armfpe
arm/fpe-arm/armfpe_init.c optional armfpe
arm/fpe-arm/armfpe.S optional armfpe
dev/hwpmc/hwpmc_arm.c optional hwpmc
geom/geom_bsd.c standard
geom/geom_bsd_enc.c standard
geom/geom_mbr.c standard

View File

@ -174,10 +174,10 @@ dev/hptmv/gui_lib.c optional hptmv
dev/hptmv/hptproc.c optional hptmv
dev/hptmv/ioctl.c optional hptmv
dev/hwpmc/hwpmc_amd.c optional hwpmc
dev/hwpmc/hwpmc_intel.c optional hwpmc
dev/hwpmc/hwpmc_pentium.c optional hwpmc
dev/hwpmc/hwpmc_piv.c optional hwpmc
dev/hwpmc/hwpmc_ppro.c optional hwpmc
dev/hwpmc/hwpmc_x86.c optional hwpmc
dev/ichwd/ichwd.c optional ichwd
dev/if_ndis/if_ndis.c optional ndis
dev/if_ndis/if_ndis_pccard.c optional ndis pccard

View File

@ -52,6 +52,7 @@ dev/fb/fb.c optional fb
dev/fb/fb.c optional vga
dev/fb/splash.c optional splash
dev/fb/vga.c optional vga
dev/hwpmc/hwpmc_ia64.c optional hwpmc
dev/kbd/atkbd.c optional atkbd
dev/kbd/atkbdc.c optional atkbdc
dev/kbd/kbd.c optional atkbd

View File

@ -118,10 +118,10 @@ dev/fb/splash.c optional splash
dev/fe/if_fe_cbus.c optional fe isa
dev/fe/if_fe_pccard.c optional fe card
dev/hwpmc/hwpmc_amd.c optional hwpmc
dev/hwpmc/hwpmc_intel.c optional hwpmc
dev/hwpmc/hwpmc_pentium.c optional hwpmc
dev/hwpmc/hwpmc_piv.c optional hwpmc
dev/hwpmc/hwpmc_ppro.c optional hwpmc
dev/hwpmc/hwpmc_x86.c optional hwpmc
dev/io/iodev.c optional io
dev/kbd/kbd.c optional pckbd
dev/kbd/kbd.c optional sc

View File

@ -15,6 +15,7 @@ font.h optional sc \
clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8"
dev/fb/fb.c optional sc
dev/hwpmc/hwpmc_powerpc.c optional hwpmc
dev/kbd/kbd.c optional sc
dev/syscons/scgfbrndr.c optional sc
dev/syscons/schistory.c optional sc

View File

@ -24,6 +24,7 @@ dev/fb/creator.c optional creator sc
dev/fb/fb.c optional sc
dev/fb/machfb.c optional machfb sc
dev/fb/splash.c optional splash
dev/hwpmc/hwpmc_sparc64.c optional hwpmc
dev/kbd/kbd.c optional sc
dev/kbd/kbd.c optional ukbd
dev/ofw/ofw_bus_if.m standard

View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/pmc.h>
#include <machine/pmc_mdep.h>
struct pmc_mdep *
pmc_md_initialize()
{
return NULL;
}

View File

@ -38,93 +38,13 @@ __FBSDID("$FreeBSD$");
#include <sys/smp.h>
#include <sys/systm.h>
#include <machine/cpufunc.h>
#include <machine/md_var.h>
#include <machine/pmc_mdep.h>
#include <machine/specialreg.h>
/* AMD K7 and K8 PMCs */
#define AMD_PMC_EVSEL_0 0xC0010000
#define AMD_PMC_EVSEL_1 0xC0010001
#define AMD_PMC_EVSEL_2 0xC0010002
#define AMD_PMC_EVSEL_3 0xC0010003
#define AMD_PMC_PERFCTR_0 0xC0010004
#define AMD_PMC_PERFCTR_1 0xC0010005
#define AMD_PMC_PERFCTR_2 0xC0010006
#define AMD_PMC_PERFCTR_3 0xC0010007
#define K7_VALID_EVENT_CODE(c) (((c) >= 0x40 && (c) <= 0x47) || \
((c) >= 0x80 && (c) <= 0x85) || ((c) >= 0xC0 && (c) <= 0xC7) || \
((c) >= 0xCD && (c) <= 0xCF))
#define AMD_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | \
PMC_CAP_SYSTEM | PMC_CAP_EDGE | PMC_CAP_THRESHOLD | \
PMC_CAP_READ | PMC_CAP_WRITE | PMC_CAP_INVERT | PMC_CAP_QUALIFIER)
/* reserved bits include bit 21 and the top two bits of the unit mask */
#define K7_PMC_RESERVED ((1 << 21) | (3 << 13))
#define K8_PMC_RESERVED (1 << 21)
#define AMD_PMC_IS_STOPPED(evsel) ((rdmsr((evsel)) & AMD_PMC_ENABLE) == 0)
#define AMD_PMC_HAS_OVERFLOWED(pmc) ((rdpmc(pmc) & (1ULL << 47)) == 0)
#if __i386__
#define AMD_NPMCS K7_NPMCS
#define AMD_PMC_CLASS PMC_CLASS_K7
#define AMD_PMC_COUNTERMASK K7_PMC_COUNTERMASK
#define AMD_PMC_TO_COUNTER(x) K7_PMC_TO_COUNTER(x)
#define AMD_PMC_INVERT K7_PMC_INVERT
#define AMD_PMC_ENABLE K7_PMC_ENABLE
#define AMD_PMC_INT K7_PMC_INT
#define AMD_PMC_PC K7_PMC_PC
#define AMD_PMC_EDGE K7_PMC_EDGE
#define AMD_PMC_OS K7_PMC_OS
#define AMD_PMC_USR K7_PMC_USR
#define AMD_PMC_UNITMASK_M K7_PMC_UNITMASK_M
#define AMD_PMC_UNITMASK_O K7_PMC_UNITMASK_O
#define AMD_PMC_UNITMASK_E K7_PMC_UNITMASK_E
#define AMD_PMC_UNITMASK_S K7_PMC_UNITMASK_S
#define AMD_PMC_UNITMASK_I K7_PMC_UNITMASK_I
#define AMD_PMC_UNITMASK K7_PMC_UNITMASK
#define AMD_PMC_EVENTMASK K7_PMC_EVENTMASK
#define AMD_PMC_TO_UNITMASK(x) K7_PMC_TO_UNITMASK(x)
#define AMD_PMC_TO_EVENTMASK(x) K7_PMC_TO_EVENTMASK(x)
#define AMD_VALID_BITS K7_VALID_BITS
#define AMD_PMC_CLASS_NAME "K7-"
#elif __amd64__
#define AMD_NPMCS K8_NPMCS
#define AMD_PMC_CLASS PMC_CLASS_K8
#define AMD_PMC_COUNTERMASK K8_PMC_COUNTERMASK
#define AMD_PMC_TO_COUNTER(x) K8_PMC_TO_COUNTER(x)
#define AMD_PMC_INVERT K8_PMC_INVERT
#define AMD_PMC_ENABLE K8_PMC_ENABLE
#define AMD_PMC_INT K8_PMC_INT
#define AMD_PMC_PC K8_PMC_PC
#define AMD_PMC_EDGE K8_PMC_EDGE
#define AMD_PMC_OS K8_PMC_OS
#define AMD_PMC_USR K8_PMC_USR
#define AMD_PMC_UNITMASK_M K8_PMC_UNITMASK_M
#define AMD_PMC_UNITMASK_O K8_PMC_UNITMASK_O
#define AMD_PMC_UNITMASK_E K8_PMC_UNITMASK_E
#define AMD_PMC_UNITMASK_S K8_PMC_UNITMASK_S
#define AMD_PMC_UNITMASK_I K8_PMC_UNITMASK_I
#define AMD_PMC_UNITMASK K8_PMC_UNITMASK
#define AMD_PMC_EVENTMASK K8_PMC_EVENTMASK
#define AMD_PMC_TO_UNITMASK(x) K8_PMC_TO_UNITMASK(x)
#define AMD_PMC_TO_EVENTMASK(x) K8_PMC_TO_EVENTMASK(x)
#define AMD_VALID_BITS K8_VALID_BITS
#define AMD_PMC_CLASS_NAME "K8-"
#else
#error Unsupported architecture.
#if DEBUG
enum pmc_class amd_pmc_class;
#endif
/* AMD K7 & K8 PMCs */
@ -134,7 +54,7 @@ struct amd_descr {
uint32_t pm_perfctr; /* address of PERFCTR register */
};
static const struct amd_descr amd_pmcdesc[AMD_NPMCS] =
static struct amd_descr amd_pmcdesc[AMD_NPMCS] =
{
{
.pm_descr =
@ -151,8 +71,8 @@ static const struct amd_descr amd_pmcdesc[AMD_NPMCS] =
{
.pm_descr =
{
.pd_name = AMD_PMC_CLASS_NAME "0",
.pd_class = AMD_PMC_CLASS,
.pd_name = "",
.pd_class = -1,
.pd_caps = AMD_PMC_CAPS,
.pd_width = 48
},
@ -162,8 +82,8 @@ static const struct amd_descr amd_pmcdesc[AMD_NPMCS] =
{
.pm_descr =
{
.pd_name = AMD_PMC_CLASS_NAME "1",
.pd_class = AMD_PMC_CLASS,
.pd_name = "",
.pd_class = -1,
.pd_caps = AMD_PMC_CAPS,
.pd_width = 48
},
@ -173,8 +93,8 @@ static const struct amd_descr amd_pmcdesc[AMD_NPMCS] =
{
.pm_descr =
{
.pd_name = AMD_PMC_CLASS_NAME "2",
.pd_class = AMD_PMC_CLASS,
.pd_name = "",
.pd_class = -1,
.pd_caps = AMD_PMC_CAPS,
.pd_width = 48
},
@ -184,8 +104,8 @@ static const struct amd_descr amd_pmcdesc[AMD_NPMCS] =
{
.pm_descr =
{
.pd_name = AMD_PMC_CLASS_NAME "3",
.pd_class = AMD_PMC_CLASS,
.pd_name = "",
.pd_class = -1,
.pd_caps = AMD_PMC_CAPS,
.pd_width = 48
},
@ -201,12 +121,12 @@ struct amd_event_code_map {
};
const struct amd_event_code_map amd_event_codes[] = {
#if __i386__
#if defined(__i386__) /* 32 bit Athlon (K7) only */
{ PMC_EV_K7_DC_ACCESSES, 0x40, 0 },
{ PMC_EV_K7_DC_MISSES, 0x41, 0 },
{ PMC_EV_K7_DC_REFILLS_FROM_L2, 0x42, K7_PMC_UNITMASK_MOESI },
{ PMC_EV_K7_DC_REFILLS_FROM_SYSTEM, 0x43, K7_PMC_UNITMASK_MOESI },
{ PMC_EV_K7_DC_WRITEBACKS, 0x44, K7_PMC_UNITMASK_MOESI },
{ PMC_EV_K7_DC_REFILLS_FROM_L2, 0x42, AMD_PMC_UNITMASK_MOESI },
{ PMC_EV_K7_DC_REFILLS_FROM_SYSTEM, 0x43, AMD_PMC_UNITMASK_MOESI },
{ PMC_EV_K7_DC_WRITEBACKS, 0x44, AMD_PMC_UNITMASK_MOESI },
{ PMC_EV_K7_L1_DTLB_MISS_AND_L2_DTLB_HITS, 0x45, 0 },
{ PMC_EV_K7_L1_AND_L2_DTLB_MISSES, 0x46, 0 },
{ PMC_EV_K7_MISALIGNED_REFERENCES, 0x47, 0 },
@ -227,10 +147,9 @@ const struct amd_event_code_map amd_event_codes[] = {
{ PMC_EV_K7_RETIRED_RESYNC_BRANCHES, 0xC7, 0 },
{ PMC_EV_K7_INTERRUPTS_MASKED_CYCLES, 0xCD, 0 },
{ PMC_EV_K7_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES, 0xCE, 0 },
{ PMC_EV_K7_HARDWARE_INTERRUPTS, 0xCF, 0 }
{ PMC_EV_K7_HARDWARE_INTERRUPTS, 0xCF, 0 },
#endif
#if __amd64__
{ PMC_EV_K8_FP_DISPATCHED_FPU_OPS, 0x00, 0x3F },
{ PMC_EV_K8_FP_CYCLES_WITH_NO_FPU_OPS_RETIRED, 0x01, 0x00 },
{ PMC_EV_K8_FP_DISPATCHED_FPU_FAST_FLAG_OPS, 0x02, 0x00 },
@ -327,7 +246,6 @@ const struct amd_event_code_map amd_event_codes[] = {
{ PMC_EV_K8_NB_HT_BUS0_BANDWIDTH, 0xF6, 0x0F },
{ PMC_EV_K8_NB_HT_BUS1_BANDWIDTH, 0xF7, 0x0F },
{ PMC_EV_K8_NB_HT_BUS2_BANDWIDTH, 0xF8, 0x0F }
#endif
};
@ -373,13 +291,15 @@ amd_read_pmc(int cpu, int ri, pmc_value_t *v)
return 0;
}
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
#if DEBUG
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
("[amd,%d] unknown PMC class (%d)", __LINE__,
pd->pm_descr.pd_class));
#endif
tmp = rdmsr(pd->pm_perfctr); /* RDMSR serializes */
if (PMC_IS_SAMPLING_MODE(mode))
*v = -tmp;
*v = AMD_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp);
else
*v = tmp;
@ -418,13 +338,15 @@ amd_write_pmc(int cpu, int ri, pmc_value_t v)
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
return 0;
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
#if DEBUG
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
("[amd,%d] unknown PMC class (%d)", __LINE__,
pd->pm_descr.pd_class));
#endif
/* use 2's complement of the count for sampling mode PMCs */
if (PMC_IS_SAMPLING_MODE(mode))
v = -v;
v = AMD_RELOAD_COUNT_TO_PERFCTR_VALUE(v);
PMCDBG(MDP,WRI,1,"amd-write cpu=%d ri=%d v=%jx", cpu, ri, v);
@ -552,8 +474,10 @@ amd_allocate_pmc(int cpu, int ri, struct pmc *pm,
return 0;
}
KASSERT(pd->pd_class == AMD_PMC_CLASS,
#if DEBUG
KASSERT(pd->pd_class == amd_pmc_class,
("[amd,%d] Unknown PMC class (%d)", __LINE__, pd->pd_class));
#endif
pe = a->pm_ev;
@ -570,7 +494,7 @@ amd_allocate_pmc(int cpu, int ri, struct pmc *pm,
if (i == amd_event_codes_size)
return EINVAL;
unitmask = a->pm_amd_config & AMD_PMC_UNITMASK;
unitmask = a->pm_md.pm_amd.pm_amd_config & AMD_PMC_UNITMASK;
if (unitmask & ~allowed_unitmask) /* disallow reserved bits */
return EINVAL;
@ -578,7 +502,7 @@ amd_allocate_pmc(int cpu, int ri, struct pmc *pm,
config |= unitmask;
if (caps & PMC_CAP_THRESHOLD)
config |= a->pm_amd_config & AMD_PMC_COUNTERMASK;
config |= a->pm_md.pm_amd.pm_amd_config & AMD_PMC_COUNTERMASK;
/* set at least one of the 'usr' or 'os' caps */
if (caps & PMC_CAP_USER)
@ -631,7 +555,7 @@ amd_release_pmc(int cpu, int ri, struct pmc *pmc)
#if DEBUG
pd = &amd_pmcdesc[ri];
if (pd->pm_descr.pd_class == AMD_PMC_CLASS)
if (pd->pm_descr.pd_class == amd_pmc_class)
KASSERT(AMD_PMC_IS_STOPPED(pd->pm_evsel),
("[amd,%d] PMC %d released while active", __LINE__, ri));
#endif
@ -669,9 +593,11 @@ amd_start_pmc(int cpu, int ri)
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
return 0; /* TSCs are always running */
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
#if DEBUG
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
("[amd,%d] unknown PMC class (%d)", __LINE__,
pd->pm_descr.pd_class));
#endif
KASSERT(AMD_PMC_IS_STOPPED(pd->pm_evsel),
("[amd,%d] pmc%d,cpu%d: Starting active PMC \"%s\"", __LINE__,
@ -715,9 +641,11 @@ amd_stop_pmc(int cpu, int ri)
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
return 0;
KASSERT(pd->pm_descr.pd_class == AMD_PMC_CLASS,
#if DEBUG
KASSERT(pd->pm_descr.pd_class == amd_pmc_class,
("[amd,%d] unknown PMC class (%d)", __LINE__,
pd->pm_descr.pd_class));
#endif
KASSERT(!AMD_PMC_IS_STOPPED(pd->pm_evsel),
("[amd,%d] PMC%d, CPU%d \"%s\" already stopped",
@ -741,18 +669,18 @@ amd_stop_pmc(int cpu, int ri)
static int
amd_intr(int cpu, uintptr_t eip, int usermode)
{
int i, retval;
enum pmc_mode mode;
uint32_t perfctr;
int i, error, retval, ri;
uint32_t config, evsel, perfctr;
struct pmc *pm;
struct pmc_cpu *pc;
struct pmc_hw *phw;
(void) usermode;
pmc_value_t v;
KASSERT(cpu >= 0 && cpu < mp_ncpus,
("[amd,%d] out of range CPU %d", __LINE__, cpu));
PMCDBG(MDP,INT,1, "cpu=%d eip=%p", cpu, (void *) eip);
retval = 0;
pc = pmc_pcpu[cpu];
@ -760,36 +688,53 @@ amd_intr(int cpu, uintptr_t eip, int usermode)
/*
* look for all PMCs that have interrupted:
* - skip over the TSC [PMC#0]
* - look for a PMC with a valid 'struct pmc' association
* - look for a PMC in (a) sampling mode and (b) which has
* overflowed. If found, we update the process's
* histogram or send it a profiling signal by calling
* the appropriate helper function.
* - look for a running, sampling PMC which has overflowed
* and which has a valid 'struct pmc' association
*
* If found, we call a helper to process the interrupt.
*/
for (i = 1; i < AMD_NPMCS; i++) {
for (i = 0; i < AMD_NPMCS-1; i++) {
ri = i + 1; /* row index; TSC is at ri == 0 */
if (!AMD_PMC_HAS_OVERFLOWED(i))
continue;
phw = pc->pc_hwpmcs[ri];
phw = pc->pc_hwpmcs[i];
perfctr = amd_pmcdesc[i].pm_perfctr;
KASSERT(phw != NULL, ("[amd,%d] null PHW pointer", __LINE__));
if ((pm = phw->phw_pmc) == NULL ||
pm->pm_state != PMC_STATE_RUNNING) {
atomic_add_int(&pmc_stats.pm_intr_ignored, 1);
pm->pm_state != PMC_STATE_RUNNING ||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
continue;
}
mode = PMC_TO_MODE(pm);
if (PMC_IS_SAMPLING_MODE(mode) &&
AMD_PMC_HAS_OVERFLOWED(perfctr)) {
atomic_add_int(&pmc_stats.pm_intr_processed, 1);
if (PMC_IS_SYSTEM_MODE(mode))
pmc_update_histogram(phw, eip);
else if (PMC_IS_VIRTUAL_MODE(mode))
pmc_send_signal(pm);
retval = 1;
}
/* stop the PMC, reload count */
evsel = AMD_PMC_EVSEL_0 + i;
perfctr = AMD_PMC_PERFCTR_0 + i;
v = pm->pm_sc.pm_reloadcount;
config = rdmsr(evsel);
KASSERT((config & ~AMD_PMC_ENABLE) ==
(pm->pm_md.pm_amd.pm_amd_evsel & ~AMD_PMC_ENABLE),
("[amd,%d] config mismatch reg=0x%x pm=0x%x", __LINE__,
config, pm->pm_md.pm_amd.pm_amd_evsel));
wrmsr(evsel, config & ~AMD_PMC_ENABLE);
wrmsr(perfctr, AMD_RELOAD_COUNT_TO_PERFCTR_VALUE(v));
/* restart if there was no error during logging */
error = pmc_process_interrupt(cpu, pm, eip, usermode);
if (error == 0)
wrmsr(evsel, config | AMD_PMC_ENABLE);
retval = 1; /* found an interrupting PMC */
}
if (retval == 0)
atomic_add_int(&pmc_stats.pm_intr_ignored, 1);
return retval;
}
@ -881,9 +826,6 @@ amd_init(int cpu)
MALLOC(pcs, struct amd_cpu *, sizeof(struct amd_cpu), M_PMC,
M_WAITOK|M_ZERO);
if (pcs == NULL)
return ENOMEM;
phw = &pcs->pc_amdpmcs[0];
/*
@ -938,7 +880,8 @@ amd_cleanup(int cpu)
* Next, free up allocated space.
*/
pcs = pmc_pcpu[cpu];
if ((pcs = pmc_pcpu[cpu]) == NULL)
return 0;
#if DEBUG
/* check the TSC */
@ -951,8 +894,6 @@ amd_cleanup(int cpu)
("[amd,%d] CPU%d/PMC%d not stopped", __LINE__, cpu, i));
}
#endif
KASSERT(pcs != NULL,
("[amd,%d] null per-cpu state pointer (cpu%d)", __LINE__, cpu));
pmc_pcpu[cpu] = NULL;
FREE(pcs, M_PMC);
@ -966,30 +907,47 @@ amd_cleanup(int cpu)
struct pmc_mdep *
pmc_amd_initialize(void)
{
enum pmc_cputype cputype;
enum pmc_class class;
struct pmc_mdep *pmc_mdep;
char *name;
int i;
/* The presence of hardware performance counters on the AMD
Athlon, Duron or later processors, is _not_ indicated by
any of the processor feature flags set by the 'CPUID'
instruction, so we only check the 'instruction family'
field returned by CPUID for instruction family >= 6. This
test needs to be be refined. */
/*
* The presence of hardware performance counters on the AMD
* Athlon, Duron or later processors, is _not_ indicated by
* any of the processor feature flags set by the 'CPUID'
* instruction, so we only check the 'instruction family'
* field returned by CPUID for instruction family >= 6.
*/
if ((cpu_id & 0xF00) < 0x600)
cputype = -1;
switch (cpu_id & 0xF00) {
case 0x600: /* Athlon(tm) processor */
cputype = PMC_CPU_AMD_K7;
class = PMC_CLASS_K7;
name = "K7";
break;
case 0xF00: /* Athlon64/Opteron processor */
cputype = PMC_CPU_AMD_K8;
class = PMC_CLASS_K8;
name = "K8";
break;
}
if ((int) cputype == -1) {
(void) printf("pmc: Unknown AMD CPU.\n");
return NULL;
}
#if DEBUG
amd_pmc_class = class;
#endif
MALLOC(pmc_mdep, struct pmc_mdep *, sizeof(struct pmc_mdep),
M_PMC, M_WAITOK|M_ZERO);
#if __i386__
pmc_mdep->pmd_cputype = PMC_CPU_AMD_K7;
#elif __amd64__
pmc_mdep->pmd_cputype = PMC_CPU_AMD_K8;
#else
#error Unknown AMD CPU type.
#endif
pmc_mdep->pmd_cputype = cputype;
pmc_mdep->pmd_npmc = AMD_NPMCS;
/* this processor has two classes of usable PMCs */
@ -1001,13 +959,21 @@ pmc_amd_initialize(void)
pmc_mdep->pmd_classes[0].pm_width = 64;
/* AMD K7/K8 PMCs */
pmc_mdep->pmd_classes[1].pm_class = AMD_PMC_CLASS;
pmc_mdep->pmd_classes[1].pm_class = class;
pmc_mdep->pmd_classes[1].pm_caps = AMD_PMC_CAPS;
pmc_mdep->pmd_classes[1].pm_width = 48;
pmc_mdep->pmd_nclasspmcs[0] = 1;
pmc_mdep->pmd_nclasspmcs[1] = (AMD_NPMCS-1);
/* fill in the correct pmc name and class */
for (i = 1; i < AMD_NPMCS; i++) {
(void) snprintf(amd_pmcdesc[i].pm_descr.pd_name,
sizeof(amd_pmcdesc[i].pm_descr.pd_name), "%s-%d",
name, i-1);
amd_pmcdesc[i].pm_descr.pd_class = class;
}
pmc_mdep->pmd_init = amd_init;
pmc_mdep->pmd_cleanup = amd_cleanup;
pmc_mdep->pmd_switch_in = amd_switch_in;

103
sys/dev/hwpmc/hwpmc_amd.h Normal file
View File

@ -0,0 +1,103 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/* Machine dependent interfaces */
#ifndef _DEV_HWPMC_AMD_H_
#define _DEV_HWPMC_AMD_H_ 1
/* AMD K7 and K8 PMCs */
#define AMD_PMC_EVSEL_0 0xC0010000
#define AMD_PMC_EVSEL_1 0xC0010001
#define AMD_PMC_EVSEL_2 0xC0010002
#define AMD_PMC_EVSEL_3 0xC0010003
#define AMD_PMC_PERFCTR_0 0xC0010004
#define AMD_PMC_PERFCTR_1 0xC0010005
#define AMD_PMC_PERFCTR_2 0xC0010006
#define AMD_PMC_PERFCTR_3 0xC0010007
#define AMD_NPMCS 5 /* 1 TSC + 4 PMCs */
#define AMD_PMC_COUNTERMASK 0xFF000000
#define AMD_PMC_TO_COUNTER(x) (((x) << 24) & AMD_PMC_COUNTERMASK)
#define AMD_PMC_INVERT (1 << 23)
#define AMD_PMC_ENABLE (1 << 22)
#define AMD_PMC_INT (1 << 20)
#define AMD_PMC_PC (1 << 19)
#define AMD_PMC_EDGE (1 << 18)
#define AMD_PMC_OS (1 << 17)
#define AMD_PMC_USR (1 << 16)
#define AMD_PMC_UNITMASK_M 0x10
#define AMD_PMC_UNITMASK_O 0x08
#define AMD_PMC_UNITMASK_E 0x04
#define AMD_PMC_UNITMASK_S 0x02
#define AMD_PMC_UNITMASK_I 0x01
#define AMD_PMC_UNITMASK_MOESI 0x1F
#define AMD_PMC_UNITMASK 0xFF00
#define AMD_PMC_EVENTMASK 0x00FF
#define AMD_PMC_TO_UNITMASK(x) (((x) << 8) & AMD_PMC_UNITMASK)
#define AMD_PMC_TO_EVENTMASK(x) ((x) & 0xFF)
#define AMD_VALID_BITS (AMD_PMC_COUNTERMASK | AMD_PMC_INVERT | \
AMD_PMC_ENABLE | AMD_PMC_INT | AMD_PMC_PC | AMD_PMC_EDGE | \
AMD_PMC_OS | AMD_PMC_USR | AMD_PMC_UNITMASK | AMD_PMC_EVENTMASK)
#define AMD_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | \
PMC_CAP_SYSTEM | PMC_CAP_EDGE | PMC_CAP_THRESHOLD | \
PMC_CAP_READ | PMC_CAP_WRITE | PMC_CAP_INVERT | PMC_CAP_QUALIFIER)
#define AMD_PMC_IS_STOPPED(evsel) ((rdmsr((evsel)) & AMD_PMC_ENABLE) == 0)
#define AMD_PMC_HAS_OVERFLOWED(pmc) ((rdpmc(pmc) & (1ULL << 47)) == 0)
#define AMD_RELOAD_COUNT_TO_PERFCTR_VALUE(V) (-(V))
#define AMD_PERFCTR_VALUE_TO_RELOAD_COUNT(P) (-(P))
struct pmc_md_amd_op_pmcallocate {
uint32_t pm_amd_config;
};
#ifdef _KERNEL
/* MD extension for 'struct pmc' */
struct pmc_md_amd_pmc {
uint32_t pm_amd_evsel;
};
/*
* Prototypes
*/
struct pmc_mdep *pmc_amd_initialize(void); /* AMD K7/K8 PMCs */
#endif /* _KERNEL */
#endif /* _DEV_HWPMC_AMD_H_ */

40
sys/dev/hwpmc/hwpmc_arm.c Normal file
View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/pmc.h>
#include <machine/pmc_mdep.h>
struct pmc_mdep *
pmc_md_initialize()
{
return NULL;
}

View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/pmc.h>
#include <machine/pmc_mdep.h>
struct pmc_mdep *
pmc_md_initialize()
{
return NULL;
}

View File

@ -0,0 +1,978 @@
/*-
* Copyright (c) 2005 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* Logging code for hwpmc(4)
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/file.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/pmc.h>
#include <sys/pmclog.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
/*
* Sysctl tunables
*/
SYSCTL_DECL(_kern_hwpmc);
/*
* kern.hwpmc.logbuffersize -- size of the per-cpu owner buffers.
*/
static int pmclog_buffer_size = PMC_LOG_BUFFER_SIZE;
TUNABLE_INT(PMC_SYSCTL_NAME_PREFIX "logbuffersize", &pmclog_buffer_size);
SYSCTL_INT(_kern_hwpmc, OID_AUTO, logbuffersize, CTLFLAG_TUN|CTLFLAG_RD,
&pmclog_buffer_size, 0, "size of log buffers in kilobytes");
/*
* kern.hwpmc.nbuffer -- number of global log buffers
*/
static int pmc_nlogbuffers = PMC_NLOGBUFFERS;
TUNABLE_INT(PMC_SYSCTL_NAME_PREFIX "nbuffers", &pmc_nlogbuffers);
SYSCTL_INT(_kern_hwpmc, OID_AUTO, nbuffers, CTLFLAG_TUN|CTLFLAG_RD,
&pmc_nlogbuffers, 0, "number of global log buffers");
/*
* Global log buffer list and associated spin lock.
*/
TAILQ_HEAD(, pmclog_buffer) pmc_bufferlist =
TAILQ_HEAD_INITIALIZER(pmc_bufferlist);
static struct mtx pmc_bufferlist_mtx; /* spin lock */
static struct mtx pmc_kthread_mtx; /* sleep lock */
#define PMCLOG_INIT_BUFFER_DESCRIPTOR(D) do { \
const int __roundup = roundup(sizeof(*D), \
sizeof(uint32_t)); \
(D)->plb_fence = ((char *) (D)) + \
1024*pmclog_buffer_size; \
(D)->plb_base = (D)->plb_ptr = ((char *) (D)) + \
__roundup; \
} while (0)
/*
* Log file record constructors.
*/
/* reserve LEN bytes of space and initialize the entry header */
#define _PMCLOG_RESERVE(PO,TYPE,LEN,ACTION) do { \
uint32_t *_le; \
int _len = roundup((LEN), sizeof(uint32_t)); \
if ((_le = pmclog_reserve((PO), _len)) == NULL) { \
ACTION; \
} \
*_le = (PMCLOG_HEADER_MAGIC << 24) | \
(PMCLOG_TYPE_ ## TYPE << 16) | \
(_len & 0xFFFF); \
_le += 3 /* skip over timestamp */
#define PMCLOG_RESERVE(P,T,L) _PMCLOG_RESERVE(P,T,L,return)
#define PMCLOG_RESERVE_WITH_ERROR(P,T,L) _PMCLOG_RESERVE(P,T,L, \
error=ENOMEM;goto error)
#define PMCLOG_EMIT32(V) do { *_le++ = (V); } while (0)
#define PMCLOG_EMIT64(V) do { \
*_le++ = (uint32_t) ((V) & 0xFFFFFFFF); \
*_le++ = (uint32_t) (((V) >> 32) & 0xFFFFFFFF); \
} while (0)
/* Emit a string. Caution: does NOT update _le, so needs to be last */
#define PMCLOG_EMITSTRING(S,L) do { bcopy((S), _le, (L)); } while (0)
#define PMCLOG_DESPATCH(PO) \
pmclog_release((PO)); \
} while (0)
/*
* Assertions about the log file format.
*/
CTASSERT(sizeof(struct pmclog_closelog) == 3*4);
CTASSERT(sizeof(struct pmclog_dropnotify) == 3*4);
CTASSERT(sizeof(struct pmclog_mappingchange) == PATH_MAX +
5*4 + 2*sizeof(uintfptr_t));
CTASSERT(offsetof(struct pmclog_mappingchange,pl_pathname) ==
5*4 + 2*sizeof(uintfptr_t));
CTASSERT(sizeof(struct pmclog_pcsample) == 5*4 + sizeof(uintfptr_t));
CTASSERT(sizeof(struct pmclog_pmcallocate) == 6*4);
CTASSERT(sizeof(struct pmclog_pmcattach) == 5*4 + PATH_MAX);
CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 5*4);
CTASSERT(sizeof(struct pmclog_pmcdetach) == 5*4);
CTASSERT(sizeof(struct pmclog_proccsw) == 5*4 + 8);
CTASSERT(sizeof(struct pmclog_procexec) == 4*4 + PATH_MAX);
CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 4*4);
CTASSERT(sizeof(struct pmclog_procexit) == 5*4 + 8);
CTASSERT(sizeof(struct pmclog_procfork) == 5*4);
CTASSERT(sizeof(struct pmclog_sysexit) == 4*4);
CTASSERT(sizeof(struct pmclog_userdata) == 4*4);
/*
* Log buffer structure
*/
struct pmclog_buffer {
TAILQ_ENTRY(pmclog_buffer) plb_next;
char *plb_base;
char *plb_ptr;
char *plb_fence;
};
/*
* Prototypes
*/
static int pmclog_get_buffer(struct pmc_owner *po);
static void pmclog_loop(void *arg);
static void pmclog_release(struct pmc_owner *po);
static uint32_t *pmclog_reserve(struct pmc_owner *po, int length);
static void pmclog_schedule_io(struct pmc_owner *po);
static void pmclog_stop_kthread(struct pmc_owner *po);
/*
* Helper functions
*/
/*
* Get a log buffer
*/
static int
pmclog_get_buffer(struct pmc_owner *po)
{
struct pmclog_buffer *plb;
mtx_assert(&po->po_mtx, MA_OWNED);
KASSERT(po->po_curbuf == NULL,
("[pmc,%d] po=%p current buffer still valid", __LINE__, po));
mtx_lock_spin(&pmc_bufferlist_mtx);
if ((plb = TAILQ_FIRST(&pmc_bufferlist)) != NULL)
TAILQ_REMOVE(&pmc_bufferlist, plb, plb_next);
mtx_unlock_spin(&pmc_bufferlist_mtx);
PMCDBG(LOG,GTB,1, "po=%p plb=%p", po, plb);
#if DEBUG
if (plb)
KASSERT(plb->plb_ptr == plb->plb_base &&
plb->plb_base < plb->plb_fence,
("[pmc,%d] po=%p buffer invariants: ptr=%p "
"base=%p fence=%p", __LINE__, po, plb->plb_ptr,
plb->plb_base, plb->plb_fence));
#endif
po->po_curbuf = plb;
/* update stats */
atomic_add_int(&pmc_stats.pm_buffer_requests, 1);
if (plb == NULL)
atomic_add_int(&pmc_stats.pm_buffer_requests_failed, 1);
return plb ? 0 : ENOMEM;
}
/*
* Log handler loop.
*
* This function is executed by each pmc owner's helper thread.
*/
static void
pmclog_loop(void *arg)
{
int error;
struct pmc_owner *po;
struct pmclog_buffer *lb;
struct ucred *ownercred;
struct ucred *mycred;
struct thread *td;
struct uio auio;
struct iovec aiov;
size_t nbytes;
po = (struct pmc_owner *) arg;
td = curthread;
mycred = td->td_ucred;
PROC_LOCK(po->po_owner);
ownercred = crhold(po->po_owner->p_ucred);
PROC_UNLOCK(po->po_owner);
PMCDBG(LOG,INI,1, "po=%p kt=%p", po, po->po_kthread);
KASSERT(po->po_kthread == curthread->td_proc,
("[pmc,%d] proc mismatch po=%p po/kt=%p curproc=%p", __LINE__,
po, po->po_kthread, curthread->td_proc));
lb = NULL;
/*
* Loop waiting for I/O requests to be added to the owner
* struct's queue. The loop is exited when the log file
* is deconfigured.
*/
mtx_lock(&pmc_kthread_mtx);
for (;;) {
/* check if we've been asked to exit */
if ((po->po_flags & PMC_PO_OWNS_LOGFILE) == 0)
break;
if (lb == NULL) { /* look for a fresh buffer to write */
mtx_lock_spin(&po->po_mtx);
if ((lb = TAILQ_FIRST(&po->po_logbuffers)) == NULL) {
mtx_unlock_spin(&po->po_mtx);
/* wakeup any processes waiting for a FLUSH */
if (po->po_flags & PMC_PO_IN_FLUSH) {
po->po_flags &= ~PMC_PO_IN_FLUSH;
wakeup_one(po->po_kthread);
}
(void) msleep(po, &pmc_kthread_mtx, PWAIT,
"pmcloop", 0);
continue;
}
TAILQ_REMOVE(&po->po_logbuffers, lb, plb_next);
mtx_unlock_spin(&po->po_mtx);
}
mtx_unlock(&pmc_kthread_mtx);
/* process the request */
PMCDBG(LOG,WRI,2, "po=%p base=%p ptr=%p", po,
lb->plb_base, lb->plb_ptr);
/* change our thread's credentials before issuing the I/O */
aiov.iov_base = lb->plb_base;
aiov.iov_len = nbytes = lb->plb_ptr - lb->plb_base;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = -1;
auio.uio_resid = nbytes;
auio.uio_rw = UIO_WRITE;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_td = td;
/* switch thread credentials -- see kern_ktrace.c */
td->td_ucred = ownercred;
error = fo_write(po->po_file, &auio, ownercred, 0, td);
td->td_ucred = mycred;
mtx_lock(&pmc_kthread_mtx);
if (error) {
/* XXX some errors are recoverable */
/* XXX also check for SIGPIPE if a socket */
/* send a SIGIO to the owner and exit */
PROC_LOCK(po->po_owner);
psignal(po->po_owner, SIGIO);
PROC_UNLOCK(po->po_owner);
po->po_error = error; /* save for flush log */
PMCDBG(LOG,WRI,2, "po=%p error=%d", po, error);
break;
}
/* put the used buffer back into the global pool */
PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
mtx_lock_spin(&pmc_bufferlist_mtx);
TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
mtx_unlock_spin(&pmc_bufferlist_mtx);
lb = NULL;
}
po->po_kthread = NULL;
mtx_unlock(&pmc_kthread_mtx);
/* return the current I/O buffer to the global pool */
if (lb) {
PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
mtx_lock_spin(&pmc_bufferlist_mtx);
TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
mtx_unlock_spin(&pmc_bufferlist_mtx);
}
/*
* Exit this thread, signalling the waiter
*/
crfree(ownercred);
kthread_exit(0);
}
/*
* Release and log entry and schedule an I/O if needed.
*/
static void
pmclog_release(struct pmc_owner *po)
{
KASSERT(po->po_curbuf->plb_ptr >= po->po_curbuf->plb_base,
("[pmc,%d] buffer invariants po=%p ptr=%p base=%p", __LINE__,
po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base));
KASSERT(po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
("[pmc,%d] buffer invariants po=%p ptr=%p fenc=%p", __LINE__,
po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_fence));
/* schedule an I/O if we've filled a buffer */
if (po->po_curbuf->plb_ptr >= po->po_curbuf->plb_fence)
pmclog_schedule_io(po);
mtx_unlock_spin(&po->po_mtx);
PMCDBG(LOG,REL,1, "po=%p", po);
}
/*
* Attempt to reserve 'length' bytes of space in an owner's log
* buffer. The function returns a pointer to 'length' bytes of space
* if there was enough space or returns NULL if no space was
* available. Non-null returns do so with the po mutex locked. The
* caller must invoke pmclog_release() on the pmc owner structure
* when done.
*/
static uint32_t *
pmclog_reserve(struct pmc_owner *po, int length)
{
char *newptr, *oldptr;
uint32_t *lh;
struct timespec ts;
PMCDBG(LOG,ALL,1, "po=%p len=%d", po, length);
KASSERT(length % sizeof(uint32_t) == 0,
("[pmclog,%d] length not a multiple of word size", __LINE__));
mtx_lock_spin(&po->po_mtx);
if (po->po_curbuf == NULL)
if (pmclog_get_buffer(po) != 0) {
mtx_unlock_spin(&po->po_mtx);
return NULL;
}
KASSERT(po->po_curbuf != NULL,
("[pmc,%d] po=%p no current buffer", __LINE__, po));
KASSERT(po->po_curbuf->plb_ptr >= po->po_curbuf->plb_base &&
po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
("[pmc,%d] po=%p buffer invariants: ptr=%p base=%p fence=%p",
__LINE__, po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base,
po->po_curbuf->plb_fence));
oldptr = po->po_curbuf->plb_ptr;
newptr = oldptr + length;
KASSERT(oldptr != NULL,
("[pmc,%d] po=%p Null log buffer pointer", __LINE__, po));
/*
* If we have space in the current buffer, return a pointer to
* available space with the PO structure locked.
*/
if (newptr <= po->po_curbuf->plb_fence) {
po->po_curbuf->plb_ptr = newptr;
goto done;
}
/* otherwise, schedule the current buffer and get a fresh buffer */
pmclog_schedule_io(po);
if (pmclog_get_buffer(po) != 0) {
mtx_unlock_spin(&po->po_mtx);
return NULL;
}
KASSERT(po->po_curbuf != NULL,
("[pmc,%d] po=%p no current buffer", __LINE__, po));
KASSERT(po->po_curbuf->plb_ptr != NULL,
("[pmc,%d] null return from pmc_get_log_buffer", __LINE__));
KASSERT(po->po_curbuf->plb_ptr == po->po_curbuf->plb_base &&
po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
("[pmc,%d] po=%p buffer invariants: ptr=%p base=%p fence=%p",
__LINE__, po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base,
po->po_curbuf->plb_fence));
oldptr = po->po_curbuf->plb_ptr;
done:
lh = (uint32_t *) oldptr; lh++;
/* fill in the timestamp */
getnanotime(&ts);
*lh++ = ts.tv_sec & 0xFFFFFFFF;
*lh++ = ts.tv_nsec & 0xFFFFFFF;
return (uint32_t *) oldptr;
}
/*
* Schedule an I/O.
*
* Transfer the current buffer to the helper kthread.
*/
static void
pmclog_schedule_io(struct pmc_owner *po)
{
KASSERT(po->po_curbuf != NULL,
("[pmc,%d] schedule_io with null buffer po=%p", __LINE__, po));
KASSERT(po->po_curbuf->plb_ptr >= po->po_curbuf->plb_base,
("[pmc,%d] buffer invariants po=%p ptr=%p base=%p", __LINE__,
po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base));
KASSERT(po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
("[pmc,%d] buffer invariants po=%p ptr=%p fenc=%p", __LINE__,
po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_fence));
PMCDBG(LOG,SIO, 1, "po=%p", po);
mtx_assert(&po->po_mtx, MA_OWNED);
/*
* Add the current buffer to the tail of the buffer list and
* wakeup the helper.
*/
TAILQ_INSERT_TAIL(&po->po_logbuffers, po->po_curbuf, plb_next);
po->po_curbuf = NULL;
wakeup_one(po);
}
/*
* Stop the helper kthread.
*/
static void
pmclog_stop_kthread(struct pmc_owner *po)
{
/*
* Unset flag, wakeup the helper thread,
* wait for it to exit
*/
mtx_assert(&pmc_kthread_mtx, MA_OWNED);
po->po_flags &= ~PMC_PO_OWNS_LOGFILE;
wakeup_one(po);
if (po->po_kthread)
msleep(po->po_kthread, &pmc_kthread_mtx, PPAUSE, "pmcdcl", 0);
}
/*
* Public functions
*/
/*
* Configure a log file for pmc owner 'po'.
*
* Parameter 'logfd' is a file handle referencing an open file in the
* owner process. This file needs to have been opened for writing.
*/
int
pmclog_configure_log(struct pmc_owner *po, int logfd)
{
int error;
struct proc *p;
PMCDBG(LOG,CFG,1, "config po=%p logfd=%d", po, logfd);
p = po->po_owner;
/* return EBUSY if a log file was already present */
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
return EBUSY;
KASSERT(po->po_kthread == NULL,
("[pmc,%d] po=%p kthread (%p) already present", __LINE__, po,
po->po_kthread));
KASSERT(po->po_file == NULL,
("[pmc,%d] po=%p file (%p) already present", __LINE__, po,
po->po_file));
/* get a reference to the file state */
error = fget_write(curthread, logfd, &po->po_file);
if (error)
goto error;
/* mark process as owning a log file */
po->po_flags |= PMC_PO_OWNS_LOGFILE;
error = kthread_create(pmclog_loop, po, &po->po_kthread,
RFHIGHPID, 0, "hwpmc: proc(%d)", p->p_pid);
if (error)
goto error;
/* mark process as using HWPMCs */
PROC_LOCK(p);
p->p_flag |= P_HWPMC;
PROC_UNLOCK(p);
/* create a log initialization entry */
PMCLOG_RESERVE_WITH_ERROR(po, INITIALIZE,
sizeof(struct pmclog_initialize));
PMCLOG_EMIT32(PMC_VERSION);
PMCLOG_EMIT32(md->pmd_cputype);
PMCLOG_DESPATCH(po);
return 0;
error:
/* shutdown the thread */
mtx_lock(&pmc_kthread_mtx);
if (po->po_kthread)
pmclog_stop_kthread(po);
mtx_unlock(&pmc_kthread_mtx);
KASSERT(po->po_kthread == NULL, ("[pmc,%d] po=%p kthread not stopped",
__LINE__, po));
if (po->po_file)
(void) fdrop(po->po_file, curthread);
po->po_file = NULL; /* clear file and error state */
po->po_error = 0;
return error;
}
/*
* De-configure a log file. This will throw away any buffers queued
* for this owner process.
*/
int
pmclog_deconfigure_log(struct pmc_owner *po)
{
int error;
struct pmclog_buffer *lb;
PMCDBG(LOG,CFG,1, "de-config po=%p", po);
if ((po->po_flags & PMC_PO_OWNS_LOGFILE) == 0)
return EINVAL;
/* remove this owner from the global SS pmc owner list */
if (po->po_sscount)
LIST_REMOVE(po, po_ssnext);
KASSERT(po->po_file != NULL,
("[pmc,%d] po=%p no log file", __LINE__, po));
/* stop the kthread, this will reset the 'OWNS_LOGFILE' flag */
mtx_lock(&pmc_kthread_mtx);
if (po->po_kthread)
pmclog_stop_kthread(po);
mtx_unlock(&pmc_kthread_mtx);
KASSERT(po->po_kthread == NULL,
("[pmc,%d] po=%p kthread not stopped", __LINE__, po));
/* return all queued log buffers to the global pool */
while ((lb = TAILQ_FIRST(&po->po_logbuffers)) != NULL) {
TAILQ_REMOVE(&po->po_logbuffers, lb, plb_next);
PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
mtx_lock_spin(&pmc_bufferlist_mtx);
TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
mtx_unlock_spin(&pmc_bufferlist_mtx);
}
/* return the 'current' buffer to the global pool */
if ((lb = po->po_curbuf) != NULL) {
PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
mtx_lock_spin(&pmc_bufferlist_mtx);
TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
mtx_unlock_spin(&pmc_bufferlist_mtx);
}
/* drop a reference to the fd */
error = fdrop(po->po_file, curthread);
po->po_file = NULL;
po->po_error = 0;
return error;
}
/*
* Flush a process' log buffer.
*/
int
pmclog_flush(struct pmc_owner *po)
{
int error, has_pending_buffers;
PMCDBG(LOG,FLS,1, "po=%p", po);
/*
* If there is a pending error recorded by the logger thread,
* return that.
*/
if (po->po_error)
return po->po_error;
error = 0;
/*
* Check that we do have an active log file.
*/
mtx_lock(&pmc_kthread_mtx);
if ((po->po_flags & PMC_PO_OWNS_LOGFILE) == 0) {
error = EINVAL;
goto error;
}
/*
* Schedule the current buffer if any.
*/
mtx_lock_spin(&po->po_mtx);
if (po->po_curbuf)
pmclog_schedule_io(po);
has_pending_buffers = !TAILQ_EMPTY(&po->po_logbuffers);
mtx_unlock_spin(&po->po_mtx);
if (has_pending_buffers) {
po->po_flags |= PMC_PO_IN_FLUSH; /* ask for a wakeup */
error = msleep(po->po_kthread, &pmc_kthread_mtx, PWAIT,
"pmcflush", 0);
}
error:
mtx_unlock(&pmc_kthread_mtx);
return error;
}
/*
* Send a 'close log' event to the log file.
*/
void
pmclog_process_closelog(struct pmc_owner *po)
{
PMCLOG_RESERVE(po,CLOSELOG,sizeof(struct pmclog_closelog));
PMCLOG_DESPATCH(po);
}
void
pmclog_process_dropnotify(struct pmc_owner *po)
{
PMCLOG_RESERVE(po,DROPNOTIFY,sizeof(struct pmclog_dropnotify));
PMCLOG_DESPATCH(po);
}
void
pmclog_process_mappingchange(struct pmc_owner *po, pid_t pid, int type,
uintfptr_t start, uintfptr_t end, char *path)
{
int pathlen, recordlen;
pathlen = strlen(path) + 1; /* #bytes for path name */
recordlen = offsetof(struct pmclog_mappingchange, pl_pathname) +
pathlen;
PMCLOG_RESERVE(po,MAPPINGCHANGE,recordlen);
PMCLOG_EMIT32(type);
PMCLOG_EMITADDR(start);
PMCLOG_EMITADDR(end);
PMCLOG_EMIT32(pid);
PMCLOG_EMITSTRING(path,pathlen);
PMCLOG_DESPATCH(po);
}
void
pmclog_process_pcsample(struct pmc *pm, struct pmc_sample *ps)
{
struct pmc_owner *po;
PMCDBG(LOG,SAM,1,"pm=%p pid=%d pc=%p", pm, ps->ps_pid,
(void *) ps->ps_pc);
po = pm->pm_owner;
PMCLOG_RESERVE(po, PCSAMPLE, sizeof(struct pmclog_pcsample));
PMCLOG_EMIT32(ps->ps_pid);
PMCLOG_EMITADDR(ps->ps_pc);
PMCLOG_EMIT32(pm->pm_id);
PMCLOG_DESPATCH(po);
}
void
pmclog_process_pmcallocate(struct pmc *pm)
{
struct pmc_owner *po;
po = pm->pm_owner;
PMCDBG(LOG,ALL,1, "pm=%p", pm);
PMCLOG_RESERVE(po, PMCALLOCATE, sizeof(struct pmclog_pmcallocate));
PMCLOG_EMIT32(pm->pm_id);
PMCLOG_EMIT32(pm->pm_event);
PMCLOG_EMIT32(pm->pm_flags);
PMCLOG_DESPATCH(po);
}
void
pmclog_process_pmcattach(struct pmc *pm, pid_t pid, char *path)
{
int pathlen, recordlen;
struct pmc_owner *po;
PMCDBG(LOG,ATT,1,"pm=%p pid=%d", pm, pid);
po = pm->pm_owner;
pathlen = strlen(path) + 1; /* #bytes for the string */
recordlen = offsetof(struct pmclog_pmcattach, pl_pathname) + pathlen;
PMCLOG_RESERVE(po, PMCATTACH, recordlen);
PMCLOG_EMIT32(pm->pm_id);
PMCLOG_EMIT32(pid);
PMCLOG_EMITSTRING(path, pathlen);
PMCLOG_DESPATCH(po);
}
void
pmclog_process_pmcdetach(struct pmc *pm, pid_t pid)
{
struct pmc_owner *po;
PMCDBG(LOG,ATT,1,"!pm=%p pid=%d", pm, pid);
po = pm->pm_owner;
PMCLOG_RESERVE(po, PMCDETACH, sizeof(struct pmclog_pmcdetach));
PMCLOG_EMIT32(pm->pm_id);
PMCLOG_EMIT32(pid);
PMCLOG_DESPATCH(po);
}
/*
* Log a context switch event to the log file.
*/
void
pmclog_process_proccsw(struct pmc *pm, struct pmc_process *pp, pmc_value_t v)
{
struct pmc_owner *po;
KASSERT(pm->pm_flags & PMC_F_LOG_PROCCSW,
("[pmclog,%d] log-process-csw called gratuitously", __LINE__));
PMCDBG(LOG,SWO,1,"pm=%p pid=%d v=%jx", pm, pp->pp_proc->p_pid,
v);
po = pm->pm_owner;
PMCLOG_RESERVE(po, PROCCSW, sizeof(struct pmclog_proccsw));
PMCLOG_EMIT32(pm->pm_id);
PMCLOG_EMIT64(v);
PMCLOG_EMIT32(pp->pp_proc->p_pid);
PMCLOG_DESPATCH(po);
}
void
pmclog_process_procexec(struct pmc_owner *po, pid_t pid, char *path)
{
int pathlen, recordlen;
PMCDBG(LOG,EXC,1,"po=%p pid=%d path=\"%s\"", po, pid, path);
pathlen = strlen(path) + 1; /* #bytes for the path */
recordlen = offsetof(struct pmclog_procexec, pl_pathname) + pathlen;
PMCLOG_RESERVE(po, PROCEXEC, recordlen);
PMCLOG_EMIT32(pid);
PMCLOG_EMITSTRING(path,pathlen);
PMCLOG_DESPATCH(po);
}
/*
* Log a process exit event (and accumulated pmc value) to the log file.
*/
void
pmclog_process_procexit(struct pmc *pm, struct pmc_process *pp)
{
int ri;
struct pmc_owner *po;
KASSERT(pm->pm_flags & PMC_F_LOG_PROCEXIT,
("[pmc,%d] log-process-exit called gratuitously", __LINE__));
ri = PMC_TO_ROWINDEX(pm);
PMCDBG(LOG,EXT,1,"pm=%p pid=%d v=%jx", pm, pp->pp_proc->p_pid,
pp->pp_pmcs[ri].pp_pmcval);
po = pm->pm_owner;
PMCLOG_RESERVE(po, PROCEXIT, sizeof(struct pmclog_procexit));
PMCLOG_EMIT32(pm->pm_id);
PMCLOG_EMIT64(pp->pp_pmcs[ri].pp_pmcval);
PMCLOG_EMIT32(pp->pp_proc->p_pid);
PMCLOG_DESPATCH(po);
}
/*
* Log a fork event.
*/
void
pmclog_process_procfork(struct pmc_owner *po, pid_t oldpid, pid_t newpid)
{
PMCLOG_RESERVE(po, PROCFORK, sizeof(struct pmclog_procfork));
PMCLOG_EMIT32(oldpid);
PMCLOG_EMIT32(newpid);
PMCLOG_DESPATCH(po);
}
/*
* Log a process exit event of the form suitable for system-wide PMCs.
*/
void
pmclog_process_sysexit(struct pmc_owner *po, pid_t pid)
{
PMCLOG_RESERVE(po, SYSEXIT, sizeof(struct pmclog_sysexit));
PMCLOG_EMIT32(pid);
PMCLOG_DESPATCH(po);
}
/*
* Write a user log entry.
*/
int
pmclog_process_userlog(struct pmc_owner *po, struct pmc_op_writelog *wl)
{
int error;
PMCDBG(LOG,WRI,1, "writelog po=%p ud=0x%x", po, wl->pm_userdata);
error = 0;
PMCLOG_RESERVE_WITH_ERROR(po, USERDATA,
sizeof(struct pmclog_userdata));
PMCLOG_EMIT32(wl->pm_userdata);
PMCLOG_DESPATCH(po);
error:
return error;
}
/*
* Initialization.
*
* Create a pool of log buffers and initialize mutexes.
*/
void
pmclog_initialize()
{
int n;
struct pmclog_buffer *plb;
if (pmclog_buffer_size <= 0) {
(void) printf("hwpmc: tunable logbuffersize=%d must be greater "
"than zero.\n", pmclog_buffer_size);
pmclog_buffer_size = PMC_LOG_BUFFER_SIZE;
}
if (pmc_nlogbuffers <= 0) {
(void) printf("hwpmc: tunable nlogbuffers=%d must be greater "
"than zero.\n", pmc_nlogbuffers);
pmc_nlogbuffers = PMC_NLOGBUFFERS;
}
/* create global pool of log buffers */
for (n = 0; n < pmc_nlogbuffers; n++) {
MALLOC(plb, struct pmclog_buffer *, 1024 * pmclog_buffer_size,
M_PMC, M_ZERO|M_WAITOK);
PMCLOG_INIT_BUFFER_DESCRIPTOR(plb);
TAILQ_INSERT_HEAD(&pmc_bufferlist, plb, plb_next);
}
mtx_init(&pmc_bufferlist_mtx, "pmc-buffer-list", "pmc", MTX_SPIN);
mtx_init(&pmc_kthread_mtx, "pmc-kthread", "pmc", MTX_DEF);
}
/*
* Shutdown logging.
*
* Destroy mutexes and release memory back the to free pool.
*/
void
pmclog_shutdown()
{
struct pmclog_buffer *plb;
mtx_destroy(&pmc_kthread_mtx);
mtx_destroy(&pmc_bufferlist_mtx);
while ((plb = TAILQ_FIRST(&pmc_bufferlist)) != NULL) {
TAILQ_REMOVE(&pmc_bufferlist, plb, plb_next);
FREE(plb, M_PMC);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -35,8 +35,9 @@ __FBSDID("$FreeBSD$");
#include <sys/smp.h>
#include <sys/systm.h>
#include <machine/cputypes.h>
#include <machine/cpufunc.h>
#include <machine/md_var.h>
#include <machine/pmc_mdep.h>
/*
* Intel Pentium PMCs

View File

@ -0,0 +1,72 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/* Machine dependent interfaces */
#ifndef _DEV_HWPMC_PENTIUM_H_
#define _DEV_HWPMC_PENTIUM_H_ 1
/* Intel Pentium PMCs */
#define PENTIUM_NPMCS 3 /* 1 TSC + 2 PMCs */
#define PENTIUM_CESR_PC1 (1 << 25)
#define PENTIUM_CESR_CC1_MASK 0x01C00000
#define PENTIUM_CESR_TO_CC1(C) (((C) & 0x07) << 22)
#define PENTIUM_CESR_ES1_MASK 0x003F0000
#define PENTIUM_CESR_TO_ES1(E) (((E) & 0x3F) << 16)
#define PENTIUM_CESR_PC0 (1 << 9)
#define PENTIUM_CESR_CC0_MASK 0x000001C0
#define PENTIUM_CESR_TO_CC0(C) (((C) & 0x07) << 6)
#define PENTIUM_CESR_ES0_MASK 0x0000003F
#define PENTIUM_CESR_TO_ES0(E) ((E) & 0x3F)
#define PENTIUM_CESR_RESERVED 0xFC00FC00
#define PENTIUM_MSR_CESR 0x11
#define PENTIUM_MSR_CTR0 0x12
#define PENTIUM_MSR_CTR1 0x13
struct pmc_md_pentium_op_pmcallocate {
uint32_t pm_pentium_config;
};
#ifdef _KERNEL
/* MD extension for 'struct pmc' */
struct pmc_md_pentium_pmc {
uint32_t pm_pentium_cesr;
};
/*
* Prototypes
*/
int pmc_initialize_p5(struct pmc_mdep *); /* Pentium PMCs */
#endif /* _KERNEL */
#endif /* _DEV_HWPMC_PENTIUM_H_ */

View File

@ -35,8 +35,9 @@ __FBSDID("$FreeBSD$");
#include <sys/smp.h>
#include <sys/systm.h>
#include <machine/apicreg.h>
#include <machine/cpufunc.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
/*
* PENTIUM 4 SUPPORT
@ -134,7 +135,11 @@ __FBSDID("$FreeBSD$");
* CPUy +.....-
* RC 0 1 2 1 0
*
* Here CPUx and CPUy are one of the two logical processors on a HTT CPU.
* Key:
* 'CPU[xy]' : one of the two logical processors on a HTT CPU.
* 'RC' : run count (#threads per physical core).
* '+' : point in time when a thread is put on a CPU.
* '-' : point in time where a thread is taken off a CPU.
*
* Handling HTT CONFIG
*
@ -438,7 +443,9 @@ struct p4_cpu {
struct pmc_hw *pc_hwpmcs[P4_NPMCS];
struct pmc_hw pc_p4pmcs[P4_NPMCS];
char pc_escrs[P4_NESCR];
struct mtx pc_mtx; /* spin lock */
struct mtx pc_mtx; /* spin lock */
uint32_t pc_intrflag; /* NMI handler flags */
unsigned int pc_intrlock; /* NMI handler spin lock */
unsigned char pc_flags[P4_NPMCS]; /* 4 bits each: {cfg,run}count */
union {
pmc_value_t pc_hw[P4_NPMCS * P4_NHTT];
@ -447,6 +454,20 @@ struct p4_cpu {
pmc_value_t pc_pmc_values[P4_NPMCS * P4_NHTT];
};
/*
* A 'logical' CPU shares PMC resources with partner 'physical' CPU,
* except the TSC, which is architectural and hence seperate. The
* 'logical' CPU descriptor thus has pointers to the physical CPUs
* descriptor state except for the TSC (rowindex 0) which is not
* shared.
*/
struct p4_logicalcpu {
struct pmc_cpu pc_common;
struct pmc_hw *pc_hwpmcs[P4_NPMCS];
struct pmc_hw pc_tsc;
};
#define P4_PCPU_PMC_VALUE(PC,RI,CPU) (PC)->pc_pmc_values[(RI)*((CPU) & 1)]
#define P4_PCPU_HW_VALUE(PC,RI,CPU) (PC)->pc_si.pc_hw[(RI)*((CPU) & 1)]
#define P4_PCPU_SAVED_IP(PC,RI,CPU) (PC)->pc_si.pc_ip[(RI)*((CPU) & 1)]
@ -468,6 +489,29 @@ struct p4_cpu {
#define P4_CPU_TO_FLAG(C) (pmc_cpu_is_logical(cpu) ? 0x2 : 0x1)
#define P4_PCPU_GET_INTRFLAG(PC,I) ((PC)->pc_intrflag & (1 << (I)))
#define P4_PCPU_SET_INTRFLAG(PC,I,V) do { \
uint32_t __mask; \
__mask = 1 << (I); \
if ((V)) \
(PC)->pc_intrflag |= __mask; \
else \
(PC)->pc_intrflag &= ~__mask; \
} while (0)
/*
* A minimal spin lock implementation for use inside the NMI handler.
*
* We don't want to use a regular spin lock here, because curthread
* may not be consistent at the time the handler is invoked.
*/
#define P4_PCPU_ACQ_INTR_SPINLOCK(PC) do { \
while (!atomic_cmpset_acq_int(&pc->pc_intrlock, 0, 1)) \
ia32_pause(); \
} while (0)
#define P4_PCPU_REL_INTR_SPINLOCK(PC) \
atomic_store_rel_int(&pc->pc_intrlock, 0);
/* ESCR row disposition */
static int p4_escrdisp[P4_NESCR];
@ -538,6 +582,7 @@ p4_init(int cpu)
int n, phycpu;
char *pescr;
struct p4_cpu *pcs;
struct p4_logicalcpu *plcs;
struct pmc_hw *phw;
KASSERT(cpu >= 0 && cpu < mp_ncpus,
@ -562,8 +607,23 @@ p4_init(int cpu)
cpu, phycpu));
if (pcs == NULL) /* decline to init */
return ENXIO;
p4_system_has_htt = 1;
pmc_pcpu[cpu] = (struct pmc_cpu *) pcs;
MALLOC(plcs, struct p4_logicalcpu *,
sizeof(struct p4_logicalcpu), M_PMC, M_WAITOK|M_ZERO);
/* The TSC is architectural state and is not shared */
plcs->pc_hwpmcs[0] = &plcs->pc_tsc;
plcs->pc_tsc.phw_state = PMC_PHW_FLAG_IS_ENABLED |
PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(0) |
PMC_PHW_FLAG_IS_SHAREABLE;
/* Other PMCs are shared with the physical CPU */
for (n = 1; n < P4_NPMCS; n++)
plcs->pc_hwpmcs[n] = pcs->pc_hwpmcs[n];
pmc_pcpu[cpu] = (struct pmc_cpu *) plcs;
return 0;
}
@ -605,16 +665,17 @@ p4_cleanup(int cpu)
PMCDBG(MDP,INI,0, "p4-cleanup cpu=%d", cpu);
/*
* Free up the per-cpu structure for the given cpu if
* allocated, and if this is a physical CPU.
*/
if ((pcs = (struct p4_cpu *) pmc_pcpu[cpu]) == NULL)
return 0;
if ((pcs = (struct p4_cpu *) pmc_pcpu[cpu]) != NULL &&
!pmc_cpu_is_logical(cpu)) {
/*
* If the CPU is physical we need to teardown the
* full MD state.
*/
if (!pmc_cpu_is_logical(cpu))
mtx_destroy(&pcs->pc_mtx);
FREE(pcs, M_PMC);
}
FREE(pcs, M_PMC);
pmc_pcpu[cpu] = NULL;
@ -637,7 +698,7 @@ p4_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
if (pp->pp_flags & PMC_PP_ENABLE_MSR_ACCESS)
load_cr4(rcr4() | CR4_PCE);
PMCDBG(MDP,SWI,2, "cr4=0x%x", rcr4());
PMCDBG(MDP,SWI,2, "cr4=0x%x", (uint32_t) rcr4());
return 0;
}
@ -657,7 +718,7 @@ p4_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
/* always disallow the RDPMC instruction */
load_cr4(rcr4() & ~CR4_PCE);
PMCDBG(MDP,SWO,2, "cr4=0x%x", rcr4());
PMCDBG(MDP,SWO,2, "cr4=0x%x", (uint32_t) rcr4());
return 0;
}
@ -681,6 +742,26 @@ p4_read_pmc(int cpu, int ri, pmc_value_t *v)
KASSERT(ri >= 0 && ri < P4_NPMCS,
("[p4,%d] illegal row-index %d", __LINE__, ri));
if (ri == 0) { /* TSC */
#if DEBUG
pc = (struct p4_cpu *) pmc_pcpu[cpu];
phw = pc->pc_hwpmcs[ri];
pm = phw->phw_pmc;
KASSERT(pm, ("[p4,%d] cpu=%d ri=%d not configured", __LINE__,
cpu, ri));
KASSERT(PMC_TO_CLASS(pm) == PMC_CLASS_TSC,
("[p4,%d] cpu=%d ri=%d not a TSC (%d)", __LINE__, cpu, ri,
PMC_TO_CLASS(pm)));
KASSERT(PMC_IS_COUNTING_MODE(PMC_TO_MODE(pm)),
("[p4,%d] TSC counter in non-counting mode", __LINE__));
#endif
*v = rdtsc();
PMCDBG(MDP,REA,2, "p4-read -> %jx", *v);
return 0;
}
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_PHYSICAL_CPU(cpu)];
phw = pc->pc_hwpmcs[ri];
pd = &p4_pmcdesc[ri];
@ -698,14 +779,6 @@ p4_read_pmc(int cpu, int ri, pmc_value_t *v)
PMCDBG(MDP,REA,1, "p4-read cpu=%d ri=%d mode=%d", cpu, ri, mode);
if (PMC_TO_CLASS(pm) == PMC_CLASS_TSC) {
KASSERT(PMC_IS_COUNTING_MODE(mode),
("[p4,%d] TSC counter in non-counting mode", __LINE__));
*v = rdtsc();
PMCDBG(MDP,REA,2, "p4-read -> %jx", *v);
return 0;
}
KASSERT(pd->pm_descr.pd_class == PMC_CLASS_P4,
("[p4,%d] unknown PMC class %d", __LINE__, pd->pm_descr.pd_class));
@ -747,6 +820,27 @@ p4_write_pmc(int cpu, int ri, pmc_value_t v)
KASSERT(ri >= 0 && ri < P4_NPMCS,
("[amd,%d] illegal row-index %d", __LINE__, ri));
/*
* The P4's TSC register is writeable, but we don't allow a
* write as changing the TSC's value could interfere with
* timekeeping and other system functions.
*/
if (ri == 0) {
#if DEBUG
pc = (struct p4_cpu *) pmc_pcpu[cpu];
phw = pc->pc_hwpmcs[ri];
pm = phw->phw_pmc;
KASSERT(pm, ("[p4,%d] cpu=%d ri=%d not configured", __LINE__,
cpu, ri));
KASSERT(PMC_TO_CLASS(pm) == PMC_CLASS_TSC,
("[p4,%d] cpu=%d ri=%d not a TSC (%d)", __LINE__,
cpu, ri, PMC_TO_CLASS(pm)));
#endif
return 0;
}
/* Shared PMCs */
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_PHYSICAL_CPU(cpu)];
phw = pc->pc_hwpmcs[ri];
pm = phw->phw_pmc;
@ -761,14 +855,6 @@ p4_write_pmc(int cpu, int ri, pmc_value_t v)
PMCDBG(MDP,WRI,1, "p4-write cpu=%d ri=%d mode=%d v=%jx", cpu, ri,
mode, v);
/*
* The P4's TSC register is writeable, but we don't allow a
* write as changing the TSC's value could interfere with
* timekeeping and other system functions.
*/
if (PMC_TO_CLASS(pm) == PMC_CLASS_TSC)
return 0;
/*
* write the PMC value to the register/saved value: for
* sampling mode PMCs, the value to be programmed into the PMC
@ -808,7 +894,21 @@ p4_config_pmc(int cpu, int ri, struct pmc *pm)
KASSERT(ri >= 0 && ri < P4_NPMCS,
("[p4,%d] illegal row-index %d", __LINE__, ri));
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_PHYSICAL_CPU(cpu)];
PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
if (ri == 0) { /* TSC */
pc = (struct p4_cpu *) pmc_pcpu[cpu];
phw = pc->pc_hwpmcs[ri];
KASSERT(pm == NULL || phw->phw_pmc == NULL,
("[p4,%d] hwpmc doubly config'ed", __LINE__));
phw->phw_pmc = pm;
return 0;
}
/* Shared PMCs */
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_PHYSICAL_CPU(cpu)];
phw = pc->pc_hwpmcs[ri];
KASSERT(pm == NULL || phw->phw_pmc == NULL ||
@ -826,9 +926,6 @@ p4_config_pmc(int cpu, int ri, struct pmc *pm)
("[p4,%d] cpu=%d ri=%d pmc configured with zero cfg count",
__LINE__, cpu, ri));
PMCDBG(MDP,CFG,1, "cpu=%d ri=%d cfg=%d pm=%p", cpu, ri, cfgflags,
pm);
cpuflag = P4_CPU_TO_FLAG(cpu);
if (pm) { /* config */
@ -1073,8 +1170,8 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
/* CCCR fields */
if (caps & PMC_CAP_THRESHOLD)
cccrvalue |= (a->pm_p4_cccrconfig & P4_CCCR_THRESHOLD_MASK) |
P4_CCCR_COMPARE;
cccrvalue |= (a->pm_md.pm_p4.pm_p4_cccrconfig &
P4_CCCR_THRESHOLD_MASK) | P4_CCCR_COMPARE;
if (caps & PMC_CAP_EDGE)
cccrvalue |= P4_CCCR_EDGE;
@ -1083,7 +1180,8 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
cccrvalue |= P4_CCCR_COMPLEMENT;
if (p4_system_has_htt)
cccrvalue |= a->pm_p4_cccrconfig & P4_CCCR_ACTIVE_THREAD_MASK;
cccrvalue |= a->pm_md.pm_p4.pm_p4_cccrconfig &
P4_CCCR_ACTIVE_THREAD_MASK;
else /* no HTT; thread field should be '11b' */
cccrvalue |= P4_CCCR_TO_ACTIVE_THREAD(0x3);
@ -1096,12 +1194,14 @@ p4_allocate_pmc(int cpu, int ri, struct pmc *pm,
/* ESCR fields */
if (caps & PMC_CAP_QUALIFIER)
escrvalue |= a->pm_p4_escrconfig & P4_ESCR_EVENT_MASK_MASK;
escrvalue |= a->pm_md.pm_p4.pm_p4_escrconfig &
P4_ESCR_EVENT_MASK_MASK;
if (caps & PMC_CAP_TAGGING)
escrvalue |= (a->pm_p4_escrconfig & P4_ESCR_TAG_VALUE_MASK) |
P4_ESCR_TAG_ENABLE;
escrvalue |= (a->pm_md.pm_p4.pm_p4_escrconfig &
P4_ESCR_TAG_VALUE_MASK) | P4_ESCR_TAG_ENABLE;
if (caps & PMC_CAP_QUALIFIER)
escrvalue |= (a->pm_p4_escrconfig & P4_ESCR_EVENT_MASK_MASK);
escrvalue |= (a->pm_md.pm_p4.pm_p4_escrconfig &
P4_ESCR_EVENT_MASK_MASK);
/* HTT: T0_{OS,USR} bits may get moved to T1 at pmc start */
tflags = 0;
@ -1434,116 +1534,150 @@ p4_stop_pmc(int cpu, int ri)
* The hardware sets the CCCR_OVF whenever a counter overflow occurs, so the handler
* examines all the 18 CCCR registers, processing the counters that have overflowed.
*
* On HTT machines, multiple logical CPUs may try to enter the NMI service
* routine at the same time.
* On HTT machines, the CCCR register is shared and will interrupt
* both logical processors if so configured. Thus multiple logical
* CPUs could enter the NMI service routine at the same time. These
* will get serialized using a per-cpu spinlock dedicated for use in
* the NMI handler.
*/
extern volatile lapic_t *lapic;
static void
p4_lapic_enable_pmc_interrupt(void)
{
uint32_t value;
value = lapic->lvt_pcint;
value &= ~APIC_LVT_M;
lapic->lvt_pcint = value;
}
static int
p4_intr(int cpu, uintptr_t eip, int usermode)
{
int i, pmc_interrupted;
uint32_t cccrval, pmi_ovf_mask;
int i, did_interrupt, error, ri;
uint32_t cccrval, ovf_mask, ovf_partner;
struct p4_cpu *pc;
struct pmc_hw *phw;
struct pmc *pm;
pmc_value_t v;
(void) eip;
(void) usermode;
PMCDBG(MDP,INT, 1, "cpu=%d eip=%x pcint=0x%x", cpu, eip,
lapic->lvt_pcint);
PMCDBG(MDP,INT, 1, "cpu=%d eip=%p um=%d", cpu, (void *) eip, usermode);
pmc_interrupted = 0;
pc = (struct p4_cpu *) pmc_pcpu[cpu];
pc = (struct p4_cpu *) pmc_pcpu[P4_TO_PHYSICAL_CPU(cpu)];
pmi_ovf_mask = pmc_cpu_is_logical(cpu) ?
ovf_mask = pmc_cpu_is_logical(cpu) ?
P4_CCCR_OVF_PMI_T1 : P4_CCCR_OVF_PMI_T0;
pmi_ovf_mask |= P4_CCCR_OVF;
ovf_mask |= P4_CCCR_OVF;
if (p4_system_has_htt)
ovf_partner = pmc_cpu_is_logical(cpu) ? P4_CCCR_OVF_PMI_T0 :
P4_CCCR_OVF_PMI_T1;
else
ovf_partner = 0;
did_interrupt = 0;
if (p4_system_has_htt)
P4_PCPU_ACQ_INTR_SPINLOCK(pc);
/*
* Loop through all CCCRs, looking for ones that have the
* OVF_PMI bit set for our logical CPU.
* Loop through all CCCRs, looking for ones that have
* interrupted this CPU.
*/
for (i = 0; i < P4_NPMCS-1; i++) {
for (i = 1; i < P4_NPMCS; i++) {
cccrval = rdmsr(P4_CCCR_MSR_FIRST + i - 1);
if ((cccrval & pmi_ovf_mask) != pmi_ovf_mask)
continue;
v = rdmsr(P4_PERFCTR_MSR_FIRST + i - 1);
pmc_interrupted = 1;
PMCDBG(MDP,INT, 2, "ri=%d v=%jx", i, v);
/* Stop the counter, and turn off the overflow bit */
cccrval &= ~(P4_CCCR_OVF | P4_CCCR_ENABLE);
wrmsr(P4_CCCR_MSR_FIRST + i - 1, cccrval);
phw = pc->pc_hwpmcs[i];
pm = phw->phw_pmc;
ri = i + 1; /* row index */
/*
* Ignore de-configured or stopped PMCs.
* Also ignore counting mode PMCs that may
* have overflowed their counters.
* Check if our partner logical CPU has already marked
* this PMC has having interrupted it. If so, reset
* the flag and process the interrupt, but leave the
* hardware alone.
*/
if (pm == NULL ||
pm->pm_state != PMC_STATE_RUNNING ||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
continue;
if (p4_system_has_htt && P4_PCPU_GET_INTRFLAG(pc,ri)) {
P4_PCPU_SET_INTRFLAG(pc,ri,0);
did_interrupt = 1;
/*
* If the previous sample hasn't been read yet, the
* sampling interrupt is coming in too fast for the
* rest of the system to cope. Do not re-enable the
* counter.
*/
if (P4_PCPU_SAVED_IP(pc,i,cpu)) {
atomic_add_int(&pmc_stats.pm_intr_ignored, 1);
/*
* Ignore de-configured or stopped PMCs.
* Ignore PMCs not in sampling mode.
*/
phw = pc->pc_hwpmcs[ri];
pm = phw->phw_pmc;
if (pm == NULL ||
pm->pm_state != PMC_STATE_RUNNING ||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
continue;
}
(void) pmc_process_interrupt(cpu, pm, eip, usermode);
continue;
}
/*
* write the the reload count and restart the
* hardware.
* Fresh interrupt. Look for the CCCR_OVF bit
* and the OVF_Tx bit for this logical
* processor being set.
*/
cccrval = rdmsr(P4_CCCR_MSR_FIRST + i);
v = P4_RELOAD_COUNT_TO_PERFCTR_VALUE(
pm->pm_sc.pm_reloadcount);
wrmsr(P4_PERFCTR_MSR_FIRST + i - 1, v);
wrmsr(P4_CCCR_MSR_FIRST + i - 1,
cccrval | P4_CCCR_ENABLE);
}
if (pmc_interrupted) {
if ((cccrval & ovf_mask) != ovf_mask)
continue;
/*
* On Intel CPUs, the PMC 'pcint' entry in the LAPIC
* gets masked when a PMC interrupts the CPU. We need
* to unmask this.
* If the other logical CPU would also have been
* interrupted due to the PMC being shared, record
* this fact in the per-cpu saved interrupt flag
* bitmask.
*/
p4_lapic_enable_pmc_interrupt();
if (p4_system_has_htt && (cccrval & ovf_partner))
P4_PCPU_SET_INTRFLAG(pc, ri, 1);
/* XXX: Invoke helper (non-NMI) interrupt here */
v = rdmsr(P4_PERFCTR_MSR_FIRST + i);
PMCDBG(MDP,INT, 2, "ri=%d v=%jx", ri, v);
/* Stop the counter, and reset the overflow bit */
cccrval &= ~(P4_CCCR_OVF | P4_CCCR_ENABLE);
wrmsr(P4_CCCR_MSR_FIRST + i, cccrval);
did_interrupt = 1;
/*
* Ignore de-configured or stopped PMCs. Ignore PMCs
* not in sampling mode.
*/
phw = pc->pc_hwpmcs[ri];
pm = phw->phw_pmc;
if (pm == NULL ||
pm->pm_state != PMC_STATE_RUNNING ||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
continue;
}
/*
* Process the interrupt. Re-enable the PMC if
* processing was successful.
*/
error = pmc_process_interrupt(cpu, pm, eip, usermode);
/*
* Only the first processor executing the NMI handler
* in a HTT pair will restart a PMC, and that too
* only if there were no errors.
*/
v = P4_RELOAD_COUNT_TO_PERFCTR_VALUE(
pm->pm_sc.pm_reloadcount);
wrmsr(P4_PERFCTR_MSR_FIRST + i, v);
if (error == 0)
wrmsr(P4_CCCR_MSR_FIRST + i,
cccrval | P4_CCCR_ENABLE);
}
return pmc_interrupted;
/* allow the other CPU to proceed */
if (p4_system_has_htt)
P4_PCPU_REL_INTR_SPINLOCK(pc);
/*
* On Intel P4 CPUs, the PMC 'pcint' entry in the LAPIC gets
* masked when a PMC interrupts the CPU. We need to unmask
* the interrupt source explicitly.
*/
if (did_interrupt)
pmc_x86_lapic_enable_pmc_interrupt();
else
atomic_add_int(&pmc_stats.pm_intr_ignored, 1);
return did_interrupt;
}
/*

124
sys/dev/hwpmc/hwpmc_piv.h Normal file
View File

@ -0,0 +1,124 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/* Machine dependent interfaces */
#ifndef _DEV_HWPMC_PIV_H_
#define _DEV_HWPMC_PIV_H_ 1
/* Intel P4 PMCs */
#define P4_NPMCS 19 /* 1 TSC + 18 PMCS */
#define P4_NESCR 45
#define P4_INVALID_PMC_INDEX -1
#define P4_MAX_ESCR_PER_EVENT 2
#define P4_MAX_PMC_PER_ESCR 3
#define P4_CCCR_OVF (1 << 31)
#define P4_CCCR_CASCADE (1 << 30)
#define P4_CCCR_OVF_PMI_T1 (1 << 27)
#define P4_CCCR_OVF_PMI_T0 (1 << 26)
#define P4_CCCR_FORCE_OVF (1 << 25)
#define P4_CCCR_EDGE (1 << 24)
#define P4_CCCR_THRESHOLD_SHIFT 20
#define P4_CCCR_THRESHOLD_MASK 0x00F00000
#define P4_CCCR_TO_THRESHOLD(C) (((C) << P4_CCCR_THRESHOLD_SHIFT) & \
P4_CCCR_THRESHOLD_MASK)
#define P4_CCCR_COMPLEMENT (1 << 19)
#define P4_CCCR_COMPARE (1 << 18)
#define P4_CCCR_ACTIVE_THREAD_SHIFT 16
#define P4_CCCR_ACTIVE_THREAD_MASK 0x00030000
#define P4_CCCR_TO_ACTIVE_THREAD(T) (((T) << P4_CCCR_ACTIVE_THREAD_SHIFT) & \
P4_CCCR_ACTIVE_THREAD_MASK)
#define P4_CCCR_ESCR_SELECT_SHIFT 13
#define P4_CCCR_ESCR_SELECT_MASK 0x0000E000
#define P4_CCCR_TO_ESCR_SELECT(E) (((E) << P4_CCCR_ESCR_SELECT_SHIFT) & \
P4_CCCR_ESCR_SELECT_MASK)
#define P4_CCCR_ENABLE (1 << 12)
#define P4_CCCR_VALID_BITS (P4_CCCR_OVF | P4_CCCR_CASCADE | \
P4_CCCR_OVF_PMI_T1 | P4_CCCR_OVF_PMI_T0 | P4_CCCR_FORCE_OVF | \
P4_CCCR_EDGE | P4_CCCR_THRESHOLD_MASK | P4_CCCR_COMPLEMENT | \
P4_CCCR_COMPARE | P4_CCCR_ESCR_SELECT_MASK | P4_CCCR_ENABLE)
#define P4_ESCR_EVENT_SELECT_SHIFT 25
#define P4_ESCR_EVENT_SELECT_MASK 0x7E000000
#define P4_ESCR_TO_EVENT_SELECT(E) (((E) << P4_ESCR_EVENT_SELECT_SHIFT) & \
P4_ESCR_EVENT_SELECT_MASK)
#define P4_ESCR_EVENT_MASK_SHIFT 9
#define P4_ESCR_EVENT_MASK_MASK 0x01FFFE00
#define P4_ESCR_TO_EVENT_MASK(M) (((M) << P4_ESCR_EVENT_MASK_SHIFT) & \
P4_ESCR_EVENT_MASK_MASK)
#define P4_ESCR_TAG_VALUE_SHIFT 5
#define P4_ESCR_TAG_VALUE_MASK 0x000001E0
#define P4_ESCR_TO_TAG_VALUE(T) (((T) << P4_ESCR_TAG_VALUE_SHIFT) & \
P4_ESCR_TAG_VALUE_MASK)
#define P4_ESCR_TAG_ENABLE 0x00000010
#define P4_ESCR_T0_OS 0x00000008
#define P4_ESCR_T0_USR 0x00000004
#define P4_ESCR_T1_OS 0x00000002
#define P4_ESCR_T1_USR 0x00000001
#define P4_ESCR_OS P4_ESCR_T0_OS
#define P4_ESCR_USR P4_ESCR_T0_USR
#define P4_ESCR_VALID_BITS (P4_ESCR_EVENT_SELECT_MASK | \
P4_ESCR_EVENT_MASK_MASK | P4_ESCR_TAG_VALUE_MASK | \
P4_ESCR_TAG_ENABLE | P4_ESCR_T0_OS | P4_ESCR_T0_USR | P4_ESCR_T1_OS \
P4_ESCR_T1_USR)
#define P4_PERFCTR_MASK 0xFFFFFFFFFFLL /* 40 bits */
#define P4_PERFCTR_OVERFLOWED(PMC) ((rdpmc(PMC) & (1LL << 39)) == 0)
#define P4_CCCR_MSR_FIRST 0x360 /* MSR_BPU_CCCR0 */
#define P4_PERFCTR_MSR_FIRST 0x300 /* MSR_BPU_COUNTER0 */
#define P4_RELOAD_COUNT_TO_PERFCTR_VALUE(V) (1 - (V))
#define P4_PERFCTR_VALUE_TO_RELOAD_COUNT(P) (1 - (P))
struct pmc_md_p4_op_pmcallocate {
uint32_t pm_p4_cccrconfig;
uint32_t pm_p4_escrconfig;
};
#ifdef _KERNEL
/* MD extension for 'struct pmc' */
struct pmc_md_p4_pmc {
uint32_t pm_p4_cccrvalue;
uint32_t pm_p4_escrvalue;
uint32_t pm_p4_escr;
uint32_t pm_p4_escrmsr;
};
/*
* Prototypes
*/
int pmc_initialize_p4(struct pmc_mdep *); /* Pentium IV PMCs */
#endif /* _KERNEL */
#endif /* _MACHINE_PMC_MDEP_H */

View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/pmc.h>
#include <machine/pmc_mdep.h>
struct pmc_mdep *
pmc_md_initialize()
{
return NULL;
}

View File

@ -35,11 +35,29 @@ __FBSDID("$FreeBSD$");
#include <sys/smp.h>
#include <sys/systm.h>
#include <machine/cputypes.h>
#include <machine/cpufunc.h>
#include <machine/md_var.h>
#include <machine/pmc_mdep.h>
#include <machine/specialreg.h>
/*
* PENTIUM PRO SUPPORT
*
* Quirks:
*
* - Both PMCs are enabled by a single bit P6_EVSEL_EN in performance
* counter '0'. This bit needs to be '1' if any of the two
* performance counters are in use. Perf counters can also be
* switched off by writing zeros to their EVSEL register.
*
* - While the width of these counters is 40 bits, we do not appear to
* have a way of writing 40 bits to the counter MSRs. A WRMSR
* instruction will sign extend bit 31 of the value being written to
* the perf counter -- a value of 0x80000000 written to an perf
* counter register will be sign extended to 0xFF80000000.
*
* This quirk primarily affects thread-mode PMCs in counting mode, as
* these PMCs read and write PMC registers at every context switch.
*/
struct p6pmc_descr {
@ -269,16 +287,43 @@ p6_find_event(enum pmc_event ev)
* Per-CPU data structure for P6 class CPUs
*
* [common stuff]
* [flags for maintaining PMC start/stop state]
* [3 struct pmc_hw pointers]
* [3 struct pmc_hw structures]
*/
struct p6_cpu {
struct pmc_cpu pc_common;
uint32_t pc_state;
struct pmc_hw *pc_hwpmcs[P6_NPMCS];
struct pmc_hw pc_p6pmcs[P6_NPMCS];
};
/*
* If CTR1 is active, we need to keep the 'EN' bit if CTR0 set,
* with the rest of CTR0 being zero'ed out.
*/
#define P6_SYNC_CTR_STATE(PC) do { \
uint32_t _config, _enable; \
_enable = 0; \
if ((PC)->pc_state & 0x02) \
_enable |= P6_EVSEL_EN; \
if ((PC)->pc_state & 0x01) \
_config = rdmsr(P6_MSR_EVSEL0) | \
P6_EVSEL_EN; \
else \
_config = 0; \
wrmsr(P6_MSR_EVSEL0, _config | _enable); \
} while (0)
#define P6_MARK_STARTED(PC,RI) do { \
(PC)->pc_state |= (1 << ((RI)-1)); \
} while (0)
#define P6_MARK_STOPPED(PC,RI) do { \
(PC)->pc_state &= ~(1<< ((RI)-1)); \
} while (0)
static int
p6_init(int cpu)
{
@ -294,9 +339,6 @@ p6_init(int cpu)
MALLOC(pcs, struct p6_cpu *, sizeof(struct p6_cpu), M_PMC,
M_WAITOK|M_ZERO);
if (pcs == NULL)
return ENOMEM;
phw = pcs->pc_p6pmcs;
for (n = 0; n < P6_NPMCS; n++, phw++) {
@ -377,12 +419,14 @@ p6_read_pmc(int cpu, int ri, pmc_value_t *v)
KASSERT(pm,
("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
if (pd->pm_descr.pd_class == PMC_CLASS_TSC)
if (pd->pm_descr.pd_class == PMC_CLASS_TSC) {
*v = rdtsc();
return 0;
}
tmp = rdmsr(pd->pm_pmc_msr) & P6_PERFCTR_MASK;
tmp = rdmsr(pd->pm_pmc_msr) & P6_PERFCTR_READ_MASK;
if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
*v = -tmp;
*v = P6_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp);
else
*v = tmp;
@ -413,9 +457,9 @@ p6_write_pmc(int cpu, int ri, pmc_value_t v)
pd->pm_pmc_msr, v);
if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
v = -v;
v = P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v);
wrmsr(pd->pm_pmc_msr, v & P6_PERFCTR_MASK);
wrmsr(pd->pm_pmc_msr, v & P6_PERFCTR_WRITE_MASK);
return 0;
}
@ -518,7 +562,7 @@ p6_allocate_pmc(int cpu, int ri, struct pmc *pm,
} else
allowed_unitmask = P6_EVSEL_TO_UMASK(pevent->pm_unitmask);
unitmask = a->pm_p6_config & P6_EVSEL_UMASK_MASK;
unitmask = a->pm_md.pm_ppro.pm_ppro_config & P6_EVSEL_UMASK_MASK;
if (unitmask & ~allowed_unitmask) /* disallow reserved bits */
return EINVAL;
@ -533,7 +577,8 @@ p6_allocate_pmc(int cpu, int ri, struct pmc *pm,
config |= unitmask;
if (caps & PMC_CAP_THRESHOLD)
config |= a->pm_p6_config & P6_EVSEL_CMASK_MASK;
config |= a->pm_md.pm_ppro.pm_ppro_config &
P6_EVSEL_CMASK_MASK;
/* set at least one of the 'usr' or 'os' caps */
if (caps & PMC_CAP_USER)
@ -550,7 +595,7 @@ p6_allocate_pmc(int cpu, int ri, struct pmc *pm,
if (caps & PMC_CAP_INTERRUPT)
config |= P6_EVSEL_INT;
pm->pm_md.pm_p6.pm_p6_evsel = config;
pm->pm_md.pm_ppro.pm_ppro_evsel = config;
PMCDBG(MDP,ALL,2, "p6-allocate config=0x%x", config);
@ -584,6 +629,7 @@ p6_start_pmc(int cpu, int ri)
{
uint32_t config;
struct pmc *pm;
struct p6_cpu *pc;
struct pmc_hw *phw;
const struct p6pmc_descr *pd;
@ -592,7 +638,8 @@ p6_start_pmc(int cpu, int ri)
KASSERT(ri >= 0 && ri < P6_NPMCS,
("[p6,%d] illegal row-index %d", __LINE__, ri));
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
pc = (struct p6_cpu *) pmc_pcpu[cpu];
phw = pc->pc_common.pc_hwpmcs[ri];
pm = phw->phw_pmc;
pd = &p6_pmcdesc[ri];
@ -609,25 +656,24 @@ p6_start_pmc(int cpu, int ri)
("[p6,%d] unknown PMC class %d", __LINE__,
pd->pm_descr.pd_class));
config = pm->pm_md.pm_p6.pm_p6_evsel;
config = pm->pm_md.pm_ppro.pm_ppro_evsel;
PMCDBG(MDP,STA,2, "p6-start/2 cpu=%d ri=%d evselmsr=0x%x config=0x%x",
cpu, ri, pd->pm_evsel_msr, config);
if (pd->pm_evsel_msr == P6_MSR_EVSEL0) /* CTR 0 */
wrmsr(pd->pm_evsel_msr, config | P6_EVSEL_EN);
else { /* CTR1 shares the enable bit CTR 0 */
wrmsr(pd->pm_evsel_msr, config);
wrmsr(P6_MSR_EVSEL0, rdmsr(P6_MSR_EVSEL0) | P6_EVSEL_EN);
}
P6_MARK_STARTED(pc, ri);
wrmsr(pd->pm_evsel_msr, config);
P6_SYNC_CTR_STATE(pc);
return 0;
}
static int
p6_stop_pmc(int cpu, int ri)
{
uint32_t config;
struct pmc *pm;
struct p6_cpu *pc;
struct pmc_hw *phw;
struct p6pmc_descr *pd;
@ -636,7 +682,8 @@ p6_stop_pmc(int cpu, int ri)
KASSERT(ri >= 0 && ri < P6_NPMCS,
("[p6,%d] illegal row index %d", __LINE__, ri));
phw = pmc_pcpu[cpu]->pc_hwpmcs[ri];
pc = (struct p6_cpu *) pmc_pcpu[cpu];
phw = pc->pc_common.pc_hwpmcs[ri];
pm = phw->phw_pmc;
pd = &p6_pmcdesc[ri];
@ -653,30 +700,75 @@ p6_stop_pmc(int cpu, int ri)
PMCDBG(MDP,STO,1, "p6-stop cpu=%d ri=%d", cpu, ri);
/*
* If CTR0 is being turned off but CTR1 is active, we need
* leave CTR0's EN field set. If CTR1 is being stopped, it
* suffices to zero its EVSEL register.
*/
wrmsr(pd->pm_evsel_msr, 0); /* stop hw */
P6_MARK_STOPPED(pc, ri); /* update software state */
if (ri == 1 &&
pmc_pcpu[cpu]->pc_hwpmcs[2]->phw_pmc != NULL)
config = P6_EVSEL_EN;
else
config = 0;
wrmsr(pd->pm_evsel_msr, config);
P6_SYNC_CTR_STATE(pc); /* restart CTR1 if need be */
PMCDBG(MDP,STO,2, "p6-stop/2 cpu=%d ri=%d config=0x%x", cpu, ri,
config);
PMCDBG(MDP,STO,2, "p6-stop/2 cpu=%d ri=%d", cpu, ri);
return 0;
}
static int
p6_intr(int cpu, uintptr_t eip, int usermode)
{
(void) cpu;
(void) eip;
return 0;
int i, error, retval, ri;
uint32_t perf0cfg;
struct pmc *pm;
struct p6_cpu *pc;
struct pmc_hw *phw;
pmc_value_t v;
KASSERT(cpu >= 0 && cpu < mp_ncpus,
("[p6,%d] CPU %d out of range", __LINE__, cpu));
retval = 0;
pc = (struct p6_cpu *) pmc_pcpu[cpu];
/* stop both PMCs */
perf0cfg = rdmsr(P6_MSR_EVSEL0);
wrmsr(P6_MSR_EVSEL0, perf0cfg & ~P6_EVSEL_EN);
for (i = 0; i < P6_NPMCS-1; i++) {
ri = i + 1;
if (!P6_PMC_HAS_OVERFLOWED(i))
continue;
phw = pc->pc_common.pc_hwpmcs[ri];
if ((pm = phw->phw_pmc) == NULL ||
pm->pm_state != PMC_STATE_RUNNING ||
!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
continue;
}
retval = 1;
error = pmc_process_interrupt(cpu, pm, eip, usermode);
if (error)
P6_MARK_STOPPED(pc,ri);
/* reload sampling count */
v = pm->pm_sc.pm_reloadcount;
wrmsr(P6_MSR_PERFCTR0 + i,
P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v));
}
/*
* On P6 processors, the LAPIC needs to have its PMC interrupt
* unmasked after a PMC interrupt.
*/
if (retval)
pmc_x86_lapic_enable_pmc_interrupt();
else
atomic_add_int(&pmc_stats.pm_intr_ignored, 1);
/* restart counters that can be restarted */
P6_SYNC_CTR_STATE(pc);
return retval;
}
static int

View File

@ -0,0 +1,83 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/* Machine dependent interfaces */
#ifndef _DEV_HWPMC_PPRO_H_
#define _DEV_HWPMC_PPRO_H_
/* Intel PPro, Celeron, P-II, P-III, Pentium-M PMCS */
#define P6_NPMCS 3 /* 1 TSC + 2 PMCs */
#define P6_EVSEL_CMASK_MASK 0xFF000000
#define P6_EVSEL_TO_CMASK(C) (((C) & 0xFF) << 24)
#define P6_EVSEL_INV (1 << 23)
#define P6_EVSEL_EN (1 << 22)
#define P6_EVSEL_INT (1 << 20)
#define P6_EVSEL_PC (1 << 19)
#define P6_EVSEL_E (1 << 18)
#define P6_EVSEL_OS (1 << 17)
#define P6_EVSEL_USR (1 << 16)
#define P6_EVSEL_UMASK_MASK 0x0000FF00
#define P6_EVSEL_TO_UMASK(U) (((U) & 0xFF) << 8)
#define P6_EVSEL_EVENT_SELECT(ES) ((ES) & 0xFF)
#define P6_EVSEL_RESERVED (1 << 21)
#define P6_MSR_EVSEL0 0x0186
#define P6_MSR_EVSEL1 0x0187
#define P6_MSR_PERFCTR0 0x00C1
#define P6_MSR_PERFCTR1 0x00C2
#define P6_PERFCTR_READ_MASK 0xFFFFFFFFFFLL /* 40 bits */
#define P6_PERFCTR_WRITE_MASK 0xFFFFFFFFU /* 32 bits */
#define P6_RELOAD_COUNT_TO_PERFCTR_VALUE(R) (-(R))
#define P6_PERFCTR_VALUE_TO_RELOAD_COUNT(P) (-(P))
#define P6_PMC_HAS_OVERFLOWED(P) ((rdpmc(P) & (1LL << 39)) == 0)
struct pmc_md_ppro_op_pmcallocate {
uint32_t pm_ppro_config;
};
#ifdef _KERNEL
/* MD extension for 'struct pmc' */
struct pmc_md_ppro_pmc {
uint32_t pm_ppro_evsel;
};
/*
* Prototypes
*/
int pmc_initialize_p6(struct pmc_mdep *); /* Pentium Pro PMCs */
#endif /* _KERNEL */
#endif /* _DEV_HWPMC_PPRO_H_ */

View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/pmc.h>
#include <machine/pmc_mdep.h>
struct pmc_mdep *
pmc_md_initialize()
{
return NULL;
}

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2003-2005 Joseph Koshy
* Copyright (c) 2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -28,17 +28,28 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/bus.h>
#include <sys/pmc.h>
#include <sys/pmckern.h>
#include <sys/smp.h>
#include <sys/systm.h>
#include <machine/cputypes.h>
#include <machine/apicreg.h>
#include <machine/pmc_mdep.h>
#include <machine/md_var.h>
struct pmc_mdep *
extern volatile lapic_t *lapic;
void
pmc_x86_lapic_enable_pmc_interrupt(void)
{
uint32_t value;
value = lapic->lvt_pcint;
value &= ~APIC_LVT_M;
lapic->lvt_pcint = value;
}
static struct pmc_mdep *
pmc_intel_initialize(void)
{
struct pmc_mdep *pmc_mdep;
@ -53,6 +64,7 @@ pmc_intel_initialize(void)
cputype = -1;
switch (cpu_id & 0xF00) {
#if defined(__i386__)
case 0x500: /* Pentium family processors */
cputype = PMC_CPU_INTEL_P5;
break;
@ -75,12 +87,15 @@ pmc_intel_initialize(void)
break;
}
break;
#endif
#if defined(__i386__) || defined(__amd64__)
case 0xF00: /* P4 */
model = ((cpu_id & 0xF0000) >> 12) | ((cpu_id & 0xF0) >> 4);
if (model >= 0 && model <= 3) /* known models */
cputype = PMC_CPU_INTEL_PIV;
break;
}
#endif
if ((int) cputype == -1) {
printf("pmc: Unknown Intel CPU.\n");
@ -101,14 +116,18 @@ pmc_intel_initialize(void)
switch (cputype) {
#if defined(__i386__) || defined(__amd64__)
/*
* Intel Pentium 4 Processors
* Intel Pentium 4 Processors, and P4/EMT64 processors.
*/
case PMC_CPU_INTEL_PIV:
error = pmc_initialize_p4(pmc_mdep);
break;
#endif
#if defined(__i386__)
/*
* P6 Family Processors
*/
@ -129,6 +148,7 @@ pmc_intel_initialize(void)
case PMC_CPU_INTEL_P5:
error = pmc_initialize_p5(pmc_mdep);
break;
#endif
default:
KASSERT(0,("[intel,%d] Unknown CPU type", __LINE__));
@ -141,3 +161,19 @@ pmc_intel_initialize(void)
return pmc_mdep;
}
/*
* Machine dependent initialization for x86 class platforms.
*/
struct pmc_mdep *
pmc_md_initialize()
{
/* determine the CPU kind */
if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
return pmc_amd_initialize();
else if (strcmp(cpu_vendor, "GenuineIntel") == 0)
return pmc_intel_initialize();
return NULL;
}

530
sys/dev/hwpmc/pmc_events.h Normal file
View File

@ -0,0 +1,530 @@
/*-
* Copyright (c) 2005 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _DEV_HWPMC_PMC_EVENTS_H_
#define _DEV_HWPMC_PMC_EVENTS_H_
/*
* PMC event codes.
*
* __PMC_EV(CLASS, SYMBOLIC-NAME, VALUE, READABLE-NAME)
*
*/
/*
* AMD K7 Events, from "The AMD Athlon(tm) Processor x86 Code
* Optimization Guide" [Doc#22007K, Feb 2002]
*/
#define __PMC_EV_K7() \
__PMC_EV(K7, DC_ACCESSES, k7-dc-accesses) \
__PMC_EV(K7, DC_MISSES, k7-dc-misses) \
__PMC_EV(K7, DC_REFILLS_FROM_L2, k7-dc-refills-from-l2) \
__PMC_EV(K7, DC_REFILLS_FROM_SYSTEM, k7-dc-refills-from-system) \
__PMC_EV(K7, DC_WRITEBACKS, k7-dc-writebacks) \
__PMC_EV(K7, L1_DTLB_MISS_AND_L2_DTLB_HITS, \
k7-l1-dtlb-miss-and-l2-dtlb-hits) \
__PMC_EV(K7, L1_AND_L2_DTLB_MISSES, k7-l1-and-l2-dtlb-misses) \
__PMC_EV(K7, MISALIGNED_REFERENCES, k7-misaligned-references) \
__PMC_EV(K7, IC_FETCHES, k7-ic-fetches) \
__PMC_EV(K7, IC_MISSES, k7-ic-misses) \
__PMC_EV(K7, L1_ITLB_MISSES, k7-l1-itlb-misses) \
__PMC_EV(K7, L1_L2_ITLB_MISSES, k7-l1-l2-itlb-misses) \
__PMC_EV(K7, RETIRED_INSTRUCTIONS, k7-retired-instructions) \
__PMC_EV(K7, RETIRED_OPS, k7-retired-ops) \
__PMC_EV(K7, RETIRED_BRANCHES, k7-retired-branches) \
__PMC_EV(K7, RETIRED_BRANCHES_MISPREDICTED, \
k7-retired-branches-mispredicted) \
__PMC_EV(K7, RETIRED_TAKEN_BRANCHES, k7-retired-taken-branches) \
__PMC_EV(K7, RETIRED_TAKEN_BRANCHES_MISPREDICTED, \
k7-retired-taken-branches-mispredicted) \
__PMC_EV(K7, RETIRED_FAR_CONTROL_TRANSFERS, \
k7-retired-far-control-transfers) \
__PMC_EV(K7, RETIRED_RESYNC_BRANCHES, k7-retired-resync-branches) \
__PMC_EV(K7, INTERRUPTS_MASKED_CYCLES, k7-interrupts-masked-cycles) \
__PMC_EV(K7, INTERRUPTS_MASKED_WHILE_PENDING_CYCLES, \
k7-interrupts-masked-while-pending-cycles) \
__PMC_EV(K7, HARDWARE_INTERRUPTS, k7-hardware-interrupts)
#define PMC_EV_K7_FIRST PMC_EV_K7_DC_ACCESSES
#define PMC_EV_K7_LAST PMC_EV_K7_HARDWARE_INTERRUPTS
/*
* Intel P4 Events, from "IA-32 Intel(r) Architecture Software
* Developer's Manual, Volume 3: System Programming Guide" [245472-012]
*/
#define __PMC_EV_P4() \
__PMC_EV(P4, TC_DELIVER_MODE, p4-tc-deliver-mode) \
__PMC_EV(P4, BPU_FETCH_REQUEST, p4-bpu-fetch-request) \
__PMC_EV(P4, ITLB_REFERENCE, p4-itlb-reference) \
__PMC_EV(P4, MEMORY_CANCEL, p4-memory-cancel) \
__PMC_EV(P4, MEMORY_COMPLETE, p4-memory-complete) \
__PMC_EV(P4, LOAD_PORT_REPLAY, p4-load-port-replay) \
__PMC_EV(P4, STORE_PORT_REPLAY, p4-store-port-replay) \
__PMC_EV(P4, MOB_LOAD_REPLAY, p4-mob-load-replay) \
__PMC_EV(P4, PAGE_WALK_TYPE, p4-page-walk-type) \
__PMC_EV(P4, BSQ_CACHE_REFERENCE, p4-bsq-cache-reference) \
__PMC_EV(P4, IOQ_ALLOCATION, p4-ioq-allocation) \
__PMC_EV(P4, IOQ_ACTIVE_ENTRIES, p4-ioq-active-entries) \
__PMC_EV(P4, FSB_DATA_ACTIVITY, p4-fsb-data-activity) \
__PMC_EV(P4, BSQ_ALLOCATION, p4-bsq-allocation) \
__PMC_EV(P4, BSQ_ACTIVE_ENTRIES, p4-bsq-active-entries) \
__PMC_EV(P4, SSE_INPUT_ASSIST, p4-sse-input-assist) \
__PMC_EV(P4, PACKED_SP_UOP, p4-packed-sp-uop) \
__PMC_EV(P4, PACKED_DP_UOP, p4-packed-dp-uop) \
__PMC_EV(P4, SCALAR_SP_UOP, p4-scalar-sp-uop) \
__PMC_EV(P4, SCALAR_DP_UOP, p4-scalar-dp-uop) \
__PMC_EV(P4, 64BIT_MMX_UOP, p4-64bit-mmx-uop) \
__PMC_EV(P4, 128BIT_MMX_UOP, p4-128bit-mmx-uop) \
__PMC_EV(P4, X87_FP_UOP, p4-x87-fp-uop) \
__PMC_EV(P4, X87_SIMD_MOVES_UOP, p4-x87-simd-moves-uop) \
__PMC_EV(P4, GLOBAL_POWER_EVENTS, p4-global-power-events) \
__PMC_EV(P4, TC_MS_XFER, p4-tc-ms-xfer) \
__PMC_EV(P4, UOP_QUEUE_WRITES, p4-uop-queue-writes) \
__PMC_EV(P4, RETIRED_MISPRED_BRANCH_TYPE, \
p4-retired-mispred-branch-type) \
__PMC_EV(P4, RETIRED_BRANCH_TYPE, p4-retired-branch-type) \
__PMC_EV(P4, RESOURCE_STALL, p4-resource-stall) \
__PMC_EV(P4, WC_BUFFER, p4-wc-buffer) \
__PMC_EV(P4, B2B_CYCLES, p4-b2b-cycles) \
__PMC_EV(P4, BNR, p4-bnr) \
__PMC_EV(P4, SNOOP, p4-snoop) \
__PMC_EV(P4, RESPONSE, p4-response) \
__PMC_EV(P4, FRONT_END_EVENT, p4-front-end-event) \
__PMC_EV(P4, EXECUTION_EVENT, p4-execution-event) \
__PMC_EV(P4, REPLAY_EVENT, p4-replay-event) \
__PMC_EV(P4, INSTR_RETIRED, p4-instr-retired) \
__PMC_EV(P4, UOPS_RETIRED, p4-uops-retired) \
__PMC_EV(P4, UOP_TYPE, p4-uop-type) \
__PMC_EV(P4, BRANCH_RETIRED, p4-branch-retired) \
__PMC_EV(P4, MISPRED_BRANCH_RETIRED, p4-mispred-branch-retired) \
__PMC_EV(P4, X87_ASSIST, p4-x87-assist) \
__PMC_EV(P4, MACHINE_CLEAR, p4-machine-clear)
#define PMC_EV_P4_FIRST PMC_EV_P4_TC_DELIVER_MODE
#define PMC_EV_P4_LAST PMC_EV_P4_MACHINE_CLEAR
/* Intel Pentium Pro, P-II, P-III and Pentium-M style events */
#define __PMC_EV_P6() \
__PMC_EV(P6, DATA_MEM_REFS, p6-data-mem-refs) \
__PMC_EV(P6, DCU_LINES_IN, p6-dcu-lines-in) \
__PMC_EV(P6, DCU_M_LINES_IN, p6-dcu-m-lines-in) \
__PMC_EV(P6, DCU_M_LINES_OUT, p6-dcu-m-lines-out) \
__PMC_EV(P6, DCU_MISS_OUTSTANDING, p6-dcu-miss-outstanding) \
__PMC_EV(P6, IFU_FETCH, p6-ifu-fetch) \
__PMC_EV(P6, IFU_FETCH_MISS, p6-ifu-fetch-miss) \
__PMC_EV(P6, ITLB_MISS, p6-itlb-miss) \
__PMC_EV(P6, IFU_MEM_STALL, p6-ifu-mem-stall) \
__PMC_EV(P6, ILD_STALL, p6-ild-stall) \
__PMC_EV(P6, L2_IFETCH, p6-l2-ifetch) \
__PMC_EV(P6, L2_LD, p6-l2-ld) \
__PMC_EV(P6, L2_ST, p6-l2-st) \
__PMC_EV(P6, L2_LINES_IN, p6-l2-lines-in) \
__PMC_EV(P6, L2_LINES_OUT, p6-l2-lines-out) \
__PMC_EV(P6, L2_M_LINES_INM, p6-l2-m-lines-inm) \
__PMC_EV(P6, L2_M_LINES_OUTM, p6-l2-m-lines-outm) \
__PMC_EV(P6, L2_RQSTS, p6-l2-rqsts) \
__PMC_EV(P6, L2_ADS, p6-l2-ads) \
__PMC_EV(P6, L2_DBUS_BUSY, p6-l2-dbus-busy) \
__PMC_EV(P6, L2_DBUS_BUSY_RD, p6-l2-dbus-busy-rd) \
__PMC_EV(P6, BUS_DRDY_CLOCKS, p6-bus-drdy-clocks) \
__PMC_EV(P6, BUS_LOCK_CLOCKS, p6-bus-lock-clocks) \
__PMC_EV(P6, BUS_REQ_OUTSTANDING, p6-bus-req-outstanding) \
__PMC_EV(P6, BUS_TRAN_BRD, p6-bus-tran-brd) \
__PMC_EV(P6, BUS_TRAN_RFO, p6-bus-tran-rfo) \
__PMC_EV(P6, BUS_TRANS_WB, p6-bus-trans-wb) \
__PMC_EV(P6, BUS_TRAN_IFETCH, p6-bus-tran-ifetch) \
__PMC_EV(P6, BUS_TRAN_INVAL, p6-bus-tran-inval) \
__PMC_EV(P6, BUS_TRAN_PWR, p6-bus-tran-pwr) \
__PMC_EV(P6, BUS_TRANS_P, p6-bus-trans-p) \
__PMC_EV(P6, BUS_TRANS_IO, p6-bus-trans-io) \
__PMC_EV(P6, BUS_TRAN_DEF, p6-bus-tran-def) \
__PMC_EV(P6, BUS_TRAN_BURST, p6-bus-tran-burst) \
__PMC_EV(P6, BUS_TRAN_ANY, p6-bus-tran-any) \
__PMC_EV(P6, BUS_TRAN_MEM, p6-bus-tran-mem) \
__PMC_EV(P6, BUS_DATA_RCV, p6-bus-data-rcv) \
__PMC_EV(P6, BUS_BNR_DRV, p6-bus-bnr-drv) \
__PMC_EV(P6, BUS_HIT_DRV, p6-bus-hit-drv) \
__PMC_EV(P6, BUS_HITM_DRV, p6-bus-hitm-drv) \
__PMC_EV(P6, BUS_SNOOP_STALL, p6-bus-snoop-stall) \
__PMC_EV(P6, FLOPS, p6-flops) \
__PMC_EV(P6, FP_COMPS_OPS_EXE, p6-fp-comps-ops-exe) \
__PMC_EV(P6, FP_ASSIST, p6-fp-assist) \
__PMC_EV(P6, MUL, p6-mul) \
__PMC_EV(P6, DIV, p6-div) \
__PMC_EV(P6, CYCLES_DIV_BUSY, p6-cycles-div-busy) \
__PMC_EV(P6, LD_BLOCKS, p6-ld-blocks) \
__PMC_EV(P6, SB_DRAINS, p6-sb-drains) \
__PMC_EV(P6, MISALIGN_MEM_REF, p6-misalign-mem-ref) \
__PMC_EV(P6, EMON_KNI_PREF_DISPATCHED, p6-emon-kni-pref-dispatched) \
__PMC_EV(P6, EMON_KNI_PREF_MISS, p6-emon-kni-pref-miss) \
__PMC_EV(P6, INST_RETIRED, p6-inst-retired) \
__PMC_EV(P6, UOPS_RETIRED, p6-uops-retired) \
__PMC_EV(P6, INST_DECODED, p6-inst-decoded) \
__PMC_EV(P6, EMON_KNI_INST_RETIRED, p6-emon-kni-inst-retired) \
__PMC_EV(P6, EMON_KNI_COMP_INST_RET, p6-emon-kni-comp-inst-ret) \
__PMC_EV(P6, HW_INT_RX, p6-hw-int-rx) \
__PMC_EV(P6, CYCLES_INT_MASKED, p6-cycles-int-masked) \
__PMC_EV(P6, CYCLES_INT_PENDING_AND_MASKED, \
p6-cycles-in-pending-and-masked) \
__PMC_EV(P6, BR_INST_RETIRED, p6-br-inst-retired) \
__PMC_EV(P6, BR_MISS_PRED_RETIRED, p6-br-miss-pred-retired) \
__PMC_EV(P6, BR_TAKEN_RETIRED, p6-br-taken-retired) \
__PMC_EV(P6, BR_MISS_PRED_TAKEN_RET, p6-br-miss-pred-taken-ret) \
__PMC_EV(P6, BR_INST_DECODED, p6-br-inst-decoded) \
__PMC_EV(P6, BTB_MISSES, p6-btb-misses) \
__PMC_EV(P6, BR_BOGUS, p6-br-bogus) \
__PMC_EV(P6, BACLEARS, p6-baclears) \
__PMC_EV(P6, RESOURCE_STALLS, p6-resource-stalls) \
__PMC_EV(P6, PARTIAL_RAT_STALLS, p6-partial-rat-stalls) \
__PMC_EV(P6, SEGMENT_REG_LOADS, p6-segment-reg-loads) \
__PMC_EV(P6, CPU_CLK_UNHALTED, p6-cpu-clk-unhalted) \
__PMC_EV(P6, MMX_INSTR_EXEC, p6-mmx-instr-exec) \
__PMC_EV(P6, MMX_SAT_INSTR_EXEC, p6-mmx-sat-instr-exec) \
__PMC_EV(P6, MMX_UOPS_EXEC, p6-mmx-uops-exec) \
__PMC_EV(P6, MMX_INSTR_TYPE_EXEC, p6-mmx-instr-type-exec) \
__PMC_EV(P6, FP_MMX_TRANS, p6-fp-mmx-trans) \
__PMC_EV(P6, MMX_ASSIST, p6-mmx-assist) \
__PMC_EV(P6, MMX_INSTR_RET, p6-mmx-instr-ret) \
__PMC_EV(P6, SEG_RENAME_STALLS, p6-seg-rename-stalls) \
__PMC_EV(P6, SEG_REG_RENAMES, p6-seg-reg-renames) \
__PMC_EV(P6, RET_SEG_RENAMES, p6-ret-seg-renames) \
__PMC_EV(P6, EMON_EST_TRANS, p6-emon-est-trans) \
__PMC_EV(P6, EMON_THERMAL_TRIP, p6-emon-thermal-trip) \
__PMC_EV(P6, BR_INST_EXEC, p6-br-inst-exec) \
__PMC_EV(P6, BR_MISSP_EXEC, p6-br-missp-exec) \
__PMC_EV(P6, BR_BAC_MISSP_EXEC, p6-br-bac-missp-exec) \
__PMC_EV(P6, BR_CND_EXEC, p6-br-cnd-exec) \
__PMC_EV(P6, BR_CND_MISSP_EXEC, p6-br-cnd-missp-exec) \
__PMC_EV(P6, BR_IND_EXEC, p6-br-ind-exec) \
__PMC_EV(P6, BR_IND_MISSP_EXEC, p6-br-ind-missp-exec) \
__PMC_EV(P6, BR_RET_EXEC, p6-br-ret-exec) \
__PMC_EV(P6, BR_RET_MISSP_EXEC, p6-br-ret-missp-exec) \
__PMC_EV(P6, BR_RET_BAC_MISSP_EXEC, p6-br-ret-bac-missp-exec) \
__PMC_EV(P6, BR_CALL_EXEC, p6-br-call-exec) \
__PMC_EV(P6, BR_CALL_MISSP_EXEC, p6-br-call-missp-exec) \
__PMC_EV(P6, BR_IND_CALL_EXEC, p6-br-ind-call-exec) \
__PMC_EV(P6, EMON_SIMD_INSTR_RETIRED, p6-emon-simd-instr-retired) \
__PMC_EV(P6, EMON_SYNCH_UOPS, p6-emon-synch-uops) \
__PMC_EV(P6, EMON_ESP_UOPS, p6-emon-esp-uops) \
__PMC_EV(P6, EMON_FUSED_UOPS_RET, p6-emon-fused-uops-ret) \
__PMC_EV(P6, EMON_UNFUSION, p6-emon-unfusion) \
__PMC_EV(P6, EMON_PREF_RQSTS_UP, p6-emon-pref-rqsts-up) \
__PMC_EV(P6, EMON_PREF_RQSTS_DN, p6-emon-pref-rqsts-dn) \
__PMC_EV(P6, EMON_SSE_SSE2_INST_RETIRED, \
p6-emon-sse-sse2-inst-retired) \
__PMC_EV(P6, EMON_SSE_SSE2_COMP_INST_RETIRED, \
p6-emon-sse-sse2-comp-inst-retired)
#define PMC_EV_P6_FIRST PMC_EV_P6_DATA_MEM_REFS
#define PMC_EV_P6_LAST PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED
/* AMD K8 PMCs */
#define __PMC_EV_K8() \
__PMC_EV(K8, FP_DISPATCHED_FPU_OPS, k8-fp-dispatched-fpu-ops) \
__PMC_EV(K8, FP_CYCLES_WITH_NO_FPU_OPS_RETIRED, \
k8-fp-cycles-with-no-fpu-ops-retired) \
__PMC_EV(K8, FP_DISPATCHED_FPU_FAST_FLAG_OPS, \
k8-fp-dispatched-fpu-fast-flag-ops) \
__PMC_EV(K8, LS_SEGMENT_REGISTER_LOAD, k8-ls-segment-register-load) \
__PMC_EV(K8, LS_MICROARCHITECTURAL_RESYNC_BY_SELF_MODIFYING_CODE, \
k8-ls-microarchitectural-resync-by-self-modifying-code) \
__PMC_EV(K8, LS_MICROARCHITECTURAL_RESYNC_BY_SNOOP, \
k8-ls-microarchitectural-resync-by-snoop) \
__PMC_EV(K8, LS_BUFFER2_FULL, k8-ls-buffer2-full) \
__PMC_EV(K8, LS_LOCKED_OPERATION, k8-ls-locked-operation) \
__PMC_EV(K8, LS_MICROARCHITECTURAL_LATE_CANCEL, \
k8-ls-microarchitectural-late-cancel) \
__PMC_EV(K8, LS_RETIRED_CFLUSH_INSTRUCTIONS, \
k8-ls-retired-cflush-instructions) \
__PMC_EV(K8, LS_RETIRED_CPUID_INSTRUCTIONS, \
k8-ls-retired-cpuid-instructions) \
__PMC_EV(K8, DC_ACCESS, k8-dc-access) \
__PMC_EV(K8, DC_MISS, k8-dc-miss) \
__PMC_EV(K8, DC_REFILL_FROM_L2, k8-dc-refill-from-l2) \
__PMC_EV(K8, DC_REFILL_FROM_SYSTEM, k8-dc-refill-from-system) \
__PMC_EV(K8, DC_COPYBACK, k8-dc-copyback) \
__PMC_EV(K8, DC_L1_DTLB_MISS_AND_L2_DTLB_HIT, \
k8-dc-l1-dtlb-miss-and-l2-dtlb-hit) \
__PMC_EV(K8, DC_L1_DTLB_MISS_AND_L2_DTLB_MISS, \
k8-dc-l1-dtlb-miss-and-l2-dtlb-miss) \
__PMC_EV(K8, DC_MISALIGNED_DATA_REFERENCE, \
k8-dc-misaligned-data-reference) \
__PMC_EV(K8, DC_MICROARCHITECTURAL_LATE_CANCEL, \
k8-dc-microarchitectural-late-cancel-of-an-access) \
__PMC_EV(K8, DC_MICROARCHITECTURAL_EARLY_CANCEL, \
k8-dc-microarchitectural-early-cancel-of-an-access) \
__PMC_EV(K8, DC_ONE_BIT_ECC_ERROR, k8-dc-one-bit-ecc-error) \
__PMC_EV(K8, DC_DISPATCHED_PREFETCH_INSTRUCTIONS, \
k8-dc-dispatched-prefetch-instructions) \
__PMC_EV(K8, DC_DCACHE_ACCESSES_BY_LOCKS, \
k8-dc-dcache-accesses-by-locks) \
__PMC_EV(K8, BU_CPU_CLK_UNHALTED, k8-bu-cpu-clk-unhalted) \
__PMC_EV(K8, BU_INTERNAL_L2_REQUEST, k8-bu-internal-l2-request) \
__PMC_EV(K8, BU_FILL_REQUEST_L2_MISS, k8-bu-fill-request-l2-miss) \
__PMC_EV(K8, BU_FILL_INTO_L2, k8-bu-fill-into-l2) \
__PMC_EV(K8, IC_FETCH, k8-ic-fetch) \
__PMC_EV(K8, IC_MISS, k8-ic-miss) \
__PMC_EV(K8, IC_REFILL_FROM_L2, k8-ic-refill-from-l2) \
__PMC_EV(K8, IC_REFILL_FROM_SYSTEM, k8-ic-refill-from-system) \
__PMC_EV(K8, IC_L1_ITLB_MISS_AND_L2_ITLB_HIT, \
k8-ic-l1-itlb-miss-and-l2-itlb-hit) \
__PMC_EV(K8, IC_L1_ITLB_MISS_AND_L2_ITLB_MISS, \
k8-ic-l1-itlb-miss-and-l2-itlb-miss) \
__PMC_EV(K8, IC_MICROARCHITECTURAL_RESYNC_BY_SNOOP, \
k8-ic-microarchitectural-resync-by-snoop) \
__PMC_EV(K8, IC_INSTRUCTION_FETCH_STALL, \
k8-ic-instruction-fetch-stall) \
__PMC_EV(K8, IC_RETURN_STACK_HIT, k8-ic-return-stack-hit) \
__PMC_EV(K8, IC_RETURN_STACK_OVERFLOW, k8-ic-return-stack-overflow) \
__PMC_EV(K8, FR_RETIRED_X86_INSTRUCTIONS, \
k8-fr-retired-x86-instructions) \
__PMC_EV(K8, FR_RETIRED_UOPS, k8-fr-retired-uops) \
__PMC_EV(K8, FR_RETIRED_BRANCHES, k8-fr-retired-branches) \
__PMC_EV(K8, FR_RETIRED_BRANCHES_MISPREDICTED, \
k8-fr-retired-branches-mispredicted) \
__PMC_EV(K8, FR_RETIRED_TAKEN_BRANCHES, \
k8-fr-retired-taken-branches) \
__PMC_EV(K8, FR_RETIRED_TAKEN_BRANCHES_MISPREDICTED, \
k8-fr-retired-taken-branches-mispredicted) \
__PMC_EV(K8, FR_RETIRED_FAR_CONTROL_TRANSFERS, \
k8-fr-retired-far-control-transfers) \
__PMC_EV(K8, FR_RETIRED_RESYNCS, k8-fr-retired-resyncs) \
__PMC_EV(K8, FR_RETIRED_NEAR_RETURNS, k8-fr-retired-near-returns) \
__PMC_EV(K8, FR_RETIRED_NEAR_RETURNS_MISPREDICTED, \
k8-fr-retired-near-returns-mispredicted) \
__PMC_EV(K8, \
FR_RETIRED_TAKEN_BRANCHES_MISPREDICTED_BY_ADDR_MISCOMPARE, \
k8-fr-retired-taken-branches-mispredicted-by-addr-miscompare) \
__PMC_EV(K8, FR_RETIRED_FPU_INSTRUCTIONS, \
k8-fr-retired-fpu-instructions) \
__PMC_EV(K8, FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS, \
k8-fr-retired-fastpath-double-op-instructions) \
__PMC_EV(K8, FR_INTERRUPTS_MASKED_CYCLES, \
k8-fr-interrupts-masked-cycles) \
__PMC_EV(K8, FR_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES, \
k8-fr-interrupts-masked-while-pending-cycles) \
__PMC_EV(K8, FR_TAKEN_HARDWARE_INTERRUPTS, \
k8-fr-taken-hardware-interrupts) \
__PMC_EV(K8, FR_DECODER_EMPTY, k8-fr-decoder-empty) \
__PMC_EV(K8, FR_DISPATCH_STALLS, k8-fr-dispatch-stalls) \
__PMC_EV(K8, FR_DISPATCH_STALL_FROM_BRANCH_ABORT_TO_RETIRE, \
k8-fr-dispatch-stall-from-branch-abort-to-retire) \
__PMC_EV(K8, FR_DISPATCH_STALL_FOR_SERIALIZATION, \
k8-fr-dispatch-stall-for-serialization) \
__PMC_EV(K8, FR_DISPATCH_STALL_FOR_SEGMENT_LOAD, \
k8-fr-dispatch-stall-for-segment-load) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_REORDER_BUFFER_IS_FULL, \
k8-fr-dispatch-stall-when-reorder-buffer-is-full) \
__PMC_EV(K8, \
FR_DISPATCH_STALL_WHEN_RESERVATION_STATIONS_ARE_FULL, \
k8-fr-dispatch-stall-when-reservation-stations-are-full) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_FPU_IS_FULL, \
k8-fr-dispatch-stall-when-fpu-is-full) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_LS_IS_FULL, \
k8-fr-dispatch-stall-when-ls-is-full) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_WAITING_FOR_ALL_TO_BE_QUIET, \
k8-fr-dispatch-stall-when-waiting-for-all-to-be-quiet) \
__PMC_EV(K8, \
FR_DISPATCH_STALL_WHEN_FAR_XFER_OR_RESYNC_BRANCH_PENDING, \
k8-fr-dispatch-stall-when-far-xfer-or-resync-branch-pending) \
__PMC_EV(K8, FR_FPU_EXCEPTIONS, k8-fr-fpu-exceptions) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR0, \
k8-fr-number-of-breakpoints-for-dr0) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR1, \
k8-fr-number-of-breakpoints-for-dr1) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR2, \
k8-fr-number-of-breakpoints-for-dr2) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR3, \
k8-fr-number-of-breakpoints-for-dr3) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_PAGE_ACCESS_EVENT, \
k8-nb-memory-controller-page-access-event) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_PAGE_TABLE_OVERFLOW, \
k8-nb-memory-controller-page-table-overflow) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_DRAM_COMMAND_SLOTS_MISSED, \
k8-nb-memory-controller-dram-slots-missed) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_TURNAROUND, \
k8-nb-memory-controller-turnaround) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_BYPASS_SATURATION, \
k8-nb-memory-controller-bypass-saturation) \
__PMC_EV(K8, NB_SIZED_COMMANDS, k8-nb-sized-commands) \
__PMC_EV(K8, NB_PROBE_RESULT, k8-nb-probe-result) \
__PMC_EV(K8, NB_HT_BUS0_BANDWIDTH, k8-nb-ht-bus0-bandwidth) \
__PMC_EV(K8, NB_HT_BUS1_BANDWIDTH, k8-nb-ht-bus1-bandwidth) \
__PMC_EV(K8, NB_HT_BUS2_BANDWIDTH, k8-nb-ht-bus2-bandwidth)
#define PMC_EV_K8_FIRST PMC_EV_K8_FP_DISPATCHED_FPU_OPS
#define PMC_EV_K8_LAST PMC_EV_K8_NB_HT_BUS2_BANDWIDTH
/* Intel Pentium Events */
#define __PMC_EV_P5() \
__PMC_EV(P5, DATA_READ, p5-data-read) \
__PMC_EV(P5, DATA_WRITE, p5-data-write) \
__PMC_EV(P5, DATA_TLB_MISS, p5-data-tlb-miss) \
__PMC_EV(P5, DATA_READ_MISS, p5-data-read-miss) \
__PMC_EV(P5, DATA_WRITE_MISS, p5-data-write-miss) \
__PMC_EV(P5, WRITE_HIT_TO_M_OR_E_STATE_LINES, \
p5-write-hit-to-m-or-e-state-lines) \
__PMC_EV(P5, DATA_CACHE_LINES_WRITTEN_BACK, \
p4-data-cache-lines-written-back) \
__PMC_EV(P5, EXTERNAL_SNOOPS, p5-external-snoops) \
__PMC_EV(P5, EXTERNAL_DATA_CACHE_SNOOP_HITS, \
p5-external-data-cache-snoop-hits) \
__PMC_EV(P5, MEMORY_ACCESSES_IN_BOTH_PIPES, \
p5-memory-accesses-in-both-pipes) \
__PMC_EV(P5, BANK_CONFLICTS, p5-bank-conflicts) \
__PMC_EV(P5, MISALIGNED_DATA_OR_IO_REFERENCES, \
p5-misaligned-data-or-io-references) \
__PMC_EV(P5, CODE_READ, p5-code-read) \
__PMC_EV(P5, CODE_TLB_MISS, p5-code-tlb-miss) \
__PMC_EV(P5, CODE_CACHE_MISS, p5-code-cache-miss) \
__PMC_EV(P5, ANY_SEGMENT_REGISTER_LOADED, \
p5-any-segment-register-loaded) \
__PMC_EV(P5, BRANCHES, p5-branches) \
__PMC_EV(P5, BTB_HITS, p5-btb-hits) \
__PMC_EV(P5, TAKEN_BRANCH_OR_BTB_HIT, \
p5-taken-branch-or-btb-hit) \
__PMC_EV(P5, PIPELINE_FLUSHES, p5-pipeline-flushes) \
__PMC_EV(P5, INSTRUCTIONS_EXECUTED, p5-instructions-executed) \
__PMC_EV(P5, INSTRUCTIONS_EXECUTED_V_PIPE, \
p5-instructions-executed-v-pipe) \
__PMC_EV(P5, BUS_CYCLE_DURATION, p5-bus-cycle-duration) \
__PMC_EV(P5, WRITE_BUFFER_FULL_STALL_DURATION, \
p5-write-buffer-full-stall-duration) \
__PMC_EV(P5, WAITING_FOR_DATA_MEMORY_READ_STALL_DURATION, \
p5-waiting-for-data-memory-read-stall-duration) \
__PMC_EV(P5, STALL_ON_WRITE_TO_AN_E_OR_M_STATE_LINE, \
p5-stall-on-write-to-an-e-or-m-state-line) \
__PMC_EV(P5, LOCKED_BUS_CYCLE, p5-locked-bus-cycle) \
__PMC_EV(P5, IO_READ_OR_WRITE_CYCLE, p5-io-read-or-write-cycle) \
__PMC_EV(P5, NONCACHEABLE_MEMORY_READS, \
p5-noncacheable-memory-reads) \
__PMC_EV(P5, PIPELINE_AGI_STALLS, p5-pipeline-agi-stalls) \
__PMC_EV(P5, FLOPS, p5-flops) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR0_REGISTER, \
p5-breakpoint-match-on-dr0-register) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR1_REGISTER, \
p5-breakpoint-match-on-dr1-register) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR2_REGISTER, \
p5-breakpoint-match-on-dr2-register) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR3_REGISTER, \
p5-breakpoint-match-on-dr3-register) \
__PMC_EV(P5, HARDWARE_INTERRUPTS, p5-hardware-interrupts) \
__PMC_EV(P5, DATA_READ_OR_WRITE, p5-data-read-or-write) \
__PMC_EV(P5, DATA_READ_MISS_OR_WRITE_MISS, \
p5-data-read-miss-or-write-miss) \
__PMC_EV(P5, BUS_OWNERSHIP_LATENCY, p5-bus-ownership-latency) \
__PMC_EV(P5, BUS_OWNERSHIP_TRANSFERS, p5-bus-ownership-transfers) \
__PMC_EV(P5, MMX_INSTRUCTIONS_EXECUTED_U_PIPE, \
p5-mmx-instructions-executed-u-pipe) \
__PMC_EV(P5, MMX_INSTRUCTIONS_EXECUTED_V_PIPE, \
p5-mmx-instructions-executed-v-pipe) \
__PMC_EV(P5, CACHE_M_LINE_SHARING, p5-cache-m-line-sharing) \
__PMC_EV(P5, CACHE_LINE_SHARING, p5-cache-line-sharing) \
__PMC_EV(P5, EMMS_INSTRUCTIONS_EXECUTED, \
p5-emms-instructions-executed) \
__PMC_EV(P5, TRANSITIONS_BETWEEN_MMX_AND_FP_INSTRUCTIONS, \
p5-transitions-between-mmx-and-fp-instructions) \
__PMC_EV(P5, BUS_UTILIZATION_DUE_TO_PROCESSOR_ACTIVITY, \
p5-bus-utilization-due-to-processor-activity) \
__PMC_EV(P5, WRITES_TO_NONCACHEABLE_MEMORY, \
p5-writes-to-noncacheable-memory) \
__PMC_EV(P5, SATURATING_MMX_INSTRUCTIONS_EXECUTED, \
p5-saturating-mmx-instructions-executed) \
__PMC_EV(P5, SATURATIONS_PERFORMED, p5-saturations-performed) \
__PMC_EV(P5, NUMBER_OF_CYCLES_NOT_IN_HALT_STATE, \
p5-number-of-cycles-not-in-halt-state) \
__PMC_EV(P5, DATA_CACHE_TLB_MISS_STALL_DURATION, \
p5-data-cache-tlb-miss-stall-duration) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_READS, \
p5-mmx-instruction-data-reads) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_READ_MISSES, \
p5-mmx-instruction-data-read-misses) \
__PMC_EV(P5, FLOATING_POINT_STALLS_DURATION, \
p5-floating-point-stalls-duration) \
__PMC_EV(P5, TAKEN_BRANCHES, p5-taken-branches) \
__PMC_EV(P5, D1_STARVATION_AND_FIFO_IS_EMPTY, \
p5-d1-starvation-and-fifo-is-empty) \
__PMC_EV(P5, D1_STARVATION_AND_ONLY_ONE_INSTRUCTION_IN_FIFO, \
p5-d1-starvation-and-only-instruction-in-fifo) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_WRITES, \
p5-mmx-instruction-data-writes) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_WRITE_MISSES, \
p5-mmx-instruction-data-write-misses) \
__PMC_EV(P5, PIPELINE_FLUSHES_DUE_TO_WRONG_BRANCH_PREDICTIONS, \
p5-pipeline-flushes-due-to-wrong-branch-predictions) \
__PMC_EV(P5, \
PIPELINE_FLUSHES_DUE_TO_WRONG_BRANCH_PREDICTIONS_RESOLVED_IN_WB_STAGE, \
p5-pipeline-flushes-due-to-wrong-branch-predictions-resolved-in-wb-stage) \
__PMC_EV(P5, MISALIGNED_DATA_MEMORY_REFERENCE_ON_MMX_INSTRUCTIONS, \
p5-misaligned-data-memory-reference-on-mmx-instructions) \
__PMC_EV(P5, PIPELINE_STALL_FOR_MMX_INSTRUCTION_DATA_MEMORY_READS, \
p5-pipeline-stall-for-mmx-instruction-data-memory-reads) \
__PMC_EV(P5, MISPREDICTED_OR_UNPREDICTED_RETURNS, \
p5-mispredicted-or-unpredicted-returns) \
__PMC_EV(P5, PREDICTED_RETURNS, p5-predicted-returns) \
__PMC_EV(P5, MMX_MULTIPLY_UNIT_INTERLOCK, \
p5-mmx-multiply-unit-interlock) \
__PMC_EV(P5, MOVD_MOVQ_STORE_STALL_DUE_TO_PREVIOUS_MMX_OPERATION, \
p5-movd-movq-store-stall-due-to-previous-mmx-operation) \
__PMC_EV(P5, RETURNS, p5-returns) \
__PMC_EV(P5, BTB_FALSE_ENTRIES, p5-btb-false-entries) \
__PMC_EV(P5, BTB_MISS_PREDICTION_ON_NOT_TAKEN_BRANCH, \
p5-btb-miss-prediction-on-not-taken-branch) \
__PMC_EV(P5, \
FULL_WRITE_BUFFER_STALL_DURATION_WHILE_EXECUTING_MMX_INSTRUCTIONS, \
p5-full-write-buffer-stall-duration-while-executing-mmx-instructions) \
__PMC_EV(P5, STALL_ON_MMX_INSTRUCTION_WRITE_TO_E_OR_M_STATE_LINE, \
p5-stall-on-mmx-instruction-write-to-e-o-m-state-line)
#define PMC_EV_P5_FIRST PMC_EV_P5_DATA_READ
#define PMC_EV_P5_LAST \
PMC_EV_P5_STALL_ON_MMX_INSTRUCTION_WRITE_TO_E_OR_M_STATE_LINE
/* timestamp counters. */
#define __PMC_EV_TSC() \
__PMC_EV(TSC, TSC, tsc)
/* All known PMC events */
#define __PMC_EVENTS() \
__PMC_EV_TSC() \
__PMC_EV_K7() \
__PMC_EV_P6() \
__PMC_EV_P4() \
__PMC_EV_K8() \
__PMC_EV_P5() \
#define PMC_EVENT_FIRST PMC_EV_TSC_TSC
#define PMC_EVENT_LAST PMC_EV_P5_LAST
#endif /* _DEV_HWPMC_PMC_EVENTS_H_ */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2003, Joseph Koshy
* Copyright (c) 2003-2005 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,166 +26,58 @@
* $FreeBSD$
*/
/* Machine dependent interfaces */
#ifndef _MACHINE_PMC_MDEP_H
#define _MACHINE_PMC_MDEP_H 1
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
/*
* On the i386 platform we support the following PMCs.
*
* K7 AMD Athlon XP/MP and other 32 bit processors.
* K8 AMD Athlon64 and Opteron PMCs in 32 bit mode.
* PIV Intel P4/HTT and P4/EMT64
* PPRO Intel Pentium Pro, Pentium-II, Pentium-III, Celeron and
* Pentium-M processors
* PENTIUM Intel Pentium MMX.
*/
/* AMD K7 PMCs */
#include <dev/hwpmc/hwpmc_amd.h> /* K7 and K8 */
#include <dev/hwpmc/hwpmc_piv.h>
#include <dev/hwpmc/hwpmc_ppro.h>
#include <dev/hwpmc/hwpmc_pentium.h>
#define K7_NPMCS 5 /* 1 TSC + 4 PMCs */
/*
* Architecture specific extensions to <sys/pmc.h> structures.
*/
#define K7_PMC_COUNTERMASK 0xFF000000
#define K7_PMC_TO_COUNTER(x) (((x) << 24) & K7_PMC_COUNTERMASK)
#define K7_PMC_INVERT (1 << 23)
#define K7_PMC_ENABLE (1 << 22)
#define K7_PMC_INT (1 << 20)
#define K7_PMC_PC (1 << 19)
#define K7_PMC_EDGE (1 << 18)
#define K7_PMC_OS (1 << 17)
#define K7_PMC_USR (1 << 16)
union pmc_md_op_pmcallocate {
struct pmc_md_amd_op_pmcallocate pm_amd;
struct pmc_md_ppro_op_pmcallocate pm_ppro;
struct pmc_md_pentium_op_pmcallocate pm_pentium;
struct pmc_md_p4_op_pmcallocate pm_p4;
uint64_t __pad[4];
};
#define K7_PMC_UNITMASK_M 0x10
#define K7_PMC_UNITMASK_O 0x08
#define K7_PMC_UNITMASK_E 0x04
#define K7_PMC_UNITMASK_S 0x02
#define K7_PMC_UNITMASK_I 0x01
#define K7_PMC_UNITMASK_MOESI 0x1F
#define K7_PMC_UNITMASK 0xFF00
#define K7_PMC_EVENTMASK 0x00FF
#define K7_PMC_TO_UNITMASK(x) (((x) << 8) & K7_PMC_UNITMASK)
#define K7_PMC_TO_EVENTMASK(x) ((x) & 0xFF)
#define K7_VALID_BITS (K7_PMC_COUNTERMASK | K7_PMC_INVERT | \
K7_PMC_ENABLE | K7_PMC_INT | K7_PMC_PC | K7_PMC_EDGE | K7_PMC_OS | \
K7_PMC_USR | K7_PMC_UNITMASK | K7_PMC_EVENTMASK)
/* Intel P4 PMCs */
#define P4_NPMCS 19 /* 1 TSC + 18 PMCS */
#define P4_NESCR 45
#define P4_INVALID_PMC_INDEX -1
#define P4_MAX_ESCR_PER_EVENT 2
#define P4_MAX_PMC_PER_ESCR 3
#define P4_CCCR_OVF (1 << 31)
#define P4_CCCR_CASCADE (1 << 30)
#define P4_CCCR_OVF_PMI_T1 (1 << 27)
#define P4_CCCR_OVF_PMI_T0 (1 << 26)
#define P4_CCCR_FORCE_OVF (1 << 25)
#define P4_CCCR_EDGE (1 << 24)
#define P4_CCCR_THRESHOLD_SHIFT 20
#define P4_CCCR_THRESHOLD_MASK 0x00F00000
#define P4_CCCR_TO_THRESHOLD(C) (((C) << P4_CCCR_THRESHOLD_SHIFT) & \
P4_CCCR_THRESHOLD_MASK)
#define P4_CCCR_COMPLEMENT (1 << 19)
#define P4_CCCR_COMPARE (1 << 18)
#define P4_CCCR_ACTIVE_THREAD_SHIFT 16
#define P4_CCCR_ACTIVE_THREAD_MASK 0x00030000
#define P4_CCCR_TO_ACTIVE_THREAD(T) (((T) << P4_CCCR_ACTIVE_THREAD_SHIFT) & \
P4_CCCR_ACTIVE_THREAD_MASK)
#define P4_CCCR_ESCR_SELECT_SHIFT 13
#define P4_CCCR_ESCR_SELECT_MASK 0x0000E000
#define P4_CCCR_TO_ESCR_SELECT(E) (((E) << P4_CCCR_ESCR_SELECT_SHIFT) & \
P4_CCCR_ESCR_SELECT_MASK)
#define P4_CCCR_ENABLE (1 << 12)
#define P4_CCCR_VALID_BITS (P4_CCCR_OVF | P4_CCCR_CASCADE | \
P4_CCCR_OVF_PMI_T1 | P4_CCCR_OVF_PMI_T0 | P4_CCCR_FORCE_OVF | \
P4_CCCR_EDGE | P4_CCCR_THRESHOLD_MASK | P4_CCCR_COMPLEMENT | \
P4_CCCR_COMPARE | P4_CCCR_ESCR_SELECT_MASK | P4_CCCR_ENABLE)
#define P4_ESCR_EVENT_SELECT_SHIFT 25
#define P4_ESCR_EVENT_SELECT_MASK 0x7E000000
#define P4_ESCR_TO_EVENT_SELECT(E) (((E) << P4_ESCR_EVENT_SELECT_SHIFT) & \
P4_ESCR_EVENT_SELECT_MASK)
#define P4_ESCR_EVENT_MASK_SHIFT 9
#define P4_ESCR_EVENT_MASK_MASK 0x01FFFE00
#define P4_ESCR_TO_EVENT_MASK(M) (((M) << P4_ESCR_EVENT_MASK_SHIFT) & \
P4_ESCR_EVENT_MASK_MASK)
#define P4_ESCR_TAG_VALUE_SHIFT 5
#define P4_ESCR_TAG_VALUE_MASK 0x000001E0
#define P4_ESCR_TO_TAG_VALUE(T) (((T) << P4_ESCR_TAG_VALUE_SHIFT) & \
P4_ESCR_TAG_VALUE_MASK)
#define P4_ESCR_TAG_ENABLE 0x00000010
#define P4_ESCR_T0_OS 0x00000008
#define P4_ESCR_T0_USR 0x00000004
#define P4_ESCR_T1_OS 0x00000002
#define P4_ESCR_T1_USR 0x00000001
#define P4_ESCR_OS P4_ESCR_T0_OS
#define P4_ESCR_USR P4_ESCR_T0_USR
#define P4_ESCR_VALID_BITS (P4_ESCR_EVENT_SELECT_MASK | \
P4_ESCR_EVENT_MASK_MASK | P4_ESCR_TAG_VALUE_MASK | \
P4_ESCR_TAG_ENABLE | P4_ESCR_T0_OS | P4_ESCR_T0_USR | P4_ESCR_T1_OS \
P4_ESCR_T1_USR)
#define P4_PERFCTR_MASK 0xFFFFFFFFFFLL /* 40 bits */
#define P4_CCCR_MSR_FIRST 0x360 /* MSR_BPU_CCCR0 */
#define P4_PERFCTR_MSR_FIRST 0x300 /* MSR_BPU_COUNTER0 */
#define P4_RELOAD_COUNT_TO_PERFCTR_VALUE(V) (1 - (V))
#define P4_PERFCTR_VALUE_TO_RELOAD_COUNT(P) (1 - (P))
/* Intel PPro, Celeron, P-II, P-III, Pentium-M PMCS */
#define P6_NPMCS 3 /* 1 TSC + 2 PMCs */
#define P6_EVSEL_CMASK_MASK 0xFF000000
#define P6_EVSEL_TO_CMASK(C) (((C) & 0xFF) << 24)
#define P6_EVSEL_INV (1 << 23)
#define P6_EVSEL_EN (1 << 22)
#define P6_EVSEL_INT (1 << 20)
#define P6_EVSEL_PC (1 << 19)
#define P6_EVSEL_E (1 << 18)
#define P6_EVSEL_OS (1 << 17)
#define P6_EVSEL_USR (1 << 16)
#define P6_EVSEL_UMASK_MASK 0x0000FF00
#define P6_EVSEL_TO_UMASK(U) (((U) & 0xFF) << 8)
#define P6_EVSEL_EVENT_SELECT(ES) ((ES) & 0xFF)
#define P6_EVSEL_RESERVED (1 << 21)
#define P6_MSR_EVSEL0 0x0186
#define P6_MSR_EVSEL1 0x0187
#define P6_MSR_PERFCTR0 0x00C1
#define P6_MSR_PERFCTR1 0x00C2
#define P6_PERFCTR_MASK 0xFFFFFFFFFFLL /* 40 bits */
/* Intel Pentium PMCs */
#define PENTIUM_NPMCS 3 /* 1 TSC + 2 PMCs */
#define PENTIUM_CESR_PC1 (1 << 25)
#define PENTIUM_CESR_CC1_MASK 0x01C00000
#define PENTIUM_CESR_TO_CC1(C) (((C) & 0x07) << 22)
#define PENTIUM_CESR_ES1_MASK 0x003F0000
#define PENTIUM_CESR_TO_ES1(E) (((E) & 0x3F) << 16)
#define PENTIUM_CESR_PC0 (1 << 9)
#define PENTIUM_CESR_CC0_MASK 0x000001C0
#define PENTIUM_CESR_TO_CC0(C) (((C) & 0x07) << 6)
#define PENTIUM_CESR_ES0_MASK 0x0000003F
#define PENTIUM_CESR_TO_ES0(E) ((E) & 0x3F)
#define PENTIUM_CESR_RESERVED 0xFC00FC00
#define PENTIUM_MSR_CESR 0x11
#define PENTIUM_MSR_CTR0 0x12
#define PENTIUM_MSR_CTR1 0x13
/* Logging */
#define PMCLOG_READADDR PMCLOG_READ32
#define PMCLOG_EMITADDR PMCLOG_EMIT32
#ifdef _KERNEL
/* MD extension for 'struct pmc' */
union pmc_md_pmc {
struct pmc_md_amd_pmc pm_amd;
struct pmc_md_ppro_pmc pm_ppro;
struct pmc_md_pentium_pmc pm_pentium;
struct pmc_md_p4_pmc pm_p4;
};
struct pmc;
/*
* Prototypes
*/
#if defined(__i386__)
struct pmc_mdep *pmc_amd_initialize(void); /* AMD K7/K8 PMCs */
struct pmc_mdep *pmc_intel_initialize(void); /* Intel PMCs */
int pmc_initialize_p4(struct pmc_mdep *); /* Pentium IV PMCs */
int pmc_initialize_p5(struct pmc_mdep *); /* Pentium PMCs */
int pmc_initialize_p6(struct pmc_mdep *); /* Pentium Pro PMCs */
#endif /* defined(__i386__) */
void pmc_x86_lapic_enable_pmc_interrupt(void);
#endif /* _KERNEL */
#endif /* _MACHINE_PMC_MDEP_H */

View File

@ -7,4 +7,18 @@
#ifndef _MACHINE_PMC_MDEP_H_
#define _MACHINE_PMC_MDEP_H_
union pmc_md_op_pmcallocate {
uint64_t __pad[4];
};
/* Logging */
#define PMCLOG_READADDR PMCLOG_READ64
#define PMCLOG_EMITADDR PMCLOG_EMIT64
#if _KERNEL
union pmc_md_pmc {
};
#endif
#endif /* !_MACHINE_PMC_MDEP_H_ */

View File

@ -670,14 +670,15 @@ do_execve(td, args, mac_p)
#ifdef HWPMC_HOOKS
/*
* Check if the process is using PMCs and if so do exec() time
* Check if system-wide sampling is in effect or if the
* current process is using PMCs. If so, do exec() time
* processing. This processing needs to happen AFTER the
* P_INEXEC flag is cleared.
*
* The proc lock needs to be released before taking the PMC
* SX.
*/
if (PMC_PROC_IS_USING_PMCS(p)) {
if (PMC_SYSTEM_SAMPLING_ACTIVE() || PMC_PROC_IS_USING_PMCS(p)) {
PROC_UNLOCK(p);
PMC_CALL_HOOK_X(td, PMC_FN_PROCESS_EXEC,
(void *) &credential_changing);

View File

@ -37,7 +37,14 @@ int (*pmc_hook)(struct thread *td, int function, void *arg) = NULL;
/* Interrupt handler */
int (*pmc_intr)(int cpu, uintptr_t pc, int usermode) = NULL;
cpumask_t pmc_cpumask;
volatile cpumask_t pmc_cpumask;
/*
* A global count of SS mode PMCs. When non-zero, this means that
* we have processes that are sampling the system as a whole.
*/
volatile int pmc_ss_count;
/*
* Since PMC(4) may not be loaded in the current kernel, the

View File

@ -1,4 +1,4 @@
#
#
# $FreeBSD$
#
@ -6,16 +6,38 @@
KMOD= hwpmc
SRCS= hwpmc_mod.c
SRCS= hwpmc_mod.c hwpmc_logging.c vnode_if.h
WARNS?= 2
.if ${MACHINE_ARCH} == "i386"
SRCS+= hwpmc_amd.c hwpmc_intel.c hwpmc_piv.c hwpmc_ppro.c hwpmc_pentium.c
.if ${MACHINE_ARCH} == "alpha"
SRCS+= hwpmc_alpha.c
.endif
.if ${MACHINE_ARCH} == "amd64"
SRCS+= hwpmc_amd.c
SRCS+= hwpmc_amd.c hwpmc_piv.c hwpmc_x86.c
SRCS+= device_if.h bus_if.h
.endif
.if ${MACHINE_ARCH} == "arm"
SRCS+= hwpmc_arm.c
.endif
.if ${MACHINE_ARCH} == "i386"
SRCS+= hwpmc_amd.c hwpmc_piv.c hwpmc_ppro.c hwpmc_pentium.c hwpmc_x86.c
SRCS+= device_if.h bus_if.h
.endif
.if ${MACHINE_ARCH} == "ia64"
SRCS+= hwpmc_ia64.c
.endif
.if ${MACHINE_ARCH} == "powerpc"
SRCS+= hwpmc_powerpc.c
.endif
.if ${MACHINE_ARCH} == "sparc64"
SRCS+= hwpmc_sparc64.c
.endif
.include <bsd.kmod.mk>

View File

@ -7,4 +7,19 @@
#ifndef _MACHINE_PMC_MDEP_H_
#define _MACHINE_PMC_MDEP_H_
union pmc_md_op_pmcallocate {
uint64_t __pad[4];
};
/* Logging */
#define PMCLOG_READADDR PMCLOG_READ32
#define PMCLOG_EMITADDR PMCLOG_EMIT32
#if _KERNEL
union pmc_md_pmc {
};
#endif
#endif /* !_MACHINE_PMC_MDEP_H_ */

View File

@ -7,4 +7,18 @@
#ifndef _MACHINE_PMC_MDEP_H_
#define _MACHINE_PMC_MDEP_H_
union pmc_md_op_pmcallocate {
uint64_t __pad[4];
};
/* Logging */
#define PMCLOG_READADDR PMCLOG_READ64
#define PMCLOG_EMITADDR PMCLOG_EMIT64
#if _KERNEL
union pmc_md_pmc {
};
#endif
#endif /* !_MACHINE_PMC_MDEP_H_ */

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2003, Joseph Koshy
* Copyright (c) 2003-2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -29,17 +29,30 @@
#ifndef _SYS_PMC_H_
#define _SYS_PMC_H_
#include <dev/hwpmc/pmc_events.h>
#include <machine/pmc_mdep.h>
#include <machine/profile.h>
#define PMC_MODULE_NAME "hwpmc"
#define PMC_NAME_MAX 16 /* HW counter name size */
#define PMC_CLASS_MAX 4 /* #classes of PMCs in a system */
/* Kernel<->userland API version number [MMmmpppp] */
/*
* Kernel<->userland API version number [MMmmpppp]
*
* Major numbers are to be incremented when an incompatible change to
* the ABI occurs that older clients will not be able to handle.
*
* Minor numbers are incremented when a backwards compatible change
* occurs that allows older correct programs to run unchanged. For
* example, when support for a new PMC type is added.
*
* The patch version is incremented for every bug fix.
*/
#define PMC_VERSION_MAJOR 0x01
#define PMC_VERSION_MINOR 0x01
#define PMC_VERSION_PATCH 0x0002
#define PMC_VERSION_MINOR 0x02
#define PMC_VERSION_PATCH 0x0003
#define PMC_VERSION (PMC_VERSION_MAJOR << 24 | \
PMC_VERSION_MINOR << 16 | PMC_VERSION_PATCH)
@ -197,511 +210,6 @@ enum pmc_disp {
#define PMC_DISP_FIRST PMC_DISP_STANDALONE
#define PMC_DISP_LAST PMC_DISP_THREAD
/*
* PMC event codes
*
* __PMC_EV(CLASS, SYMBOLIC-NAME, VALUE, READABLE-NAME)
*/
/*
* AMD K7 Events, from "The AMD Athlon(tm) Processor x86 Code
* Optimization Guide" [Doc#22007K, Feb 2002]
*/
#define __PMC_EV_K7() \
__PMC_EV(K7, DC_ACCESSES, k7-dc-accesses) \
__PMC_EV(K7, DC_MISSES, k7-dc-misses) \
__PMC_EV(K7, DC_REFILLS_FROM_L2, k7-dc-refills-from-l2) \
__PMC_EV(K7, DC_REFILLS_FROM_SYSTEM, k7-dc-refills-from-system) \
__PMC_EV(K7, DC_WRITEBACKS, k7-dc-writebacks) \
__PMC_EV(K7, L1_DTLB_MISS_AND_L2_DTLB_HITS, \
k7-l1-dtlb-miss-and-l2-dtlb-hits) \
__PMC_EV(K7, L1_AND_L2_DTLB_MISSES, k7-l1-and-l2-dtlb-misses) \
__PMC_EV(K7, MISALIGNED_REFERENCES, k7-misaligned-references) \
__PMC_EV(K7, IC_FETCHES, k7-ic-fetches) \
__PMC_EV(K7, IC_MISSES, k7-ic-misses) \
__PMC_EV(K7, L1_ITLB_MISSES, k7-l1-itlb-misses) \
__PMC_EV(K7, L1_L2_ITLB_MISSES, k7-l1-l2-itlb-misses) \
__PMC_EV(K7, RETIRED_INSTRUCTIONS, k7-retired-instructions) \
__PMC_EV(K7, RETIRED_OPS, k7-retired-ops) \
__PMC_EV(K7, RETIRED_BRANCHES, k7-retired-branches) \
__PMC_EV(K7, RETIRED_BRANCHES_MISPREDICTED, \
k7-retired-branches-mispredicted) \
__PMC_EV(K7, RETIRED_TAKEN_BRANCHES, k7-retired-taken-branches) \
__PMC_EV(K7, RETIRED_TAKEN_BRANCHES_MISPREDICTED, \
k7-retired-taken-branches-mispredicted) \
__PMC_EV(K7, RETIRED_FAR_CONTROL_TRANSFERS, \
k7-retired-far-control-transfers) \
__PMC_EV(K7, RETIRED_RESYNC_BRANCHES, k7-retired-resync-branches) \
__PMC_EV(K7, INTERRUPTS_MASKED_CYCLES, k7-interrupts-masked-cycles) \
__PMC_EV(K7, INTERRUPTS_MASKED_WHILE_PENDING_CYCLES, \
k7-interrupts-masked-while-pending-cycles) \
__PMC_EV(K7, HARDWARE_INTERRUPTS, k7-hardware-interrupts)
#define PMC_EV_K7_FIRST PMC_EV_K7_DC_ACCESSES
#define PMC_EV_K7_LAST PMC_EV_K7_HARDWARE_INTERRUPTS
/*
* Intel P4 Events, from "IA-32 Intel(r) Architecture Software
* Developer's Manual, Volume 3: System Programming Guide" [245472-012]
*/
#define __PMC_EV_P4() \
__PMC_EV(P4, TC_DELIVER_MODE, p4-tc-deliver-mode) \
__PMC_EV(P4, BPU_FETCH_REQUEST, p4-bpu-fetch-request) \
__PMC_EV(P4, ITLB_REFERENCE, p4-itlb-reference) \
__PMC_EV(P4, MEMORY_CANCEL, p4-memory-cancel) \
__PMC_EV(P4, MEMORY_COMPLETE, p4-memory-complete) \
__PMC_EV(P4, LOAD_PORT_REPLAY, p4-load-port-replay) \
__PMC_EV(P4, STORE_PORT_REPLAY, p4-store-port-replay) \
__PMC_EV(P4, MOB_LOAD_REPLAY, p4-mob-load-replay) \
__PMC_EV(P4, PAGE_WALK_TYPE, p4-page-walk-type) \
__PMC_EV(P4, BSQ_CACHE_REFERENCE, p4-bsq-cache-reference) \
__PMC_EV(P4, IOQ_ALLOCATION, p4-ioq-allocation) \
__PMC_EV(P4, IOQ_ACTIVE_ENTRIES, p4-ioq-active-entries) \
__PMC_EV(P4, FSB_DATA_ACTIVITY, p4-fsb-data-activity) \
__PMC_EV(P4, BSQ_ALLOCATION, p4-bsq-allocation) \
__PMC_EV(P4, BSQ_ACTIVE_ENTRIES, p4-bsq-active-entries) \
__PMC_EV(P4, SSE_INPUT_ASSIST, p4-sse-input-assist) \
__PMC_EV(P4, PACKED_SP_UOP, p4-packed-sp-uop) \
__PMC_EV(P4, PACKED_DP_UOP, p4-packed-dp-uop) \
__PMC_EV(P4, SCALAR_SP_UOP, p4-scalar-sp-uop) \
__PMC_EV(P4, SCALAR_DP_UOP, p4-scalar-dp-uop) \
__PMC_EV(P4, 64BIT_MMX_UOP, p4-64bit-mmx-uop) \
__PMC_EV(P4, 128BIT_MMX_UOP, p4-128bit-mmx-uop) \
__PMC_EV(P4, X87_FP_UOP, p4-x87-fp-uop) \
__PMC_EV(P4, X87_SIMD_MOVES_UOP, p4-x87-simd-moves-uop) \
__PMC_EV(P4, GLOBAL_POWER_EVENTS, p4-global-power-events) \
__PMC_EV(P4, TC_MS_XFER, p4-tc-ms-xfer) \
__PMC_EV(P4, UOP_QUEUE_WRITES, p4-uop-queue-writes) \
__PMC_EV(P4, RETIRED_MISPRED_BRANCH_TYPE, \
p4-retired-mispred-branch-type) \
__PMC_EV(P4, RETIRED_BRANCH_TYPE, p4-retired-branch-type) \
__PMC_EV(P4, RESOURCE_STALL, p4-resource-stall) \
__PMC_EV(P4, WC_BUFFER, p4-wc-buffer) \
__PMC_EV(P4, B2B_CYCLES, p4-b2b-cycles) \
__PMC_EV(P4, BNR, p4-bnr) \
__PMC_EV(P4, SNOOP, p4-snoop) \
__PMC_EV(P4, RESPONSE, p4-response) \
__PMC_EV(P4, FRONT_END_EVENT, p4-front-end-event) \
__PMC_EV(P4, EXECUTION_EVENT, p4-execution-event) \
__PMC_EV(P4, REPLAY_EVENT, p4-replay-event) \
__PMC_EV(P4, INSTR_RETIRED, p4-instr-retired) \
__PMC_EV(P4, UOPS_RETIRED, p4-uops-retired) \
__PMC_EV(P4, UOP_TYPE, p4-uop-type) \
__PMC_EV(P4, BRANCH_RETIRED, p4-branch-retired) \
__PMC_EV(P4, MISPRED_BRANCH_RETIRED, p4-mispred-branch-retired) \
__PMC_EV(P4, X87_ASSIST, p4-x87-assist) \
__PMC_EV(P4, MACHINE_CLEAR, p4-machine-clear)
#define PMC_EV_P4_FIRST PMC_EV_P4_TC_DELIVER_MODE
#define PMC_EV_P4_LAST PMC_EV_P4_MACHINE_CLEAR
/* Intel Pentium Pro, P-II, P-III and Pentium-M style events */
#define __PMC_EV_P6() \
__PMC_EV(P6, DATA_MEM_REFS, p6-data-mem-refs) \
__PMC_EV(P6, DCU_LINES_IN, p6-dcu-lines-in) \
__PMC_EV(P6, DCU_M_LINES_IN, p6-dcu-m-lines-in) \
__PMC_EV(P6, DCU_M_LINES_OUT, p6-dcu-m-lines-out) \
__PMC_EV(P6, DCU_MISS_OUTSTANDING, p6-dcu-miss-outstanding) \
__PMC_EV(P6, IFU_FETCH, p6-ifu-fetch) \
__PMC_EV(P6, IFU_FETCH_MISS, p6-ifu-fetch-miss) \
__PMC_EV(P6, ITLB_MISS, p6-itlb-miss) \
__PMC_EV(P6, IFU_MEM_STALL, p6-ifu-mem-stall) \
__PMC_EV(P6, ILD_STALL, p6-ild-stall) \
__PMC_EV(P6, L2_IFETCH, p6-l2-ifetch) \
__PMC_EV(P6, L2_LD, p6-l2-ld) \
__PMC_EV(P6, L2_ST, p6-l2-st) \
__PMC_EV(P6, L2_LINES_IN, p6-l2-lines-in) \
__PMC_EV(P6, L2_LINES_OUT, p6-l2-lines-out) \
__PMC_EV(P6, L2_M_LINES_INM, p6-l2-m-lines-inm) \
__PMC_EV(P6, L2_M_LINES_OUTM, p6-l2-m-lines-outm) \
__PMC_EV(P6, L2_RQSTS, p6-l2-rqsts) \
__PMC_EV(P6, L2_ADS, p6-l2-ads) \
__PMC_EV(P6, L2_DBUS_BUSY, p6-l2-dbus-busy) \
__PMC_EV(P6, L2_DBUS_BUSY_RD, p6-l2-dbus-busy-rd) \
__PMC_EV(P6, BUS_DRDY_CLOCKS, p6-bus-drdy-clocks) \
__PMC_EV(P6, BUS_LOCK_CLOCKS, p6-bus-lock-clocks) \
__PMC_EV(P6, BUS_REQ_OUTSTANDING, p6-bus-req-outstanding) \
__PMC_EV(P6, BUS_TRAN_BRD, p6-bus-tran-brd) \
__PMC_EV(P6, BUS_TRAN_RFO, p6-bus-tran-rfo) \
__PMC_EV(P6, BUS_TRANS_WB, p6-bus-trans-wb) \
__PMC_EV(P6, BUS_TRAN_IFETCH, p6-bus-tran-ifetch) \
__PMC_EV(P6, BUS_TRAN_INVAL, p6-bus-tran-inval) \
__PMC_EV(P6, BUS_TRAN_PWR, p6-bus-tran-pwr) \
__PMC_EV(P6, BUS_TRANS_P, p6-bus-trans-p) \
__PMC_EV(P6, BUS_TRANS_IO, p6-bus-trans-io) \
__PMC_EV(P6, BUS_TRAN_DEF, p6-bus-tran-def) \
__PMC_EV(P6, BUS_TRAN_BURST, p6-bus-tran-burst) \
__PMC_EV(P6, BUS_TRAN_ANY, p6-bus-tran-any) \
__PMC_EV(P6, BUS_TRAN_MEM, p6-bus-tran-mem) \
__PMC_EV(P6, BUS_DATA_RCV, p6-bus-data-rcv) \
__PMC_EV(P6, BUS_BNR_DRV, p6-bus-bnr-drv) \
__PMC_EV(P6, BUS_HIT_DRV, p6-bus-hit-drv) \
__PMC_EV(P6, BUS_HITM_DRV, p6-bus-hitm-drv) \
__PMC_EV(P6, BUS_SNOOP_STALL, p6-bus-snoop-stall) \
__PMC_EV(P6, FLOPS, p6-flops) \
__PMC_EV(P6, FP_COMPS_OPS_EXE, p6-fp-comps-ops-exe) \
__PMC_EV(P6, FP_ASSIST, p6-fp-assist) \
__PMC_EV(P6, MUL, p6-mul) \
__PMC_EV(P6, DIV, p6-div) \
__PMC_EV(P6, CYCLES_DIV_BUSY, p6-cycles-div-busy) \
__PMC_EV(P6, LD_BLOCKS, p6-ld-blocks) \
__PMC_EV(P6, SB_DRAINS, p6-sb-drains) \
__PMC_EV(P6, MISALIGN_MEM_REF, p6-misalign-mem-ref) \
__PMC_EV(P6, EMON_KNI_PREF_DISPATCHED, p6-emon-kni-pref-dispatched) \
__PMC_EV(P6, EMON_KNI_PREF_MISS, p6-emon-kni-pref-miss) \
__PMC_EV(P6, INST_RETIRED, p6-inst-retired) \
__PMC_EV(P6, UOPS_RETIRED, p6-uops-retired) \
__PMC_EV(P6, INST_DECODED, p6-inst-decoded) \
__PMC_EV(P6, EMON_KNI_INST_RETIRED, p6-emon-kni-inst-retired) \
__PMC_EV(P6, EMON_KNI_COMP_INST_RET, p6-emon-kni-comp-inst-ret) \
__PMC_EV(P6, HW_INT_RX, p6-hw-int-rx) \
__PMC_EV(P6, CYCLES_INT_MASKED, p6-cycles-int-masked) \
__PMC_EV(P6, CYCLES_INT_PENDING_AND_MASKED, \
p6-cycles-in-pending-and-masked) \
__PMC_EV(P6, BR_INST_RETIRED, p6-br-inst-retired) \
__PMC_EV(P6, BR_MISS_PRED_RETIRED, p6-br-miss-pred-retired) \
__PMC_EV(P6, BR_TAKEN_RETIRED, p6-br-taken-retired) \
__PMC_EV(P6, BR_MISS_PRED_TAKEN_RET, p6-br-miss-pred-taken-ret) \
__PMC_EV(P6, BR_INST_DECODED, p6-br-inst-decoded) \
__PMC_EV(P6, BTB_MISSES, p6-btb-misses) \
__PMC_EV(P6, BR_BOGUS, p6-br-bogus) \
__PMC_EV(P6, BACLEARS, p6-baclears) \
__PMC_EV(P6, RESOURCE_STALLS, p6-resource-stalls) \
__PMC_EV(P6, PARTIAL_RAT_STALLS, p6-partial-rat-stalls) \
__PMC_EV(P6, SEGMENT_REG_LOADS, p6-segment-reg-loads) \
__PMC_EV(P6, CPU_CLK_UNHALTED, p6-cpu-clk-unhalted) \
__PMC_EV(P6, MMX_INSTR_EXEC, p6-mmx-instr-exec) \
__PMC_EV(P6, MMX_SAT_INSTR_EXEC, p6-mmx-sat-instr-exec) \
__PMC_EV(P6, MMX_UOPS_EXEC, p6-mmx-uops-exec) \
__PMC_EV(P6, MMX_INSTR_TYPE_EXEC, p6-mmx-instr-type-exec) \
__PMC_EV(P6, FP_MMX_TRANS, p6-fp-mmx-trans) \
__PMC_EV(P6, MMX_ASSIST, p6-mmx-assist) \
__PMC_EV(P6, MMX_INSTR_RET, p6-mmx-instr-ret) \
__PMC_EV(P6, SEG_RENAME_STALLS, p6-seg-rename-stalls) \
__PMC_EV(P6, SEG_REG_RENAMES, p6-seg-reg-renames) \
__PMC_EV(P6, RET_SEG_RENAMES, p6-ret-seg-renames) \
__PMC_EV(P6, EMON_EST_TRANS, p6-emon-est-trans) \
__PMC_EV(P6, EMON_THERMAL_TRIP, p6-emon-thermal-trip) \
__PMC_EV(P6, BR_INST_EXEC, p6-br-inst-exec) \
__PMC_EV(P6, BR_MISSP_EXEC, p6-br-missp-exec) \
__PMC_EV(P6, BR_BAC_MISSP_EXEC, p6-br-bac-missp-exec) \
__PMC_EV(P6, BR_CND_EXEC, p6-br-cnd-exec) \
__PMC_EV(P6, BR_CND_MISSP_EXEC, p6-br-cnd-missp-exec) \
__PMC_EV(P6, BR_IND_EXEC, p6-br-ind-exec) \
__PMC_EV(P6, BR_IND_MISSP_EXEC, p6-br-ind-missp-exec) \
__PMC_EV(P6, BR_RET_EXEC, p6-br-ret-exec) \
__PMC_EV(P6, BR_RET_MISSP_EXEC, p6-br-ret-missp-exec) \
__PMC_EV(P6, BR_RET_BAC_MISSP_EXEC, p6-br-ret-bac-missp-exec) \
__PMC_EV(P6, BR_CALL_EXEC, p6-br-call-exec) \
__PMC_EV(P6, BR_CALL_MISSP_EXEC, p6-br-call-missp-exec) \
__PMC_EV(P6, BR_IND_CALL_EXEC, p6-br-ind-call-exec) \
__PMC_EV(P6, EMON_SIMD_INSTR_RETIRED, p6-emon-simd-instr-retired) \
__PMC_EV(P6, EMON_SYNCH_UOPS, p6-emon-synch-uops) \
__PMC_EV(P6, EMON_ESP_UOPS, p6-emon-esp-uops) \
__PMC_EV(P6, EMON_FUSED_UOPS_RET, p6-emon-fused-uops-ret) \
__PMC_EV(P6, EMON_UNFUSION, p6-emon-unfusion) \
__PMC_EV(P6, EMON_PREF_RQSTS_UP, p6-emon-pref-rqsts-up) \
__PMC_EV(P6, EMON_PREF_RQSTS_DN, p6-emon-pref-rqsts-dn) \
__PMC_EV(P6, EMON_SSE_SSE2_INST_RETIRED, \
p6-emon-sse-sse2-inst-retired) \
__PMC_EV(P6, EMON_SSE_SSE2_COMP_INST_RETIRED, \
p6-emon-sse-sse2-comp-inst-retired)
#define PMC_EV_P6_FIRST PMC_EV_P6_DATA_MEM_REFS
#define PMC_EV_P6_LAST PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED
/* AMD K8 PMCs */
#define __PMC_EV_K8() \
__PMC_EV(K8, FP_DISPATCHED_FPU_OPS, k8-fp-dispatched-fpu-ops) \
__PMC_EV(K8, FP_CYCLES_WITH_NO_FPU_OPS_RETIRED, \
k8-fp-cycles-with-no-fpu-ops-retired) \
__PMC_EV(K8, FP_DISPATCHED_FPU_FAST_FLAG_OPS, \
k8-fp-dispatched-fpu-fast-flag-ops) \
__PMC_EV(K8, LS_SEGMENT_REGISTER_LOAD, k8-ls-segment-register-load) \
__PMC_EV(K8, LS_MICROARCHITECTURAL_RESYNC_BY_SELF_MODIFYING_CODE, \
k8-ls-microarchitectural-resync-by-self-modifying-code) \
__PMC_EV(K8, LS_MICROARCHITECTURAL_RESYNC_BY_SNOOP, \
k8-ls-microarchitectural-resync-by-snoop) \
__PMC_EV(K8, LS_BUFFER2_FULL, k8-ls-buffer2-full) \
__PMC_EV(K8, LS_LOCKED_OPERATION, k8-ls-locked-operation) \
__PMC_EV(K8, LS_MICROARCHITECTURAL_LATE_CANCEL, \
k8-ls-microarchitectural-late-cancel) \
__PMC_EV(K8, LS_RETIRED_CFLUSH_INSTRUCTIONS, \
k8-ls-retired-cflush-instructions) \
__PMC_EV(K8, LS_RETIRED_CPUID_INSTRUCTIONS, \
k8-ls-retired-cpuid-instructions) \
__PMC_EV(K8, DC_ACCESS, k8-dc-access) \
__PMC_EV(K8, DC_MISS, k8-dc-miss) \
__PMC_EV(K8, DC_REFILL_FROM_L2, k8-dc-refill-from-l2) \
__PMC_EV(K8, DC_REFILL_FROM_SYSTEM, k8-dc-refill-from-system) \
__PMC_EV(K8, DC_COPYBACK, k8-dc-copyback) \
__PMC_EV(K8, DC_L1_DTLB_MISS_AND_L2_DTLB_HIT, \
k8-dc-l1-dtlb-miss-and-l2-dtlb-hit) \
__PMC_EV(K8, DC_L1_DTLB_MISS_AND_L2_DTLB_MISS, \
k8-dc-l1-dtlb-miss-and-l2-dtlb-miss) \
__PMC_EV(K8, DC_MISALIGNED_DATA_REFERENCE, \
k8-dc-misaligned-data-reference) \
__PMC_EV(K8, DC_MICROARCHITECTURAL_LATE_CANCEL, \
k8-dc-microarchitectural-late-cancel-of-an-access) \
__PMC_EV(K8, DC_MICROARCHITECTURAL_EARLY_CANCEL, \
k8-dc-microarchitectural-early-cancel-of-an-access) \
__PMC_EV(K8, DC_ONE_BIT_ECC_ERROR, k8-dc-one-bit-ecc-error) \
__PMC_EV(K8, DC_DISPATCHED_PREFETCH_INSTRUCTIONS, \
k8-dc-dispatched-prefetch-instructions) \
__PMC_EV(K8, DC_DCACHE_ACCESSES_BY_LOCKS, \
k8-dc-dcache-accesses-by-locks) \
__PMC_EV(K8, BU_CPU_CLK_UNHALTED, k8-bu-cpu-clk-unhalted) \
__PMC_EV(K8, BU_INTERNAL_L2_REQUEST, k8-bu-internal-l2-request) \
__PMC_EV(K8, BU_FILL_REQUEST_L2_MISS, k8-bu-fill-request-l2-miss) \
__PMC_EV(K8, BU_FILL_INTO_L2, k8-bu-fill-into-l2) \
__PMC_EV(K8, IC_FETCH, k8-ic-fetch) \
__PMC_EV(K8, IC_MISS, k8-ic-miss) \
__PMC_EV(K8, IC_REFILL_FROM_L2, k8-ic-refill-from-l2) \
__PMC_EV(K8, IC_REFILL_FROM_SYSTEM, k8-ic-refill-from-system) \
__PMC_EV(K8, IC_L1_ITLB_MISS_AND_L2_ITLB_HIT, \
k8-ic-l1-itlb-miss-and-l2-itlb-hit) \
__PMC_EV(K8, IC_L1_ITLB_MISS_AND_L2_ITLB_MISS, \
k8-ic-l1-itlb-miss-and-l2-itlb-miss) \
__PMC_EV(K8, IC_MICROARCHITECTURAL_RESYNC_BY_SNOOP, \
k8-ic-microarchitectural-resync-by-snoop) \
__PMC_EV(K8, IC_INSTRUCTION_FETCH_STALL, \
k8-ic-instruction-fetch-stall) \
__PMC_EV(K8, IC_RETURN_STACK_HIT, k8-ic-return-stack-hit) \
__PMC_EV(K8, IC_RETURN_STACK_OVERFLOW, k8-ic-return-stack-overflow) \
__PMC_EV(K8, FR_RETIRED_X86_INSTRUCTIONS, \
k8-fr-retired-x86-instructions) \
__PMC_EV(K8, FR_RETIRED_UOPS, k8-fr-retired-uops) \
__PMC_EV(K8, FR_RETIRED_BRANCHES, k8-fr-retired-branches) \
__PMC_EV(K8, FR_RETIRED_BRANCHES_MISPREDICTED, \
k8-fr-retired-branches-mispredicted) \
__PMC_EV(K8, FR_RETIRED_TAKEN_BRANCHES, \
k8-fr-retired-taken-branches) \
__PMC_EV(K8, FR_RETIRED_TAKEN_BRANCHES_MISPREDICTED, \
k8-fr-retired-taken-branches-mispredicted) \
__PMC_EV(K8, FR_RETIRED_FAR_CONTROL_TRANSFERS, \
k8-fr-retired-far-control-transfers) \
__PMC_EV(K8, FR_RETIRED_RESYNCS, k8-fr-retired-resyncs) \
__PMC_EV(K8, FR_RETIRED_NEAR_RETURNS, k8-fr-retired-near-returns) \
__PMC_EV(K8, FR_RETIRED_NEAR_RETURNS_MISPREDICTED, \
k8-fr-retired-near-returns-mispredicted) \
__PMC_EV(K8, \
FR_RETIRED_TAKEN_BRANCHES_MISPREDICTED_BY_ADDR_MISCOMPARE, \
k8-fr-retired-taken-branches-mispredicted-by-addr-miscompare) \
__PMC_EV(K8, FR_RETIRED_FPU_INSTRUCTIONS, \
k8-fr-retired-fpu-instructions) \
__PMC_EV(K8, FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS, \
k8-fr-retired-fastpath-double-op-instructions) \
__PMC_EV(K8, FR_INTERRUPTS_MASKED_CYCLES, \
k8-fr-interrupts-masked-cycles) \
__PMC_EV(K8, FR_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES, \
k8-fr-interrupts-masked-while-pending-cycles) \
__PMC_EV(K8, FR_TAKEN_HARDWARE_INTERRUPTS, \
k8-fr-taken-hardware-interrupts) \
__PMC_EV(K8, FR_DECODER_EMPTY, k8-fr-decoder-empty) \
__PMC_EV(K8, FR_DISPATCH_STALLS, k8-fr-dispatch-stalls) \
__PMC_EV(K8, FR_DISPATCH_STALL_FROM_BRANCH_ABORT_TO_RETIRE, \
k8-fr-dispatch-stall-from-branch-abort-to-retire) \
__PMC_EV(K8, FR_DISPATCH_STALL_FOR_SERIALIZATION, \
k8-fr-dispatch-stall-for-serialization) \
__PMC_EV(K8, FR_DISPATCH_STALL_FOR_SEGMENT_LOAD, \
k8-fr-dispatch-stall-for-segment-load) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_REORDER_BUFFER_IS_FULL, \
k8-fr-dispatch-stall-when-reorder-buffer-is-full) \
__PMC_EV(K8, \
FR_DISPATCH_STALL_WHEN_RESERVATION_STATIONS_ARE_FULL, \
k8-fr-dispatch-stall-when-reservation-stations-are-full) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_FPU_IS_FULL, \
k8-fr-dispatch-stall-when-fpu-is-full) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_LS_IS_FULL, \
k8-fr-dispatch-stall-when-ls-is-full) \
__PMC_EV(K8, FR_DISPATCH_STALL_WHEN_WAITING_FOR_ALL_TO_BE_QUIET, \
k8-fr-dispatch-stall-when-waiting-for-all-to-be-quiet) \
__PMC_EV(K8, \
FR_DISPATCH_STALL_WHEN_FAR_XFER_OR_RESYNC_BRANCH_PENDING, \
k8-fr-dispatch-stall-when-far-xfer-or-resync-branch-pending) \
__PMC_EV(K8, FR_FPU_EXCEPTIONS, k8-fr-fpu-exceptions) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR0, \
k8-fr-number-of-breakpoints-for-dr0) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR1, \
k8-fr-number-of-breakpoints-for-dr1) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR2, \
k8-fr-number-of-breakpoints-for-dr2) \
__PMC_EV(K8, FR_NUMBER_OF_BREAKPOINTS_FOR_DR3, \
k8-fr-number-of-breakpoints-for-dr3) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_PAGE_ACCESS_EVENT, \
k8-nb-memory-controller-page-access-event) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_PAGE_TABLE_OVERFLOW, \
k8-nb-memory-controller-page-table-overflow) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_DRAM_COMMAND_SLOTS_MISSED, \
k8-nb-memory-controller-dram-slots-missed) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_TURNAROUND, \
k8-nb-memory-controller-turnaround) \
__PMC_EV(K8, NB_MEMORY_CONTROLLER_BYPASS_SATURATION, \
k8-nb-memory-controller-bypass-saturation) \
__PMC_EV(K8, NB_SIZED_COMMANDS, k8-nb-sized-commands) \
__PMC_EV(K8, NB_PROBE_RESULT, k8-nb-probe-result) \
__PMC_EV(K8, NB_HT_BUS0_BANDWIDTH, k8-nb-ht-bus0-bandwidth) \
__PMC_EV(K8, NB_HT_BUS1_BANDWIDTH, k8-nb-ht-bus1-bandwidth) \
__PMC_EV(K8, NB_HT_BUS2_BANDWIDTH, k8-nb-ht-bus2-bandwidth)
#define PMC_EV_K8_FIRST PMC_EV_K8_FP_DISPATCHED_FPU_OPS
#define PMC_EV_K8_LAST PMC_EV_K8_NB_HT_BUS2_BANDWIDTH
/* Intel Pentium Events */
#define __PMC_EV_P5() \
__PMC_EV(P5, DATA_READ, p5-data-read) \
__PMC_EV(P5, DATA_WRITE, p5-data-write) \
__PMC_EV(P5, DATA_TLB_MISS, p5-data-tlb-miss) \
__PMC_EV(P5, DATA_READ_MISS, p5-data-read-miss) \
__PMC_EV(P5, DATA_WRITE_MISS, p5-data-write-miss) \
__PMC_EV(P5, WRITE_HIT_TO_M_OR_E_STATE_LINES, \
p5-write-hit-to-m-or-e-state-lines) \
__PMC_EV(P5, DATA_CACHE_LINES_WRITTEN_BACK, \
p4-data-cache-lines-written-back) \
__PMC_EV(P5, EXTERNAL_SNOOPS, p5-external-snoops) \
__PMC_EV(P5, EXTERNAL_DATA_CACHE_SNOOP_HITS, \
p5-external-data-cache-snoop-hits) \
__PMC_EV(P5, MEMORY_ACCESSES_IN_BOTH_PIPES, \
p5-memory-accesses-in-both-pipes) \
__PMC_EV(P5, BANK_CONFLICTS, p5-bank-conflicts) \
__PMC_EV(P5, MISALIGNED_DATA_OR_IO_REFERENCES, \
p5-misaligned-data-or-io-references) \
__PMC_EV(P5, CODE_READ, p5-code-read) \
__PMC_EV(P5, CODE_TLB_MISS, p5-code-tlb-miss) \
__PMC_EV(P5, CODE_CACHE_MISS, p5-code-cache-miss) \
__PMC_EV(P5, ANY_SEGMENT_REGISTER_LOADED, \
p5-any-segment-register-loaded) \
__PMC_EV(P5, BRANCHES, p5-branches) \
__PMC_EV(P5, BTB_HITS, p5-btb-hits) \
__PMC_EV(P5, TAKEN_BRANCH_OR_BTB_HIT, \
p5-taken-branch-or-btb-hit) \
__PMC_EV(P5, PIPELINE_FLUSHES, p5-pipeline-flushes) \
__PMC_EV(P5, INSTRUCTIONS_EXECUTED, p5-instructions-executed) \
__PMC_EV(P5, INSTRUCTIONS_EXECUTED_V_PIPE, \
p5-instructions-executed-v-pipe) \
__PMC_EV(P5, BUS_CYCLE_DURATION, p5-bus-cycle-duration) \
__PMC_EV(P5, WRITE_BUFFER_FULL_STALL_DURATION, \
p5-write-buffer-full-stall-duration) \
__PMC_EV(P5, WAITING_FOR_DATA_MEMORY_READ_STALL_DURATION, \
p5-waiting-for-data-memory-read-stall-duration) \
__PMC_EV(P5, STALL_ON_WRITE_TO_AN_E_OR_M_STATE_LINE, \
p5-stall-on-write-to-an-e-or-m-state-line) \
__PMC_EV(P5, LOCKED_BUS_CYCLE, p5-locked-bus-cycle) \
__PMC_EV(P5, IO_READ_OR_WRITE_CYCLE, p5-io-read-or-write-cycle) \
__PMC_EV(P5, NONCACHEABLE_MEMORY_READS, \
p5-noncacheable-memory-reads) \
__PMC_EV(P5, PIPELINE_AGI_STALLS, p5-pipeline-agi-stalls) \
__PMC_EV(P5, FLOPS, p5-flops) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR0_REGISTER, \
p5-breakpoint-match-on-dr0-register) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR1_REGISTER, \
p5-breakpoint-match-on-dr1-register) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR2_REGISTER, \
p5-breakpoint-match-on-dr2-register) \
__PMC_EV(P5, BREAKPOINT_MATCH_ON_DR3_REGISTER, \
p5-breakpoint-match-on-dr3-register) \
__PMC_EV(P5, HARDWARE_INTERRUPTS, p5-hardware-interrupts) \
__PMC_EV(P5, DATA_READ_OR_WRITE, p5-data-read-or-write) \
__PMC_EV(P5, DATA_READ_MISS_OR_WRITE_MISS, \
p5-data-read-miss-or-write-miss) \
__PMC_EV(P5, BUS_OWNERSHIP_LATENCY, p5-bus-ownership-latency) \
__PMC_EV(P5, BUS_OWNERSHIP_TRANSFERS, p5-bus-ownership-transfers) \
__PMC_EV(P5, MMX_INSTRUCTIONS_EXECUTED_U_PIPE, \
p5-mmx-instructions-executed-u-pipe) \
__PMC_EV(P5, MMX_INSTRUCTIONS_EXECUTED_V_PIPE, \
p5-mmx-instructions-executed-v-pipe) \
__PMC_EV(P5, CACHE_M_LINE_SHARING, p5-cache-m-line-sharing) \
__PMC_EV(P5, CACHE_LINE_SHARING, p5-cache-line-sharing) \
__PMC_EV(P5, EMMS_INSTRUCTIONS_EXECUTED, \
p5-emms-instructions-executed) \
__PMC_EV(P5, TRANSITIONS_BETWEEN_MMX_AND_FP_INSTRUCTIONS, \
p5-transitions-between-mmx-and-fp-instructions) \
__PMC_EV(P5, BUS_UTILIZATION_DUE_TO_PROCESSOR_ACTIVITY, \
p5-bus-utilization-due-to-processor-activity) \
__PMC_EV(P5, WRITES_TO_NONCACHEABLE_MEMORY, \
p5-writes-to-noncacheable-memory) \
__PMC_EV(P5, SATURATING_MMX_INSTRUCTIONS_EXECUTED, \
p5-saturating-mmx-instructions-executed) \
__PMC_EV(P5, SATURATIONS_PERFORMED, p5-saturations-performed) \
__PMC_EV(P5, NUMBER_OF_CYCLES_NOT_IN_HALT_STATE, \
p5-number-of-cycles-not-in-halt-state) \
__PMC_EV(P5, DATA_CACHE_TLB_MISS_STALL_DURATION, \
p5-data-cache-tlb-miss-stall-duration) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_READS, \
p5-mmx-instruction-data-reads) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_READ_MISSES, \
p5-mmx-instruction-data-read-misses) \
__PMC_EV(P5, FLOATING_POINT_STALLS_DURATION, \
p5-floating-point-stalls-duration) \
__PMC_EV(P5, TAKEN_BRANCHES, p5-taken-branches) \
__PMC_EV(P5, D1_STARVATION_AND_FIFO_IS_EMPTY, \
p5-d1-starvation-and-fifo-is-empty) \
__PMC_EV(P5, D1_STARVATION_AND_ONLY_ONE_INSTRUCTION_IN_FIFO, \
p5-d1-starvation-and-only-instruction-in-fifo) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_WRITES, \
p5-mmx-instruction-data-writes) \
__PMC_EV(P5, MMX_INSTRUCTION_DATA_WRITE_MISSES, \
p5-mmx-instruction-data-write-misses) \
__PMC_EV(P5, PIPELINE_FLUSHES_DUE_TO_WRONG_BRANCH_PREDICTIONS, \
p5-pipeline-flushes-due-to-wrong-branch-predictions) \
__PMC_EV(P5, \
PIPELINE_FLUSHES_DUE_TO_WRONG_BRANCH_PREDICTIONS_RESOLVED_IN_WB_STAGE, \
p5-pipeline-flushes-due-to-wrong-branch-predictions-resolved-in-wb-stage) \
__PMC_EV(P5, MISALIGNED_DATA_MEMORY_REFERENCE_ON_MMX_INSTRUCTIONS, \
p5-misaligned-data-memory-reference-on-mmx-instructions) \
__PMC_EV(P5, PIPELINE_STALL_FOR_MMX_INSTRUCTION_DATA_MEMORY_READS, \
p5-pipeline-stall-for-mmx-instruction-data-memory-reads) \
__PMC_EV(P5, MISPREDICTED_OR_UNPREDICTED_RETURNS, \
p5-mispredicted-or-unpredicted-returns) \
__PMC_EV(P5, PREDICTED_RETURNS, p5-predicted-returns) \
__PMC_EV(P5, MMX_MULTIPLY_UNIT_INTERLOCK, \
p5-mmx-multiply-unit-interlock) \
__PMC_EV(P5, MOVD_MOVQ_STORE_STALL_DUE_TO_PREVIOUS_MMX_OPERATION, \
p5-movd-movq-store-stall-due-to-previous-mmx-operation) \
__PMC_EV(P5, RETURNS, p5-returns) \
__PMC_EV(P5, BTB_FALSE_ENTRIES, p5-btb-false-entries) \
__PMC_EV(P5, BTB_MISS_PREDICTION_ON_NOT_TAKEN_BRANCH, \
p5-btb-miss-prediction-on-not-taken-branch) \
__PMC_EV(P5, \
FULL_WRITE_BUFFER_STALL_DURATION_WHILE_EXECUTING_MMX_INSTRUCTIONS, \
p5-full-write-buffer-stall-duration-while-executing-mmx-instructions) \
__PMC_EV(P5, STALL_ON_MMX_INSTRUCTION_WRITE_TO_E_OR_M_STATE_LINE, \
p5-stall-on-mmx-instruction-write-to-e-o-m-state-line)
#define PMC_EV_P5_FIRST PMC_EV_P5_DATA_READ
#define PMC_EV_P5_LAST \
PMC_EV_P5_STALL_ON_MMX_INSTRUCTION_WRITE_TO_E_OR_M_STATE_LINE
/* timestamp counters. */
#define __PMC_EV_TSC() \
__PMC_EV(TSC, TSC, tsc)
/* All known PMC events */
#define __PMC_EVENTS() \
__PMC_EV_TSC() \
__PMC_EV_K7() \
__PMC_EV_P6() \
__PMC_EV_P4() \
__PMC_EV_K8() \
__PMC_EV_P5() \
enum pmc_event {
#undef __PMC_EV
#define __PMC_EV(C,N,D) PMC_EV_ ## C ## _ ## N ,
__PMC_EVENTS()
};
#define PMC_EVENT_FIRST PMC_EV_TSC_TSC
#define PMC_EVENT_LAST PMC_EV_P5_LAST
/*
* Counter capabilities
*
@ -732,6 +240,21 @@ enum pmc_caps
#define PMC_CAP_FIRST PMC_CAP_INTERRUPT
#define PMC_CAP_LAST PMC_CAP_CASCADE
/*
* PMC Event Numbers
*
* These are generated from the definitions in "dev/hwpmc/pmc_events.h".
*/
enum pmc_event {
#undef __PMC_EV
#define __PMC_EV(C,N,D) PMC_EV_ ## C ## _ ## N ,
__PMC_EVENTS()
};
#define PMC_EVENT_FIRST PMC_EV_TSC_TSC
#define PMC_EVENT_LAST PMC_EV_P5_LAST
/*
* PMC SYSCALL INTERFACE
*/
@ -743,6 +266,7 @@ enum pmc_caps
#define __PMC_OPS() \
__PMC_OP(CONFIGURELOG, "Set log file") \
__PMC_OP(FLUSHLOG, "Flush log file") \
__PMC_OP(GETCPUINFO, "Get system CPU information") \
__PMC_OP(GETDRIVERSTATS, "Get driver statistics") \
__PMC_OP(GETMODULEVERSION, "Get module version") \
@ -751,13 +275,13 @@ enum pmc_caps
__PMC_OP(PMCALLOCATE, "Allocate and configure a PMC") \
__PMC_OP(PMCATTACH, "Attach a PMC to a process") \
__PMC_OP(PMCDETACH, "Detach a PMC from a process") \
__PMC_OP(PMCGETMSR, "Get a PMC's hardware address") \
__PMC_OP(PMCRELEASE, "Release a PMC") \
__PMC_OP(PMCRW, "Read/Set a PMC") \
__PMC_OP(PMCSETCOUNT, "Set initial count/sampling rate") \
__PMC_OP(PMCSTART, "Start a PMC") \
__PMC_OP(PMCSTOP, "Start a PMC") \
__PMC_OP(WRITELOG, "Write a log file entry") \
__PMC_OP(PMCX86GETMSR, "(x86 architectures) retrieve MSR")
__PMC_OP(WRITELOG, "Write a cookie to the log file")
enum pmc_ops {
#undef __PMC_OP
@ -772,11 +296,17 @@ enum pmc_ops {
#define PMC_F_FORCE 0x00000001 /*OP ADMIN force operation */
#define PMC_F_DESCENDANTS 0x00000002 /*OP ALLOCATE track descendants */
#define PMC_F_LOG_TC_CSW 0x00000004 /*OP ALLOCATE track ctx switches */
#define PMC_F_LOG_TC_PROCEXIT 0x00000008 /*OP ALLOCATE log proc exits */
#define PMC_F_LOG_PROCCSW 0x00000004 /*OP ALLOCATE track ctx switches */
#define PMC_F_LOG_PROCEXIT 0x00000008 /*OP ALLOCATE log proc exits */
#define PMC_F_NEWVALUE 0x00000010 /*OP RW write new value */
#define PMC_F_OLDVALUE 0x00000020 /*OP RW get old value */
#define PMC_F_KGMON 0x00000040 /*OP ALLOCATE kgmon(8) profiling */
/* internal flags */
#define PMC_F_ATTACHED_TO_OWNER 0x00010000 /*attached to owner*/
#define PMC_F_NEEDS_LOGFILE 0x00020000 /*needs log file */
#define PMC_F_ATTACH_DONE 0x00040000 /*attached at least once */
#define PMC_F_IS_STALLED 0x00080000 /*sampling is stalled*/
/*
* Cookies used to denote allocated PMCs, and the values of PMCs.
@ -834,22 +364,7 @@ struct pmc_op_pmcallocate {
enum pmc_mode pm_mode; /* desired mode */
pmc_id_t pm_pmcid; /* [return] process pmc id */
/*
* Machine dependent extensions
*/
#if __i386__
uint32_t pm_config1;
uint32_t pm_config2;
#define pm_amd_config pm_config1
#define pm_p4_cccrconfig pm_config1
#define pm_p4_escrconfig pm_config2
#define pm_p6_config pm_config1
#elif __amd64__
uint32_t pm_k8_config;
#define pm_amd_config pm_k8_config
#endif
union pmc_md_op_pmcallocate pm_md; /* MD layer extensions */
};
/*
@ -973,8 +488,12 @@ struct pmc_op_configurelog {
struct pmc_op_getdriverstats {
int pm_intr_ignored; /* #interrupts ignored */
int pm_intr_processed; /* #interrupts processed */
int pm_intr_bufferfull; /* #interrupts with ENOSPC */
int pm_syscalls; /* #syscalls */
int pm_syscall_errors; /* #syscalls with errors */
int pm_buffer_requests; /* #buffer requests */
int pm_buffer_requests_failed; /* #failed buffer requests */
int pm_log_sweeps; /* #sample buffer processing passes */
};
/*
@ -987,32 +506,50 @@ struct pmc_op_simple {
pmc_id_t pm_pmcid;
};
#if __i386__ || __amd64__
/*
* OP X86_GETMSR
* OP WRITELOG
*
* Retrieve the model specific register assoicated with the
* allocated PMC. This number can be used subsequently with
* RDPMC instructions.
* Flush the current log buffer and write 4 bytes of user data to it.
*/
struct pmc_op_x86_getmsr {
uint32_t pm_msr; /* MSR for the PMC */
struct pmc_op_writelog {
uint32_t pm_userdata;
};
/*
* OP GETMSR
*
* Retrieve the machine specific address assoicated with the allocated
* PMC. This number can be used subsequently with a read-performance-counter
* instruction.
*/
struct pmc_op_getmsr {
uint32_t pm_msr; /* machine specific address */
pmc_id_t pm_pmcid; /* allocated pmc id */
};
#endif
#ifdef _KERNEL
#include <sys/malloc.h>
#include <sys/sysctl.h>
#define PMC_REQUEST_POOL_SIZE 128
#define PMC_REQUEST_POOL_SIZE 32
#define PMC_HASH_SIZE 16
#define PMC_PCPU_BUFFER_SIZE 4096
#define PMC_MTXPOOL_SIZE 32
#define PMC_LOG_BUFFER_SIZE 4
#define PMC_NLOGBUFFERS 16
#define PMC_NSAMPLES 16
#define PMC_SYSCTL_NAME_PREFIX "kern." PMC_MODULE_NAME "."
/*
* Locking keys
*
* (b) - pmc_bufferlist_mtx (spin lock)
* (k) - pmc_kthread_mtx (sleep lock)
* (o) - po->po_mtx (spin lock)
*/
/*
* PMC commands
@ -1035,7 +572,7 @@ struct pmc_syscall_args {
*/
struct pmc_descr {
const char pd_name[PMC_NAME_MAX]; /* name */
char pd_name[PMC_NAME_MAX]; /* name */
uint32_t pd_caps; /* capabilities */
enum pmc_class pd_class; /* class of the PMC */
uint32_t pd_width; /* width in bits */
@ -1077,7 +614,8 @@ struct pmc_target {
*/
struct pmc {
LIST_HEAD(,pmc_target) pm_targets; /* list of target processes */
LIST_HEAD(,pmc_target) pm_targets; /* list of target processes */
LIST_ENTRY(pmc) pm_next; /* owner's list */
/*
* System-wide PMCs are allocated on a CPU and are not moved
@ -1123,36 +661,7 @@ struct pmc {
pmc_id_t pm_id; /* allocated PMC id */
/* md extensions */
#if __i386__
union {
/* AMD Athlon counters */
struct {
uint32_t pm_amd_evsel;
} pm_amd;
/* Intel P4 counters */
struct {
uint32_t pm_p4_cccrvalue;
uint32_t pm_p4_escrvalue;
uint32_t pm_p4_escr;
uint32_t pm_p4_escrmsr;
} pm_p4;
/* Intel P6 counters */
struct {
uint32_t pm_p6_evsel;
} pm_p6;
} pm_md;
#elif __amd64__
union {
/* AMD Athlon counters */
struct {
uint32_t pm_amd_evsel;
} pm_amd;
} pm_md;
#endif
union pmc_md_pmc pm_md;
};
/*
@ -1164,17 +673,6 @@ struct pmc {
#define PMC_TO_ROWINDEX(P) PMC_ID_TO_ROWINDEX((P)->pm_id)
#define PMC_TO_CPU(P) PMC_ID_TO_CPU((P)->pm_id)
/*
* struct pmc_list
*
* Describes a list of PMCs.
*/
struct pmc_list {
LIST_ENTRY(pmc_list) pl_next;
struct pmc *pl_pmc; /* PMC descriptor */
};
/*
* struct pmc_process
*
@ -1222,15 +720,22 @@ struct pmc_process {
*/
struct pmc_owner {
LIST_ENTRY(pmc_owner) po_next; /* hash chain */
LIST_HEAD(, pmc_list) po_pmcs; /* list of owned PMCs */
uint32_t po_flags; /* flags PMC_PO_* */
struct proc *po_owner; /* owner proc */
int po_logfd; /* XXX for now */
LIST_ENTRY(pmc_owner) po_next; /* hash chain */
LIST_ENTRY(pmc_owner) po_ssnext; /* list of SS PMC owners */
LIST_HEAD(, pmc) po_pmcs; /* owned PMC list */
TAILQ_HEAD(, pmclog_buffer) po_logbuffers; /* (o) logbuffer list */
struct mtx po_mtx; /* spin lock for (o) */
struct proc *po_owner; /* owner proc */
uint32_t po_flags; /* (k) flags PMC_PO_* */
struct proc *po_kthread; /* (k) helper kthread */
struct pmclog_buffer *po_curbuf; /* current log buffer */
struct file *po_file; /* file reference */
int po_error; /* recorded error */
int po_sscount; /* # SS PMCs owned */
};
#define PMC_PO_HAS_TS_PMC 0x00000001
#define PMC_PO_OWNS_LOGFILE 0x00000002
#define PMC_PO_OWNS_LOGFILE 0x00000001 /* has a log file */
#define PMC_PO_IN_FLUSH 0x00000010 /* in the middle of a flush */
/*
* struct pmc_hw -- describe the state of the PMC hardware
@ -1274,6 +779,27 @@ struct pmc_hw {
#define PMC_PHW_FLAG_IS_ENABLED (PMC_PHW_FLAGS_TO_STATE(0x01))
#define PMC_PHW_FLAG_IS_SHAREABLE (PMC_PHW_FLAGS_TO_STATE(0x02))
/*
* struct pmc_sample
*
* Space for N (tunable) PC samples and associated control data.
*/
struct pmc_sample {
uintfptr_t ps_pc; /* PC value at interrupt */
struct pmc *ps_pmc; /* interrupting PMC */
int ps_usermode; /* true for user mode PCs */
pid_t ps_pid; /* process PID or -1 */
};
struct pmc_samplebuffer {
struct pmc_sample * volatile ps_read; /* read pointer */
struct pmc_sample * volatile ps_write; /* write pointer */
struct pmc_sample *ps_fence; /* one beyond ps_samples[] */
struct pmc_sample ps_samples[]; /* array of sample entries */
};
/*
* struct pmc_cpustate
*
@ -1283,6 +809,7 @@ struct pmc_hw {
struct pmc_cpu {
uint32_t pc_state; /* physical cpu number + flags */
struct pmc_samplebuffer *pc_sb; /* space for samples */
struct pmc_hw *pc_hwpmcs[]; /* 'npmc' pointers */
/* other machine dependent fields come here */
};
@ -1352,10 +879,7 @@ struct pmc_mdep {
int (*pmd_describe)(int _cpu, int _ri, struct pmc_info *_pi,
struct pmc **_ppmc);
/* Machine dependent methods */
#if __i386__ || __amd64__
int (*pmd_get_msr)(int _ri, uint32_t *_msr);
#endif
};
@ -1365,36 +889,46 @@ struct pmc_mdep {
*/
extern struct pmc_cpu **pmc_pcpu;
extern struct pmc_mdep *md;
/* driver statistics */
extern struct pmc_op_getdriverstats pmc_stats;
#if DEBUG
/* debug flags */
extern unsigned int pmc_debugflags; /* [Maj:12bits] [Min:16bits] [level:4] */
/* debug flags, major flag groups */
struct pmc_debugflags {
int pdb_CPU;
int pdb_CSW;
int pdb_LOG;
int pdb_MDP;
int pdb_MOD;
int pdb_OWN;
int pdb_PMC;
int pdb_PRC;
int pdb_SAM;
};
extern struct pmc_debugflags pmc_debugflags;
#define PMC_DEBUG_DEFAULT_FLAGS 0
#define PMC_DEBUG_STRSIZE 128
#define PMC_DEBUG_DEFAULT_FLAGS { 0, 0, 0, 0, 0, 0, 0, 0 }
#define __PMCDFMAJ(M) (1 << (PMC_DEBUG_MAJ_##M+20))
#define __PMCDFMIN(M) (1 << (PMC_DEBUG_MIN_##M+4))
#define __PMCDF(M,N) (__PMCDFMAJ(M) | __PMCDFMIN(N))
#define PMCDBG(M,N,L,F,...) do { \
if (((pmc_debugflags & __PMCDF(M,N)) == __PMCDF(M,N)) && \
((pmc_debugflags & 0xF) > (L))) \
printf(#M ":" #N ": " F "\n", __VA_ARGS__); \
if (pmc_debugflags.pdb_ ## M & PMC_DEBUG_MIN_ ## N) \
printf(#M ":" #N ":" #L ": " F "\n", __VA_ARGS__); \
} while (0)
/* Major numbers */
#define PMC_DEBUG_MAJ_MOD 0 /* misc module infrastructure */
#define PMC_DEBUG_MAJ_PMC 1 /* pmc management */
#define PMC_DEBUG_MAJ_CTX 2 /* context switches */
#define PMC_DEBUG_MAJ_OWN 3 /* owner */
#define PMC_DEBUG_MAJ_PRC 4 /* processes */
#define PMC_DEBUG_MAJ_MDP 5 /* machine dependent */
#define PMC_DEBUG_MAJ_CPU 6 /* cpu switches */
#define PMC_DEBUG_MAJ_CPU 0 /* cpu switches */
#define PMC_DEBUG_MAJ_CSW 1 /* context switches */
#define PMC_DEBUG_MAJ_LOG 2 /* logging */
#define PMC_DEBUG_MAJ_MDP 3 /* machine dependent */
#define PMC_DEBUG_MAJ_MOD 4 /* misc module infrastructure */
#define PMC_DEBUG_MAJ_OWN 5 /* owner */
#define PMC_DEBUG_MAJ_PMC 6 /* pmc management */
#define PMC_DEBUG_MAJ_PRC 7 /* processes */
#define PMC_DEBUG_MAJ_SAM 8 /* sampling */
/* Minor numbers */
@ -1420,6 +954,7 @@ extern unsigned int pmc_debugflags; /* [Maj:12bits] [Min:16bits] [level:4] */
#define PMC_DEBUG_MIN_EXC 11 /* process exec */
#define PMC_DEBUG_MIN_FRK 12 /* process fork */
#define PMC_DEBUG_MIN_ATT 13 /* attach/detach */
#define PMC_DEBUG_MIN_SIG 14 /* signalling */
/* CONTEXT SWITCHES */
#define PMC_DEBUG_MIN_SWI 8 /* switch in */
@ -1441,6 +976,12 @@ extern unsigned int pmc_debugflags; /* [Maj:12bits] [Min:16bits] [level:4] */
#define PMC_DEBUG_MIN_BND 8 /* bind */
#define PMC_DEBUG_MIN_SEL 9 /* select */
/* LOG */
#define PMC_DEBUG_MIN_GTB 8 /* get buf */
#define PMC_DEBUG_MIN_SIO 9 /* schedule i/o */
#define PMC_DEBUG_MIN_FLS 10 /* flush */
#define PMC_DEBUG_MIN_SAM 11 /* sample */
#else
#define PMCDBG(M,N,L,F,...) /* nothing */
#endif
@ -1452,9 +993,10 @@ MALLOC_DECLARE(M_PMC);
* Functions
*/
void pmc_update_histogram(struct pmc_hw *phw, uintptr_t pc);
void pmc_send_signal(struct pmc *pmc);
int pmc_getrowdisp(int ri);
struct pmc_mdep *pmc_md_initialize(void); /* MD init function */
int pmc_getrowdisp(int _ri);
int pmc_process_interrupt(int _cpu, struct pmc *_pm, intfptr_t _pc,
int _usermode);
#endif /* _KERNEL */
#endif /* _SYS_PMC_H_ */

View File

@ -44,9 +44,6 @@
#define PMC_FN_CSW_OUT 3
#define PMC_FN_DO_SAMPLES 4
#define PMC_FN_PROCESS_EXIT 5 /* obsolete */
#define PMC_FN_PROCESS_FORK 6 /* obsolete */
/* hook */
extern int (*pmc_hook)(struct thread *_td, int _function, void *_arg);
extern int (*pmc_intr)(int _cpu, uintptr_t _pc, int _usermode);
@ -55,7 +52,10 @@ extern int (*pmc_intr)(int _cpu, uintptr_t _pc, int _usermode);
extern struct sx pmc_sx;
/* Per-cpu flags indicating availability of sampling data */
extern cpumask_t pmc_cpumask;
extern volatile cpumask_t pmc_cpumask;
/* Count of system-wide sampling PMCs in existence */
extern volatile int pmc_ss_count;
/* Hook invocation; for use within the kernel */
#define PMC_CALL_HOOK(t, cmd, arg) \
@ -92,6 +92,8 @@ do { \
(__predict_false(atomic_load_acq_int(&(p)->p_flag) & \
P_HWPMC))
#define PMC_SYSTEM_SAMPLING_ACTIVE() (pmc_ss_count > 0)
/* Check if a CPU has recorded samples. */
#define PMC_CPU_HAS_SAMPLES(C) (__predict_false(pmc_cpumask & (1 << (C))))

229
sys/sys/pmclog.h Normal file
View File

@ -0,0 +1,229 @@
/*-
* Copyright (c) 2005 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _SYS_PMCLOG_H_
#define _SYS_PMCLOG_H_
#include <sys/pmc.h>
enum pmclog_type {
PMCLOG_TYPE_CLOSELOG,
PMCLOG_TYPE_DROPNOTIFY,
PMCLOG_TYPE_INITIALIZE,
PMCLOG_TYPE_MAPPINGCHANGE,
PMCLOG_TYPE_PCSAMPLE,
PMCLOG_TYPE_PMCALLOCATE,
PMCLOG_TYPE_PMCATTACH,
PMCLOG_TYPE_PMCDETACH,
PMCLOG_TYPE_PROCCSW,
PMCLOG_TYPE_PROCEXEC,
PMCLOG_TYPE_PROCEXIT,
PMCLOG_TYPE_PROCFORK,
PMCLOG_TYPE_SYSEXIT,
PMCLOG_TYPE_USERDATA
};
#define PMCLOG_MAPPING_INSERT 0x01
#define PMCLOG_MAPPING_DELETE 0x02
/*
* A log entry descriptor comprises of a 32 bit header and a 64 bit
* time stamp followed by as many 32 bit words are required to record
* the event.
*
* Header field format:
*
* 31 24 16 0
* +------------+------------+-----------------------------------+
* | MAGIC | TYPE | LENGTH |
* +------------+------------+-----------------------------------+
*
* MAGIC is the constant PMCLOG_HEADER_MAGIC.
* TYPE contains a value of type enum pmclog_type.
* LENGTH contains the length of the event record, in bytes.
*/
#define PMCLOG_ENTRY_HEADER \
uint32_t pl_header; \
uint32_t pl_ts_sec; \
uint32_t pl_ts_nsec;
/*
* The following structures are used to describe the size of each kind
* of log entry to sizeof(). To keep the compiler from adding
* padding, the fields of each structure are aligned to their natural
* boundaries, and the structures are marked as 'packed'.
*
* The actual reading and writing of the log file is always in terms
* of 4 byte quantities.
*/
struct pmclog_closelog {
PMCLOG_ENTRY_HEADER
};
struct pmclog_dropnotify {
PMCLOG_ENTRY_HEADER
};
struct pmclog_initialize {
PMCLOG_ENTRY_HEADER
uint32_t pl_version; /* driver version */
uint32_t pl_cpu; /* enum pmc_cputype */
} __packed;
struct pmclog_mappingchange {
PMCLOG_ENTRY_HEADER
uint32_t pl_type;
uintfptr_t pl_start; /* 8 byte aligned */
uintfptr_t pl_end;
uint32_t pl_pid;
char pl_pathname[PATH_MAX];
} __packed;
struct pmclog_pcsample {
PMCLOG_ENTRY_HEADER
uint32_t pl_pid;
uintfptr_t pl_pc; /* 8 byte aligned */
uint32_t pl_pmcid;
} __packed;
struct pmclog_pmcallocate {
PMCLOG_ENTRY_HEADER
uint32_t pl_pmcid;
uint32_t pl_event;
uint32_t pl_flags;
} __packed;
struct pmclog_pmcattach {
PMCLOG_ENTRY_HEADER
uint32_t pl_pmcid;
uint32_t pl_pid;
char pl_pathname[PATH_MAX];
} __packed;
struct pmclog_pmcdetach {
PMCLOG_ENTRY_HEADER
uint32_t pl_pmcid;
uint32_t pl_pid;
} __packed;
struct pmclog_proccsw {
PMCLOG_ENTRY_HEADER
uint32_t pl_pmcid;
uint64_t pl_value; /* keep 8 byte aligned */
uint32_t pl_pid;
} __packed;
struct pmclog_procexec {
PMCLOG_ENTRY_HEADER
uint32_t pl_pid;
char pl_pathname[PATH_MAX];
} __packed;
struct pmclog_procexit {
PMCLOG_ENTRY_HEADER
uint32_t pl_pmcid;
uint64_t pl_value; /* keep 8 byte aligned */
uint32_t pl_pid;
} __packed;
struct pmclog_procfork {
PMCLOG_ENTRY_HEADER
uint32_t pl_oldpid;
uint32_t pl_newpid;
} __packed;
struct pmclog_sysexit {
PMCLOG_ENTRY_HEADER
uint32_t pl_pid;
} __packed;
struct pmclog_userdata {
PMCLOG_ENTRY_HEADER
uint32_t pl_userdata;
} __packed;
union pmclog_entry { /* only used to size scratch areas */
struct pmclog_closelog pl_cl;
struct pmclog_dropnotify pl_dn;
struct pmclog_initialize pl_i;
struct pmclog_pcsample pl_s;
struct pmclog_pmcallocate pl_a;
struct pmclog_pmcattach pl_t;
struct pmclog_pmcdetach pl_d;
struct pmclog_proccsw pl_c;
struct pmclog_procexec pl_x;
struct pmclog_procexit pl_e;
struct pmclog_procfork pl_f;
struct pmclog_sysexit pl_se;
struct pmclog_userdata pl_u;
};
#define PMCLOG_HEADER_MAGIC 0xEEU
#define PMCLOG_HEADER_TO_LENGTH(H) \
((H) & 0x0000FFFF)
#define PMCLOG_HEADER_TO_TYPE(H) \
(((H) & 0x00FF0000) >> 16)
#define PMCLOG_HEADER_TO_MAGIC(H) \
(((H) & 0xFF000000) >> 24)
#define PMCLOG_HEADER_CHECK_MAGIC(H) \
(PMCLOG_HEADER_TO_MAGIC(H) == PMCLOG_HEADER_MAGIC)
#ifdef _KERNEL
/*
* Prototypes
*/
int pmclog_configure_log(struct pmc_owner *_po, int _logfd);
int pmclog_deconfigure_log(struct pmc_owner *_po);
int pmclog_flush(struct pmc_owner *_po);
void pmclog_initialize(void);
void pmclog_process_closelog(struct pmc_owner *po);
void pmclog_process_dropnotify(struct pmc_owner *po);
void pmclog_process_mappingchange(struct pmc_owner *po, pid_t pid, int type,
uintfptr_t start, uintfptr_t end, char *path);
void pmclog_process_pcsample(struct pmc *_pm, struct pmc_sample *_ps);
void pmclog_process_pmcallocate(struct pmc *_pm);
void pmclog_process_pmcattach(struct pmc *_pm, pid_t _pid, char *_path);
void pmclog_process_pmcdetach(struct pmc *_pm, pid_t _pid);
void pmclog_process_proccsw(struct pmc *_pm, struct pmc_process *_pp,
pmc_value_t _v);
void pmclog_process_procexec(struct pmc_owner *_po, pid_t _pid, char *_path);
void pmclog_process_procexit(struct pmc *_pm, struct pmc_process *_pp);
void pmclog_process_procfork(struct pmc_owner *_po, pid_t _oldpid, pid_t _newpid);
void pmclog_process_sysexit(struct pmc_owner *_po, pid_t _pid);
int pmclog_process_userlog(struct pmc_owner *_po,
struct pmc_op_writelog *_wl);
void pmclog_shutdown(void);
#endif /* _KERNEL */
#endif /* _SYS_PMCLOG_H_ */

View File

@ -222,8 +222,8 @@ pmcc_do_list_state(void)
int c, cpu, n, npmc, ncpu;
unsigned int logical_cpus_mask;
struct pmc_info *pd;
struct pmc_op_getpmcinfo *pi;
const struct pmc_op_getcpuinfo *pc;
struct pmc_pmcinfo *pi;
const struct pmc_cpuinfo *pc;
if (pmc_cpuinfo(&pc) != 0)
err(EX_OSERR, "Unable to determine CPU information");
@ -280,7 +280,7 @@ pmcc_do_list_events(void)
enum pmc_class c;
unsigned int i, j, nevents;
const char **eventnamelist;
const struct pmc_op_getcpuinfo *ci;
const struct pmc_cpuinfo *ci;
if (pmc_cpuinfo(&ci) != 0)
err(EX_OSERR, "Unable to determine CPU information");
@ -307,7 +307,7 @@ static int
pmcc_show_statistics(void)
{
struct pmc_op_getdriverstats gms;
struct pmc_driverstats gms;
if (pmc_get_driver_stats(&gms) < 0)
err(EX_OSERR, "ERROR: cannot retrieve driver statistics");
@ -316,12 +316,15 @@ pmcc_show_statistics(void)
* Print statistics.
*/
#define PRINT(N,V) (void) printf("%20s %d\n", (N), gms.pm_##V)
PRINT("interrupts-processed", intr_processed);
PRINT("interrupts-ignored", intr_ignored);
PRINT("system-calls", syscalls);
PRINT("system-calls-with-errors", syscall_errors);
#define PRINT(N,V) (void) printf("%-40s %d\n", (N), gms.pm_##V)
PRINT("interrupts processed:", intr_processed);
PRINT("non-PMC interrupts:", intr_ignored);
PRINT("interrupts dropped due to lack of space:", intr_bufferfull);
PRINT("system calls:", syscalls);
PRINT("system calls with errors:", syscall_errors);
PRINT("buffer requests:", buffer_requests);
PRINT("buffer requests failed:", buffer_requests_failed);
PRINT("sampling log sweeps:", log_sweeps);
return 0;
}

View File

@ -31,12 +31,18 @@
.Nd performance measurement with performance monitoring hardware
.Sh SYNOPSIS
.Nm
.Op Fl D Ar pathname
.Op Fl C
.Op Fl E
.Op Fl O Ar logfilename
.Op Fl P Ar event-spec
.Op Fl R Ar logfilename
.Op Fl S Ar event-spec
.Op Fl W
.Op Fl c Ar cpu
.Op Fl d
.Op Fl g
.Op Fl m
.Op Fl n Ar count
.Op Fl o Ar outputfile
.Op Fl p Ar event-spec
@ -76,9 +82,9 @@ counting and sampling flavors.
The values of all counting PMCs are printed in human readable form
at regular intervals by
.Nm .
The output of sampling PMCs is configured to go to log file, for later
analysis by tools like
.Xr pmcreport 8 .
The output of sampling PMCs may be configured to go to a log file for
subsequent offline analysis, or, at the expense of greater
overhead, may be configured to be processed on the fly.
.Pp
Hardware events to measure are specified to
.Nm
@ -94,23 +100,48 @@ process' current and future children.
The following options are available:
.Bl -tag -width indent
.It Fl C
Toggle between showing cumulative and incremental counts for
Toggle between showing cumulative or incremental counts for
subsequent counting mode PMCs specified on the command line.
The default is to show incremental counts.
.It Fl D Ar pathname
Create files with per-program samples in the directory named
by
.Ar pathname .
The default is to create these files in the current directory.
.It Fl E
Toggle showing per-process counts at the time a tracked process
exits for subsequent process-mode PMCs specified on the command line.
This option is useful for mapping the performance characteristics of a
complex pipeline of processes when used in conjunction with the
.Fl d
option.
The default is to not to enable per-process tracking.
.It Fl O Ar logfilename
Send the output of sampling mode PMCs to
.Ar logfilename .
The default file name is
.Pa pmcstat.out ,
in the current directory.
If this option is not specified and one of the logging options
is requested, then
.Nm
will print a human-readable version of the log to the configured
output file.
.It Fl P Ar event-spec
Allocate a process mode sampling PMC measuring hardware events
specified in
.Ar event-spec .
.It Fl R Ar logfilename
Perform offline analysis using sampling data in file
.Ar logfilename .
.It Fl S Ar event-spec
Allocate a system mode sampling PMC measuring hardware events
specified in
.Ar event-spec .
.It Fl W
Toggle logging the incremental counts seen by the threads of a
tracked process each time they are scheduled on a CPU.
This is an experimental feature intended to help analyse the
dynamic behaviour of processes in the system.
It may incur substantial overhead if enabled.
The default is for this feature to be disabled.
.It Fl c Ar cpu
Set the cpu for subsequent system mode PMCs specified on the
command line to
@ -119,16 +150,23 @@ The default is to allocate system mode PMCs on CPU zero.
.It Fl d
Toggle between process mode PMCs measuring events for the target
process' current and future children or only measuring events for
the attached process.
the target process.
The default is to measure events for the target process alone.
.It Fl g
Produce execution profiles in a format compatible with
.Xr gprof 1 .
.It Fl m
When producing
.Xr gprof 1
compatible execution profiles, merge profiles across multiple
invocations of the same executable.
.It Fl n Ar rate
Set the default sampling rate for subsequent sampling mode
PMCs specified on the command line.
The default is to configure PMCs to sample the CPU's instruction
pointer every 65536 events.
.It Fl o Ar outputfile
Send the periodic counter output of
.Nm
Send counter readings and printed representations of logged data
to file
.Ar outputfile .
The default is to send output to
@ -188,9 +226,9 @@ sometime after
.Sh AUTHORS
.An Joseph Koshy Aq jkoshy@FreeBSD.org
.Sh SEE ALSO
.Xr gprof 1 ,
.Xr execvp 3 ,
.Xr pmc 3 ,
.Xr hwpmc 4 ,
.Xr pmccontrol 8 ,
.Xr pmcreport 8 ,
.Xr sysctl 8

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2003,2004 Joseph Koshy
* Copyright (c) 2003-2005, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -22,7 +22,6 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
@ -42,6 +41,7 @@ __FBSDID("$FreeBSD$");
#include <limits.h>
#include <math.h>
#include <pmc.h>
#include <pmclog.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
@ -51,26 +51,55 @@ __FBSDID("$FreeBSD$");
#include <sysexits.h>
#include <unistd.h>
/*
* A given invocation of pmcstat(8) can manage multiple PMCs of both
* the system-wide and per-process variety. Each of these could be in
* 'counting mode' or in 'sampling mode'.
*
* For 'counting mode' PMCs, pmcstat(8) will periodically issue a
* pmc_read() at the configured time interval and print out the value
* of the requested PMCs.
*
* For 'sampling mode' PMCs it can log to a file for offline analysis,
* or can analyse sampling data "on the fly", either by converting
* samples to printed textual form or by creating gprof(1) compatible
* profiles, one per program executed. When creating gprof(1)
* profiles it can optionally merge entries from multiple processes
* for a given executable into a single profile file.
*/
/* Operation modes */
#define FLAG_HAS_PID 0x00000001
#define FLAG_HAS_WAIT_INTERVAL 0x00000002
#define FLAG_HAS_LOG_FILE 0x00000004
#define FLAG_HAS_PROCESS 0x00000008
#define FLAG_USING_SAMPLING 0x00000010
#define FLAG_USING_COUNTING 0x00000020
#define FLAG_USING_PROCESS_PMC 0x00000040
#define FLAG_HAS_SAMPLING_PMCS 0x00000010
#define FLAG_HAS_COUNTING_PMCS 0x00000020
#define FLAG_HAS_PROCESS_PMCS 0x00000040
#define FLAG_HAS_SYSTEM_PMCS 0x00000080
#define FLAG_HAS_PIPE 0x00000100
#define FLAG_PROCESS_LOGFILE 0x00000200
#define FLAG_DO_GPROF 0x00000400
#define FLAG_DO_GPROF_MERGED 0x00000800
#define DEFAULT_SAMPLE_COUNT 65536
#define DEFAULT_WAIT_INTERVAL 5.0
#define DEFAULT_DISPLAY_HEIGHT 23
#define DEFAULT_LOGFILE_NAME "pmcstat.out"
#define DEFAULT_BUFFER_SIZE 4096
#define WRITELOG_MAGIC 0xA55AA55A
#define PRINT_HEADER_PREFIX "# "
#define READPIPEFD 0
#define WRITEPIPEFD 1
#define NPIPEFD 2
enum pmcstat_state {
PMCSTAT_FINISHED = 0,
PMCSTAT_EXITING = 1,
PMCSTAT_RUNNING = 2
};
struct pmcstat_ev {
STAILQ_ENTRY(pmcstat_ev) ev_next;
char *ev_spec; /* event specification */
@ -78,7 +107,7 @@ struct pmcstat_ev {
enum pmc_mode ev_mode; /* desired mode */
int ev_count; /* associated count if in sampling mode */
int ev_cpu; /* specific cpu if requested */
int ev_descendants; /* attach to descendants */
int ev_flags; /* PMC_F_* */
int ev_cumulative; /* show cumulative counts */
int ev_fieldwidth; /* print width */
int ev_fieldskip; /* #leading spaces */
@ -87,13 +116,16 @@ struct pmcstat_ev {
};
struct pmcstat_args {
int pa_required;
int pa_flags;
pid_t pa_pid;
FILE *pa_outputfile;
FILE *pa_logfile;
double pa_interval;
FILE *pa_outputfile;
FILE *pa_logfile;
void *pa_logparser;
char *pa_outputdir;
double pa_interval;
int pa_argc;
char **pa_argv;
char **pa_argv;
STAILQ_HEAD(, pmcstat_ev) pa_head;
} args;
@ -103,15 +135,22 @@ int pmcstat_pipefd[NPIPEFD];
int pmcstat_kq;
/* Function prototypes */
void pmcstat_cleanup(struct pmcstat_args *_a);
void pmcstat_print_counters(struct pmcstat_args *_a);
void pmcstat_print_headers(struct pmcstat_args *_a);
void pmcstat_print_pmcs(struct pmcstat_args *_a);
void pmcstat_setup_process(struct pmcstat_args *_a);
void pmcstat_show_usage(void);
void pmcstat_start_pmcs(struct pmcstat_args *_a);
void pmcstat_start_process(struct pmcstat_args *_a);
void pmcstat_cleanup(struct pmcstat_args *_a);
int pmcstat_close_log(struct pmcstat_args *_a);
void pmcstat_print_counters(struct pmcstat_args *_a);
void pmcstat_print_headers(struct pmcstat_args *_a);
void pmcstat_print_pmcs(struct pmcstat_args *_a);
void pmcstat_setup_process(struct pmcstat_args *_a);
void pmcstat_show_usage(void);
void pmcstat_start_pmcs(struct pmcstat_args *_a);
void pmcstat_start_process(struct pmcstat_args *_a);
void pmcstat_process_log(struct pmcstat_args *_a);
int pmcstat_print_log(struct pmcstat_args *_a);
#define PMCSTAT_PRINT_LOG(A,T,...) do { \
fprintf((A)->pa_outputfile, T "\t" __VA_ARGS__); \
fprintf((A)->pa_outputfile, "\n"); \
} while (0)
/*
* cleanup
@ -123,22 +162,25 @@ pmcstat_cleanup(struct pmcstat_args *a)
struct pmcstat_ev *ev, *tmp;
/* de-configure the log file if present. */
if (a->pa_flags & FLAG_USING_SAMPLING) {
if (a->pa_flags & FLAG_HAS_LOG_FILE)
(void) pmc_configure_logfile(-1);
(void) fclose(a->pa_logfile);
}
/* release allocated PMCs. */
STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp)
if (ev->ev_pmcid != PMC_ID_INVALID) {
if (pmc_release(ev->ev_pmcid) < 0)
err(EX_OSERR, "ERROR: cannot release pmc "
"%d \"%s\"", ev->ev_pmcid, ev->ev_name);
"0x%x \"%s\"", ev->ev_pmcid, ev->ev_name);
free(ev->ev_name);
free(ev->ev_spec);
STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next);
free(ev);
}
if (a->pa_logparser) {
pmclog_close(a->pa_logparser);
a->pa_logparser = NULL;
}
}
void
@ -151,9 +193,10 @@ pmcstat_start_pmcs(struct pmcstat_args *a)
assert(ev->ev_pmcid != PMC_ID_INVALID);
if (pmc_start(ev->ev_pmcid) < 0) {
warn("ERROR: Cannot start pmc %d \"%s\"",
warn("ERROR: Cannot start pmc 0x%x \"%s\"",
ev->ev_pmcid, ev->ev_name);
pmcstat_cleanup(a);
exit(EX_OSERR);
}
}
@ -255,12 +298,10 @@ pmcstat_setup_process(struct pmcstat_args *a)
struct kevent kev;
if (a->pa_flags & FLAG_HAS_PID) {
STAILQ_FOREACH(ev, &args.pa_head, ev_next)
STAILQ_FOREACH(ev, &a->pa_head, ev_next)
if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0)
err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to "
"process %d", ev->ev_name, (int) a->pa_pid);
} else {
/*
@ -269,7 +310,6 @@ pmcstat_setup_process(struct pmcstat_args *a)
* process reads its pipe for a token so that the parent
* can finish doing its pmc_attach() calls.
*/
if (pipe(pmcstat_pipefd) < 0)
err(EX_OSERR, "ERROR: cannot create pipe");
@ -288,8 +328,11 @@ pmcstat_setup_process(struct pmcstat_args *a)
(void) close(pmcstat_pipefd[READPIPEFD]);
/* exec() the program requested */
execvp(*args.pa_argv, args.pa_argv);
err(EX_OSERR, "ERROR (child): execvp failed");
execvp(*a->pa_argv, a->pa_argv);
/* and if that fails, notify the parent */
kill(getppid(), SIGCHLD);
err(EX_OSERR, "ERROR: execvp \"%s\" failed",
*a->pa_argv);
/*NOTREACHED*/
default: /* parent */
@ -307,13 +350,12 @@ pmcstat_setup_process(struct pmcstat_args *a)
}
}
/* Ask to be notified via a kevent when the child exits */
EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0);
/* Ask to be notified via a kevent when the target process exits */
EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0,
NULL);
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
err(EX_OSERR, "ERROR: cannot monitor process %d",
err(EX_OSERR, "ERROR: cannot monitor child process %d",
a->pa_pid);
return;
}
@ -332,6 +374,147 @@ pmcstat_start_process(struct pmcstat_args *a)
(void) close(pmcstat_pipefd[WRITEPIPEFD]);
}
/*
* Process a log file in offline analysis mode.
*/
void
pmcstat_process_log(struct pmcstat_args *a)
{
int runstate;
/*
* If gprof style profiles haven't been asked for, just print the
* log to the current output file.
*/
if ((a->pa_flags & (FLAG_DO_GPROF_MERGED|FLAG_DO_GPROF)) == 0) {
while ((runstate = pmcstat_print_log(a)) == PMCSTAT_RUNNING)
;
return;
}
/* convert the log to gprof compatible profiles */
assert(0); /* To be implemented */
}
/*
* Print log entries available in a configured parser.
*/
int
pmcstat_print_log(struct pmcstat_args *a)
{
struct pmclog_ev ev;
while (pmclog_read(a->pa_logparser, &ev) == 0) {
assert(ev.pl_state == PMCLOG_OK);
switch (ev.pl_type) {
case PMCLOG_TYPE_CLOSELOG:
PMCSTAT_PRINT_LOG(a,"close",);
break;
case PMCLOG_TYPE_DROPNOTIFY:
PMCSTAT_PRINT_LOG(a,"drop",);
break;
case PMCLOG_TYPE_INITIALIZE:
PMCSTAT_PRINT_LOG(a,"init","0x%x \"%s\"",
ev.pl_u.pl_i.pl_version,
pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch));
break;
case PMCLOG_TYPE_MAPPINGCHANGE:
PMCSTAT_PRINT_LOG(a,"mapping","%s %d %p %p \"%s\"",
ev.pl_u.pl_m.pl_type == PMCLOG_MAPPING_INSERT ?
"insert" : "delete",
ev.pl_u.pl_m.pl_pid,
(void *) ev.pl_u.pl_m.pl_start,
(void *) ev.pl_u.pl_m.pl_end,
ev.pl_u.pl_m.pl_pathname);
break;
case PMCLOG_TYPE_PCSAMPLE:
PMCSTAT_PRINT_LOG(a,"sample","0x%x %d %p",
ev.pl_u.pl_s.pl_pmcid,
ev.pl_u.pl_s.pl_pid,
(void *) ev.pl_u.pl_s.pl_pc);
break;
case PMCLOG_TYPE_PMCALLOCATE:
PMCSTAT_PRINT_LOG(a,"allocate","0x%x \"%s\" 0x%x",
ev.pl_u.pl_a.pl_pmcid,
ev.pl_u.pl_a.pl_evname,
ev.pl_u.pl_a.pl_flags);
break;
case PMCLOG_TYPE_PMCATTACH:
PMCSTAT_PRINT_LOG(a,"attach","0x%x %d \"%s\"",
ev.pl_u.pl_t.pl_pmcid,
ev.pl_u.pl_t.pl_pid,
ev.pl_u.pl_t.pl_pathname);
break;
case PMCLOG_TYPE_PMCDETACH:
PMCSTAT_PRINT_LOG(a,"detach","0x%x %d",
ev.pl_u.pl_d.pl_pmcid,
ev.pl_u.pl_d.pl_pid);
break;
case PMCLOG_TYPE_PROCCSW:
PMCSTAT_PRINT_LOG(a,"csw","0x%x %d %jd",
ev.pl_u.pl_c.pl_pmcid,
ev.pl_u.pl_c.pl_pid,
ev.pl_u.pl_c.pl_value);
break;
case PMCLOG_TYPE_PROCEXEC:
PMCSTAT_PRINT_LOG(a,"exec","%d \"%s\"",
ev.pl_u.pl_x.pl_pid,
ev.pl_u.pl_x.pl_pathname);
break;
case PMCLOG_TYPE_PROCEXIT:
PMCSTAT_PRINT_LOG(a,"exitvalue","0x%x %d %jd",
ev.pl_u.pl_e.pl_pmcid,
ev.pl_u.pl_e.pl_pid,
ev.pl_u.pl_e.pl_value);
break;
case PMCLOG_TYPE_PROCFORK:
PMCSTAT_PRINT_LOG(a,"fork","%d %d",
ev.pl_u.pl_f.pl_oldpid,
ev.pl_u.pl_f.pl_newpid);
break;
case PMCLOG_TYPE_USERDATA:
PMCSTAT_PRINT_LOG(a,"user","0x%x",
ev.pl_u.pl_u.pl_userdata);
break;
case PMCLOG_TYPE_SYSEXIT:
PMCSTAT_PRINT_LOG(a,"exit","%d",
ev.pl_u.pl_se.pl_pid);
break;
default:
fprintf(a->pa_outputfile, "unknown %d",
ev.pl_type);
}
}
if (ev.pl_state == PMCLOG_EOF)
return PMCSTAT_FINISHED;
else if (ev.pl_state == PMCLOG_REQUIRE_DATA)
return PMCSTAT_RUNNING;
err(EX_DATAERR, "ERROR: event parsing failed "
"(record %jd, offset 0x%jx)",
(uintmax_t) ev.pl_count + 1, ev.pl_offset);
/*NOTREACHED*/
}
/*
* Close a logfile, after first flushing all in-module queued data.
*/
int
pmcstat_close_log(struct pmcstat_args *a)
{
if (pmc_flush_logfile() < 0 ||
pmc_configure_logfile(-1) < 0)
err(EX_OSERR, "ERROR: logging failed");
a->pa_flags &= ~FLAG_HAS_LOG_FILE;
return a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
PMCSTAT_FINISHED;
}
void
pmcstat_show_usage(void)
{
@ -340,16 +523,22 @@ pmcstat_show_usage(void)
"\t Measure process and/or system performance using hardware\n"
"\t performance monitoring counters.\n"
"\t Options include:\n"
"\t -C\t\t toggle showing cumulative counts\n"
"\t -O file\t set sampling log file to \"file\"\n"
"\t -P spec\t allocate process-private sampling PMC\n"
"\t -S spec\t allocate system-wide sampling PMC\n"
"\t -c cpu\t\t set default cpu\n"
"\t -d\t\t toggle tracking descendants\n"
"\t -C\t\t (toggle) show cumulative counts\n"
"\t -D path\t create profiles in directory \"path\"\n"
"\t -E\t\t (toggle) show counts at process exit\n"
"\t -O file\t send log output to \"file\"\n"
"\t -P spec\t allocate a process-private sampling PMC\n"
"\t -R file\t read events from \"file\"\n"
"\t -S spec\t allocate a system-wide sampling PMC\n"
"\t -W\t\t (toggle) show counts per context switch\n"
"\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n"
"\t -d\t\t (toggle) track descendants\n"
"\t -g\t\t produce gprof(1) compatible profiles\n"
"\t -m\t\t merge gprof(1) profiles for executables\n"
"\t -n rate\t set sampling rate\n"
"\t -o file\t send print output to \"file\"\n"
"\t -p spec\t allocate process-private counting PMC\n"
"\t -s spec\t allocate system-wide counting PMC\n"
"\t -p spec\t allocate a process-private counting PMC\n"
"\t -s spec\t allocate a system-wide counting PMC\n"
"\t -t pid\t\t attach to running process with pid \"pid\"\n"
"\t -w secs\t set printing time interval"
);
@ -365,12 +554,16 @@ main(int argc, char **argv)
double interval;
int option, npmc, ncpu;
int c, current_cpu, current_sampling_count;
int running;
int do_descendants, use_cumulative_counts;
int do_print, do_descendants;
int do_logproccsw, do_logprocexit;
int logfd;
int pipefd[2];
int use_cumulative_counts;
pid_t pid;
char *end;
const char *errmsg;
enum pmcstat_state runstate;
struct pmcstat_ev *ev;
struct pmc_op_getpmcinfo *ppmci;
struct sigaction sa;
struct kevent kev;
struct winsize ws;
@ -378,33 +571,60 @@ main(int argc, char **argv)
current_cpu = 0;
current_sampling_count = DEFAULT_SAMPLE_COUNT;
do_descendants = 0;
do_logproccsw = 0;
do_logprocexit = 0;
use_cumulative_counts = 0;
args.pa_required = 0;
args.pa_flags = 0;
args.pa_pid = (pid_t) -1;
args.pa_logfile = NULL;
args.pa_outputdir = NULL;
args.pa_outputfile = stderr;
args.pa_interval = DEFAULT_WAIT_INTERVAL;
STAILQ_INIT(&args.pa_head);
ev = NULL;
while ((option = getopt(argc, argv, "CO:P:S:c:dn:o:p:s:t:w:")) != -1)
while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgmn:o:p:s:t:w:"))
!= -1)
switch (option) {
case 'C': /* cumulative values */
use_cumulative_counts = !use_cumulative_counts;
args.pa_required |= FLAG_HAS_COUNTING_PMCS;
break;
case 'c': /* CPU */
current_cpu = strtol(optarg, &end, 0);
if (*end != '\0' || current_cpu < 0)
errx(EX_USAGE,
"ERROR: Illegal CPU number \"%s\"",
"ERROR: Illegal CPU number \"%s\".",
optarg);
args.pa_required |= FLAG_HAS_SYSTEM_PMCS;
break;
case 'd': /* toggle descendents */
do_descendants = !do_descendants;
args.pa_required |= FLAG_HAS_PROCESS_PMCS;
break;
case 'D':
args.pa_outputdir = optarg;
break;
case 'g': /* produce gprof compatible profiles */
args.pa_flags |= FLAG_DO_GPROF;
args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
break;
case 'm': /* produce merged profiles */
args.pa_flags |= FLAG_DO_GPROF_MERGED;
args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
break;
case 'E': /* log process exit */
do_logprocexit = !do_logprocexit;
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE);
break;
case 'p': /* process virtual counting PMC */
@ -412,7 +632,7 @@ main(int argc, char **argv)
case 'P': /* process virtual sampling PMC */
case 'S': /* system-wide sampling PMC */
if ((ev = malloc(sizeof(*ev))) == NULL)
errx(EX_SOFTWARE, "ERROR: Out of memory");
errx(EX_SOFTWARE, "ERROR: Out of memory.");
switch (option) {
case 'p': ev->ev_mode = PMC_MODE_TC; break;
@ -421,14 +641,22 @@ main(int argc, char **argv)
case 'S': ev->ev_mode = PMC_MODE_SS; break;
}
if (option == 'P' || option == 'p')
args.pa_flags |= FLAG_USING_PROCESS_PMC;
if (option == 'P' || option == 'p') {
args.pa_flags |= FLAG_HAS_PROCESS_PMCS;
args.pa_required |= (FLAG_HAS_PROCESS |
FLAG_HAS_PID);
}
if (option == 'P' || option == 'S')
args.pa_flags |= FLAG_USING_SAMPLING;
if (option == 'P' || option == 'S') {
args.pa_flags |= FLAG_HAS_SAMPLING_PMCS;
args.pa_required |= FLAG_HAS_LOG_FILE;
}
if (option == 'p' || option == 's')
args.pa_flags |= FLAG_USING_COUNTING;
args.pa_flags |= FLAG_HAS_COUNTING_PMCS;
if (option == 's' || option == 'S')
args.pa_flags |= FLAG_HAS_SYSTEM_PMCS;
ev->ev_spec = strdup(optarg);
@ -442,7 +670,14 @@ main(int argc, char **argv)
else
ev->ev_cpu = PMC_CPU_ANY;
ev->ev_descendants = do_descendants;
ev->ev_flags = 0;
if (do_descendants)
ev->ev_flags |= PMC_F_DESCENDANTS;
if (do_logprocexit)
ev->ev_flags |= PMC_F_LOG_PROCEXIT;
if (do_logproccsw)
ev->ev_flags |= PMC_F_LOG_PROCCSW;
ev->ev_cumulative = use_cumulative_counts;
ev->ev_saved = 0LL;
@ -458,50 +693,68 @@ main(int argc, char **argv)
break;
case 'R': /* read an existing log file */
if ((logfd = open(optarg, O_RDONLY, 0)) < 0)
err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
"reading", optarg);
if ((args.pa_logparser = pmclog_open(logfd))
== NULL)
err(EX_OSERR, "ERROR: Cannot create parser");
args.pa_flags |= FLAG_PROCESS_LOGFILE;
break;
case 'n': /* sampling count */
current_sampling_count = strtol(optarg, &end, 0);
if (*end != '\0' || current_sampling_count <= 0)
errx(EX_USAGE,
"ERROR: Illegal count value \"%s\"",
"ERROR: Illegal count value \"%s\".",
optarg);
args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
break;
case 'o': /* outputfile */
if (args.pa_outputfile != NULL)
(void) fclose(args.pa_outputfile);
if ((args.pa_outputfile = fopen(optarg, "w")) == NULL)
errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
"writing", optarg);
"writing.", optarg);
args.pa_required |= FLAG_HAS_COUNTING_PMCS;
break;
case 'O': /* sampling output */
if (args.pa_logfile != NULL)
(void) fclose(args.pa_logfile);
errx(EX_OSERR, "ERROR: option -O may only be "
"specified once.");
if ((args.pa_logfile = fopen(optarg, "w")) == NULL)
errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
"writing", optarg);
"writing.", optarg);
args.pa_flags |= FLAG_HAS_LOG_FILE;
break;
case 't': /* target pid */
pid = strtol(optarg, &end, 0);
if (*end != '\0' || pid <= 0)
errx(EX_USAGE, "ERROR: Illegal pid value "
"\"%s\"", optarg);
"\"%s\".", optarg);
args.pa_flags |= FLAG_HAS_PID;
args.pa_required |= FLAG_HAS_PROCESS_PMCS;
args.pa_pid = pid;
break;
case 'w': /* wait interval */
interval = strtod(optarg, &end);
if (*end != '\0' || interval <= 0)
errx(EX_USAGE, "ERROR: Illegal wait interval "
"value \"%s\"", optarg);
"value \"%s\".", optarg);
args.pa_flags |= FLAG_HAS_WAIT_INTERVAL;
args.pa_interval = interval;
break;
case 'W': /* toggle LOG_CSW */
do_logproccsw = !do_logproccsw;
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE);
break;
case '?':
@ -521,25 +774,84 @@ main(int argc, char **argv)
* Check invocation syntax.
*/
if (STAILQ_EMPTY(&args.pa_head)) {
if (args.pa_flags & FLAG_PROCESS_LOGFILE) {
errmsg = NULL;
if (args.pa_flags & FLAG_HAS_PROCESS)
errmsg = "a command line specification";
else if (args.pa_flags & FLAG_HAS_PID)
errmsg = "option -t";
else if (!STAILQ_EMPTY(&args.pa_head))
errmsg = "a PMC event specification";
if (errmsg)
errx(EX_USAGE, "ERROR: option -R may not be used with "
"%s.", errmsg);
} else if (STAILQ_EMPTY(&args.pa_head)) {
warnx("ERROR: At least one PMC event must be specified");
pmcstat_show_usage();
}
if (argc == 0) {
if (args.pa_pid == -1) {
if (args.pa_flags & FLAG_USING_PROCESS_PMC)
errx(EX_USAGE, "ERROR: the -P or -p options "
"require a target process");
} else if ((args.pa_flags & FLAG_USING_PROCESS_PMC) == 0)
errx(EX_USAGE,
"ERROR: option -t requires a process-mode pmc "
"specification");
} else if (args.pa_pid != -1)
/* check for -t pid without a process PMC spec */
if ((args.pa_required & FLAG_HAS_PID) &&
(args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
errx(EX_USAGE, "ERROR: option -t requires a process mode PMC "
"to be specified.");
/* check for process-mode options without a command or -t pid */
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
(args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0)
errx(EX_USAGE, "ERROR: options -d,-E,-p,-P,-W require a "
"command line or target process.");
/* check for -p | -P without a target process of some sort */
if ((args.pa_required & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) &&
(args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0)
errx(EX_USAGE, "ERROR: the -P or -p options require a "
"target process or a command line.");
/* check for process-mode options without a process-mode PMC */
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
(args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
errx(EX_USAGE, "ERROR: options -d,-E,-W require a "
"process mode PMC to be specified.");
/* check for -c cpu and not system mode PMCs */
if ((args.pa_required & FLAG_HAS_SYSTEM_PMCS) &&
(args.pa_flags & FLAG_HAS_SYSTEM_PMCS) == 0)
errx(EX_USAGE, "ERROR: option -c requires at least one "
"system mode PMC to be specified.");
/* check for counting mode options without a counting PMC */
if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) &&
(args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0)
errx(EX_USAGE, "ERROR: options -C,-o,-W require at least one "
"counting mode PMC to be specified.");
/* check for sampling mode options without a sampling PMC spec */
if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) &&
(args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0)
errx(EX_USAGE, "ERROR: options -n,-O require at least one "
"sampling mode PMC to be specified.");
if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_PROCESS)) ==
(FLAG_HAS_PID | FLAG_HAS_PROCESS))
errx(EX_USAGE,
"ERROR: option -t cannot be specified with a command "
"name");
"line.");
/* check if -O was spuriously specified */
if ((args.pa_flags & FLAG_HAS_LOG_FILE) &&
(args.pa_required & FLAG_HAS_LOG_FILE) == 0)
errx(EX_USAGE,
"ERROR: option -O is used only with options "
"-E,-P,-S and -W.");
/* if we've been asked to process a log file, do that and exit */
if (args.pa_flags & FLAG_PROCESS_LOGFILE) {
pmcstat_process_log(&args);
exit(EX_OK);
}
/* otherwise, we've been asked to collect data */
if (pmc_init() < 0)
err(EX_UNAVAILABLE,
"ERROR: Initialization of the pmc(3) library failed");
@ -556,15 +868,9 @@ main(int argc, char **argv)
* Allocate PMCs.
*/
if (pmc_pmcinfo(0, &ppmci) < 0)
err(EX_OSERR, "ERROR: cannot retrieve pmc information");
assert(ppmci != NULL);
STAILQ_FOREACH(ev, &args.pa_head, ev_next)
if (pmc_allocate(ev->ev_spec, ev->ev_mode,
(ev->ev_descendants ? PMC_F_DESCENDANTS : 0),
ev->ev_cpu, &ev->ev_pmcid) < 0)
ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0)
err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with "
"specification \"%s\"",
PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process",
@ -614,25 +920,48 @@ main(int argc, char **argv)
}
EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT");
if (args.pa_flags & FLAG_USING_SAMPLING) {
EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO");
/*
* configure log file
*/
/*
* An exec() failure of a forked child is signalled by the
* child sending the parent a SIGCHLD. We don't register an
* actual signal handler for SIGCHLD, but instead use our
* kqueue to pick up the signal.
*/
EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD");
if (args.pa_logfile == NULL)
if ((args.pa_logfile =
fopen(DEFAULT_LOGFILE_NAME, "w")) == NULL)
err(EX_CANTCREAT, "ERROR: Cannot open sampling "
"log file \"%s\"", DEFAULT_LOGFILE_NAME);
/*
* Configure the specified log file or setup a default log
* consumer via a pipe.
*/
if (args.pa_required & FLAG_HAS_LOG_FILE) {
if (pmc_configure_logfile(fileno(args.pa_logfile)) < 0)
err(EX_OSERR, "ERROR: Cannot configure sampling "
"log");
if (args.pa_logfile == NULL) {
if (pipe(pipefd) < 0)
err(EX_OSERR, "ERROR: pipe(2) failed");
EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD,
0, 0, NULL);
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
err(EX_OSERR, "ERROR: Cannot register kevent");
logfd = pipefd[WRITEPIPEFD];
args.pa_flags |= (FLAG_HAS_PIPE | FLAG_HAS_LOG_FILE);
args.pa_logparser = pmclog_open(pipefd[READPIPEFD]);
} else
logfd = fileno(args.pa_logfile);
if (pmc_configure_logfile(logfd) < 0)
err(EX_OSERR, "ERROR: Cannot configure log file");
STAILQ_FOREACH(ev, &args.pa_head, ev_next)
if (PMC_IS_SAMPLING_MODE(ev->ev_mode) &&
@ -642,7 +971,7 @@ main(int argc, char **argv)
}
/* setup a timer for any counting mode PMCs */
if (args.pa_flags & FLAG_USING_COUNTING) {
if (args.pa_flags & FLAG_HAS_COUNTING_PMCS) {
EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0,
args.pa_interval * 1000, NULL);
@ -674,8 +1003,8 @@ main(int argc, char **argv)
* loop till either the target process (if any) exits, or we
* are killed by a SIGINT.
*/
running = 1;
runstate = PMCSTAT_RUNNING;
do_print = 0;
do {
if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) {
if (errno != EINTR)
@ -688,26 +1017,43 @@ main(int argc, char **argv)
errc(EX_OSERR, kev.data, "ERROR: kevent failed");
switch (kev.filter) {
case EVFILT_PROC: /* target process exited */
running = 0;
/* FALLTHROUGH */
case EVFILT_PROC: /* target has exited */
if (args.pa_flags & FLAG_HAS_LOG_FILE)
runstate = pmcstat_close_log(&args);
break;
case EVFILT_TIMER: /* print out counting PMCs */
pmcstat_print_pmcs(&args);
if (running == 0) /* final newline */
(void) fprintf(args.pa_outputfile, "\n");
case EVFILT_READ: /* log file data is present */
runstate = pmcstat_print_log(&args);
break;
case EVFILT_SIGNAL:
if (kev.ident == SIGINT) {
if (kev.ident == SIGCHLD) {
/*
* The child process sends us a
* SIGCHLD if its exec() failed. We
* wait for it to exit and then exit
* ourselves.
*/
(void) wait(&c);
runstate = PMCSTAT_FINISHED;
} else if (kev.ident == SIGIO) {
/*
* We get a SIGIO if a PMC loses all
* of its targets, or if logfile
* writes encounter an error.
*/
if (args.pa_flags & FLAG_HAS_LOG_FILE)
runstate = pmcstat_close_log(&args);
do_print = 1; /* print PMCs at exit */
runstate = PMCSTAT_FINISHED;
} else if (kev.ident == SIGINT) {
/* pass the signal on to the child process */
if ((args.pa_flags & FLAG_HAS_PROCESS) &&
(args.pa_flags & FLAG_HAS_PID) == 0)
if (kill(args.pa_pid, SIGINT) != 0)
err(EX_OSERR, "cannot kill "
"child");
running = 0;
err(EX_OSERR, "ERROR: cannot "
"signal child process");
runstate = PMCSTAT_FINISHED;
} else if (kev.ident == SIGWINCH) {
if (ioctl(fileno(args.pa_outputfile),
TIOCGWINSZ, &ws) < 0)
@ -718,9 +1064,25 @@ main(int argc, char **argv)
assert(0);
break;
case EVFILT_TIMER: /* print out counting PMCs */
do_print = 1;
break;
}
} while (running);
if (do_print) {
pmcstat_print_pmcs(&args);
if (runstate == PMCSTAT_FINISHED) /* final newline */
(void) fprintf(args.pa_outputfile, "\n");
do_print = 0;
}
} while (runstate != PMCSTAT_FINISHED);
/* flush any pending log entries */
if (args.pa_flags & FLAG_HAS_LOG_FILE)
pmc_flush_logfile();
pmcstat_cleanup(&args);