Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP

hp.com home


kernel perfmon interface examples

» 

HP Labs

» Research
» News and events
» Technical reports
» About HP Labs
» Careers @ HP Labs
» People
» Worldwide sites
» Downloads
Content starts here

Here we present two simple examples of how to use the kernel perfmon-2.x interface (2.6-based kernels) and also the library (libpfm-3.0
  • example of a self-monitoring session.
  • example of a single CPU system wide session.
  • 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;
    }
    


  • perfmon project links

    » project home
    » perfmon overview
    » libpfm overview
    » pfmon overview
    » mailing list
    » downloads
    » bibliography
    » presentations

    kernel interface links

    » FAQ
    » examples
    Printable version
    Privacy statement Using this site means you accept its terms Feedback to HP Labs
    © 2009 Hewlett-Packard Development Company, L.P.