example of self-montioring task using overflow notifications.
Example of a self-monitoring process
/*
* we must include the perfmon and pfmlib header files
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <perfmon/perfmon.h>
#include <perfmon/pfmlib.h>
#define MAX_EVT_NAME_LEN 256
int
main(int argc, char **argv)
{
int i, ret, fd;
pfmlib_input_param_t inp;
pfmlib_output_param_t outp;
pfarg_reg_t pc[2];
pfarg_reg_t pd[2];
pfarg_load_t load_args;
pfarg_context_t ctx[1];
char name[MAX_EVT_NAME_LEN];
/*
* Initialize libpfm (required before we use it)
*/
if (pfm_initialize() != PFMLIB_SUCCESS) {
fprintf(stderr, "cannot initialize libpfm\n");
exit(1);
}
/*
* initialize local variables
*/
memset(pc, 0, sizeof(pc));
memset(pd, 0, sizeof(pd));
memset(ctx, 0, sizeof(ctx));
memset(&inp,0, sizeof(inp));
memset(&outp,0, sizeof(outp));
memset(&load_args,0, sizeof(load_args));
/*
* find event descriptors using event names
*/
ret = pfm_find_event("cpu_cycles",
&inp.pfp_events[0].event);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr,"cpu_cycles not found\n");
exit(1);
}
ret = pfm_find_event("ia64_inst_retired",
&inp.pfp_events[1].event);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr,"ia64_inst_retired not found\n");
exit(1);
}
/*
* set the default privilege mode for all counters:
* PFM_PLM3 : user level only
*/
inp.pfp_dfl_plm = PFM_PLM3;
/*
* how many events we are interested in
*/
inp.pfp_event_count = 2;
/*
* let the library figure out the values for the PMCS
*/
ret = pfm_dispatch_events(&inp, NULL, &outp, NULL);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr, "cannot configure events: %s\n",
pfm_strerror(ret));
exit(1);
}
/*
* copy the library parameters to the OS-specific structures.
* Here we propagate the PMC indexes and values.
*/
for (i=0; i < outp.pfp_pmc_count; i++) {
pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
}
/*
* propagate the PMC indexes to the PMD arguments to the
* kernel. This is required for counting monitors.
*/
for (i=0; i < inp.pfp_event_count; i++) {
pd[i].reg_num = pc[i].reg_num;
}
/*
* now create the context for self monitoring/per-task
*/
ret = perfmonctl(0, PFM_CREATE_CONTEXT, ctx, 1);
if (ret == -1) {
fprintf(stderr, "PFM_CREATE_CONTEXT errno %d\n", errno);
exit(1);
}
/*
* extract the file descriptor identifying the context
*/
fd = ctx[0].ctx_fd;
/*
* Now program the PMC registers.
*/
ret = perfmonctl(fd, PFM_WRITE_PMCS, pc, outp.pfp_pmc_count);
if (ret == -1) {
fprintf(stderr, "PFM_WRITE_PMCS errno %d\n",errno);
exit(1);
}
/*
* We reset the PMDs that go with the PMCs
*/
ret = perfmonctl(fd, PFM_WRITE_PMDS, pd, inp.pfp_event_count);
if (ret == -1) {
fprintf(stderr, "PFM_WRITE_PMDS errno %d\n",errno);
exit(1);
}
/*
* attach the perfmon context to ourself
*/
load_args.load_pid = getpid();
ret = perfmonctl(fd, PFM_LOAD_CONTEXT, &load_args, 1);
if (ret == -1) {
fprintf(stderr, "PFM_LOAD_CONTEXT errno %d\n",errno);
exit(1);
}
/*
* start monitoring. For self-monitoring (not system-wide) tasks, it is possible to
* use the lightweight library call instead of PFM_START
*/
pfm_self_start(fd);
/*
*
* code to monitor goes here
*
*/
/*
* stop monitoring. For self-monitoring (not system-wide) tasks, it is possible to
* use the lightweight library call instead of PFM_STOP
*/
pfm_self_stop(fd);
/*
* now read the results
*/
ret = perfmonctl(fd, PFM_READ_PMDS, pd, inp.pfp_event_count);
if (ret == -1) {
fprintf(stderr, "READ_PMDS errno %d\n",errno);
exit(1);
}
/*
* and finally, print the results
*/
for (i=0; i < inp.pfp_event_count; i++) {
pfm_get_event_name(inp.pfp_events[i].event, name, MAX_EVT_NAME_LEN);
printf("PMD%u %20lu %s\n",
pd[i].reg_num,
pd[i].reg_value,
name);
}
/*
* destroy the perfmon context
*/
close(fd);
return 0;
}
Example of a single-cpu system wide session
/*
* we must include the perfmon and pfmlib header files
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <perfmon/perfmon.h>
#include <perfmon/pfmlib.h>
#define MAX_EVT_NAME_LEN 256
int
main(int argc, char **argv)
{
unsigned long cpu_mask;
int i, ret, fd;
pfmlib_input_param_t inp;
pfmlib_output_param_t outp;
pfarg_reg_t pc[2];
pfarg_reg_t pd[2];
pfarg_load_t load_args;
pfarg_context_t ctx[1];
char name[MAX_EVT_NAME_LEN];
/*
* Initialize libpfm (required before we use it)
*/
if (pfm_initialize() != PFMLIB_SUCCESS) {
fprintf(stderr, "cannot initialize libpfm\n");
exit(1);
}
/*
* initialize local variables
*/
memset(pd, 0, sizeof(pd));
memset(pc, 0, sizeof(pc));
memset(ctx, 0, sizeof(ctx));
memset(&inp,0, sizeof(inp));
memset(&outp,0, sizeof(outp));
memset(&load_args,0, sizeof(load_args));
/*
* find event descriptors using their event names
*/
ret = pfm_find_event("cpu_cycles",
&inp.pfp_events[0].event);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr,"cpu_cycles not found\n");
exit(1);
}
ret = pfm_find_event("ia64_inst_retired",
&inp.pfp_events[1].event);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr,"ia64_inst_retired not found\n");
exit(1);
}
/*
* set the default privilege mode for all counters:
* PFM_PLM3 : user level only
*/
inp.pfp_dfl_plm = PFM_PLM3;
/*
* how many events we are interested in
*/
inp.pfp_event_count = 2;
/*
* we are using the counters for a system-wide session
*/
inp.pfp_flags = PFMLIB_PFP_SYSTEMWIDE;
/*
* let the library figure out the values for the PMCS
*/
ret = pfm_dispatch_events(&inp, NULL, &outp, NULL);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr, "cannot configure events: %s\n",
pfm_strerror(ret));
exit(1);
}
/*
* copy the library parameters to the OS-specific structures.
* Here we propagate the PMC indexes and values.
*/
for (i=0; i < outp.pfp_pmc_count; i++) {
pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
}
/*
* propagate the PMC indexes to the PMD arguments to the
* kernel. This is required for counting monitors.
*/
for (i=0; i < inp.pfp_event_count; i++) {
pd[i].reg_num = pc[i].reg_num;
}
/*
* pin our task to a particular processor, here we chose CPU0
*/
cpu_mask = 1UL << 0;
ret = sched_setaffinity(getpid(), &cpu_mask, sizeof(cpu_mask));
if (ret == -1) {
fprintf(stderr, "cannot pin to CPU: %s\n",
strerror(errno));
exit(1);
}
/*
* indicate we want a system-wide context
*/
ctx[0].ctx_flags = PFM_FL_SYSTEM_WIDE;
/*
* now create the context the system-wide session
*/
ret = perfmonctl(0, PFM_CREATE_CONTEXT, ctx, 1);
if (ret == -1) {
fprintf(stderr, "PFM_CREATE_CONTEXT errno %d\n", errno);
exit(1);
}
/*
* extract the file descriptor identifying the context
*/
fd = ctx[0].ctx_fd;
/*
* Now program the PMC registers.
*/
ret = perfmonctl(fd, PFM_WRITE_PMCS, pc, outp.pfp_pmc_count);
if (ret == -1) {
fprintf(stderr, "PFM_WRITE_PMCS errno %d\n",errno);
exit(1);
}
/*
* We reset the PMDs that go with the PMCs
*/
ret = perfmonctl(fd, PFM_WRITE_PMDS, pd, inp.pfp_event_count);
if (ret == -1) {
fprintf(stderr, "PFM_WRITE_PMDS errno %d\n",errno);
exit(1);
}
/*
* attach the perfmon context to ourself, system-wide is
* ALWAYS self-monitoring.
*/
load_args.load_pid = getpid();
ret = perfmonctl(fd, PFM_LOAD_CONTEXT, &load_args, 1);
if (ret == -1) {
fprintf(stderr, "PFM_LOAD_CONTEXT errno %d\n",errno);
exit(1);
}
/*
* start monitoring. For system-wide sessions,
* we must ALWAYS call the kernel
*/
ret = perfmonctl(fd, PFM_START, NULL, 0);
if (ret == -1) {
fprintf(stderr, "PFM_START errno %d\n",errno);
exit(1);
}
/*
* now monitoring
*/
printf("<press a key to stop monitoring>\n"); getchar();
/*
* stop monitoring. For system-wide sessions,
* we must ALWAYS call the kernel
*/
ret = perfmonctl(fd, PFM_STOP, NULL, 0);
if (ret == -1) {
fprintf(stderr, "PFM_STOP errno %d\n",errno);
exit(1);
}
/*
* now read the results
*/
ret = perfmonctl(fd, PFM_READ_PMDS, pd, inp.pfp_event_count);
if (ret == -1) {
fprintf(stderr, "READ_PMDS errno %d\n",errno);
exit(1);
}
/*
* and finally, print the results
*/
for (i=0; i < inp.pfp_event_count; i++) {
pfm_get_event_name(inp.pfp_events[i].event, name, MAX_EVT_NAME_LEN);
printf("PMD%u %20lu %s\n",
pd[i].reg_num,
pd[i].reg_value,
name);
}
/*
* destroy the perfmon context
*/
close(fd);
return 0;
}
Example of a self-monitoring task with overflow notifications
/*
* we must include the perfmon and pfmlib header files
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <perfmon/perfmon.h>
#include <perfmon/pfmlib.h>
#define SMPL_PERIOD 1000000000UL
static unsigned long notification_received;
#define NUM_PMCS PFMLIB_MAX_PMCS
#define NUM_PMDS PFMLIB_MAX_PMDS
static int ctx_fd;
static void
sigio_handler(int n)
{
pfm_msg_t msg;
int ret;
/*
* extract overflow message from queue
*/
ret = read(ctx_fd, &msg, sizeof(msg));
if (ret != sizeof(msg)) {
fprintf(stderr, "cannot read overflow message: %s\n", strerror(errno));
exit(1);
}
/*
* increment our notification counter
*/
notification_received++;
/*
* and resume monitoring
*/
ret = perfmonctl(ctx_fd, PFM_RESTART,NULL, 0);
if (ret == -1) {
fprintf(stderr, "error PFM_RESTART: %s", strerror(errno));
exit(1);
}
}
int
main(int argc, char **argv)
{
char **p;
pfarg_context_t ctx[1];
pfmlib_input_param_t inp;
pfmlib_output_param_t outp;
pfarg_reg_t pc[NUM_PMCS];
pfarg_reg_t pd[NUM_PMDS];
pfarg_load_t load_args;
pfmlib_options_t pfmlib_options;
unsigned long sum;
struct sigaction act;
unsigned int i;
int fd, ret;
/*
* Initialize libpfm (required before we use it)
*/
if (pfm_initialize() != PFMLIB_SUCCESS) {
fprintf(stderr, "cannot initialize libpfm\n");
exit(1);
}
/*
* Install the signal handler (SIGIO)
*/
memset(&act, 0, sizeof(act));
act.sa_handler = (sig_t)sigio_handler;
sigaction (SIGIO, &act, 0);
/*
* initialize local variables
*/
memset(pc, 0, sizeof(pc));
memset(pd, 0, sizeof(pd));
memset(ctx, 0, sizeof(ctx));
memset(&inp,0, sizeof(inp));
memset(&outp,0, sizeof(outp));
memset(&load_args, 0, sizeof(load_args));
/*
* find event descriptors using their event names
*/
ret = pfm_find_event("cpu_cycles",
&inp.pfp_events[0].event);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr,"cpu_cycles not found\n");
exit(1);
}
/*
* set the default privilege mode for all counters:
* PFM_PLM3 : user level only
*/
inp.pfp_dfl_plm = PFM_PLM3;
/*
* how many events we are interested in
*/
inp.pfp_event_count = 1;
/*
* let the library figure out the values for the PMCS
*/
ret = pfm_dispatch_events(&inp, NULL, &outp, NULL);
if (ret != PFMLIB_SUCCESS) {
fprintf(stderr, "cannot configure events: %s\n",
pfm_strerror(ret));
exit(1);
}
/*
* copy the library parameters to the OS-specific structures.
* Here we propagate the PMC indexes and values.
*/
for (i=0; i < outp.pfp_pmc_count; i++) {
pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
}
/*
* propagate the PMC indexes to the PMD arguments to the
* kernel. This is required for counting monitors.
*/
for (i=0; i < inp.pfp_event_count; i++) {
pd[i].reg_num = pc[i].reg_num;
}
/*
* now create the context the system-wide session
*/
ret = perfmonctl(0, PFM_CREATE_CONTEXT, ctx, 1);
if (ret == -1) {
fprintf(stderr, "PFM_CREATE_CONTEXT errno %d\n", errno);
exit(1);
}
/*
* extract the file descriptor identifying the context
* and make a global copy for signal handler
*/
fd = ctx_fd = ctx[0].ctx_fd;
/*
* We want to get notified when the counter overflows
*/
pc[0].reg_flags |= PFM_REGFL_OVFL_NOTIFY;
/*
* we arm the counter, such that it will overflow
* after SMPL_PERIOD events have been observed
*/
pd[0].reg_value = ~0UL - SMPL_PERIOD + 1;
pd[0].reg_long_reset = ~0UL - SMPL_PERIOD + 1;
pd[0].reg_short_reset = ~0UL - SMPL_PERIOD + 1;
/*
* Now program the PMC registers.
*/
ret = perfmonctl(fd, PFM_WRITE_PMCS, pc, outp.pfp_pmc_count);
if (ret == -1) {
fprintf(stderr, "PFM_WRITE_PMCS errno %d\n",errno);
exit(1);
}
/*
* We reset the PMDs that go with the PMCs
*/
ret = perfmonctl(fd, PFM_WRITE_PMDS, pd, inp.pfp_event_count);
if (ret == -1) {
fprintf(stderr, "PFM_WRITE_PMDS errno %d\n",errno);
exit(1);
}
/*
* attach the perfmon context to ourself, system-wide is
* ALWAYS self-monitoring.
*/
load_args.load_pid = getpid();
ret = perfmonctl(fd, PFM_LOAD_CONTEXT, &load_args, 1);
if (ret == -1) {
fprintf(stderr, "PFM_LOAD_CONTEXT errno %d\n",errno);
exit(1);
}
/*
* setup asynchronous notification on the file descriptor
*/
ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_ASYNC);
if (ret == -1) {
fprintf(stderr, "cannot set ASYNC: %s\n", strerror(errno));
exit(1);
}
/*
* get ownership of the descriptor
*/
ret = fcntl(fd, F_SETOWN, getpid());
if (ret == -1) {
fprintf(stderr, "cannot set ownership: %s\n", strerror(errno));
exit(1);
}
/*
* start monitoring. For self-monitoring (not system-wide) tasks, it is possible to
* use the lightweight library call instead of PFM_START
*/
pfm_self_start(fd);
/*
*
* code to monitor goes here. We use a stupid loop
* to burn some cycles.
*
*/
for(i=0, sum=0; i < 4*SMPL_PERIOD; i++) sum +=2*i;
/*
* stop monitoring. For self-monitoring (not system-wide) tasks, it is possible to
* use the lightweight library call instead of PFM_STOP
*/
pfm_self_stop(fd);
/*
* destroy the perfmon context
*/
close(fd);
/*
* print number of noticifications, i.e., overflows.
* the test on sum is to ensure the compiler does not optimize
* the loop away.
*/
if (sum) printf("%lu overflows generated\n", notification_received);
return 0;
}
