int _papi_libpfm_init(papi_vector_t *my_vector, int cidx) { int retval; unsigned int ncnt; unsigned int version; char pmu_name[PAPI_MIN_STR_LEN]; /* The following checks the version of the PFM library against the version PAPI linked to... */ SUBDBG( "pfm_initialize()\n" ); if ( ( retval = pfm_initialize( ) ) != PFMLIB_SUCCESS ) { PAPIERROR( "pfm_initialize(): %s", pfm_strerror( retval ) ); return PAPI_ESYS; } /* Get the libpfm3 version */ SUBDBG( "pfm_get_version(%p)\n", &version ); if ( pfm_get_version( &version ) != PFMLIB_SUCCESS ) { PAPIERROR( "pfm_get_version(%p): %s", version, pfm_strerror( retval ) ); return PAPI_ESYS; } /* Set the version */ sprintf( my_vector->cmp_info.support_version, "%d.%d", PFM_VERSION_MAJOR( version ), PFM_VERSION_MINOR( version ) ); /* Complain if the compiled-against version doesn't match current version */ if ( PFM_VERSION_MAJOR( version ) != PFM_VERSION_MAJOR( PFMLIB_VERSION ) ) { PAPIERROR( "Version mismatch of libpfm: compiled %#x vs. installed %#x\n", PFM_VERSION_MAJOR( PFMLIB_VERSION ), PFM_VERSION_MAJOR( version ) ); return PAPI_ESYS; } /* Always initialize globals dynamically to handle forks properly. */ _perfmon2_pfm_pmu_type = -1; /* Opened once for all threads. */ SUBDBG( "pfm_get_pmu_type(%p)\n", &_perfmon2_pfm_pmu_type ); if ( pfm_get_pmu_type( &_perfmon2_pfm_pmu_type ) != PFMLIB_SUCCESS ) { PAPIERROR( "pfm_get_pmu_type(%p): %s", _perfmon2_pfm_pmu_type, pfm_strerror( retval ) ); return PAPI_ESYS; } pmu_name[0] = '\0'; if ( pfm_get_pmu_name( pmu_name, PAPI_MIN_STR_LEN ) != PFMLIB_SUCCESS ) { PAPIERROR( "pfm_get_pmu_name(%p,%d): %s", pmu_name, PAPI_MIN_STR_LEN, pfm_strerror( retval ) ); return PAPI_ESYS; } SUBDBG( "PMU is a %s, type %d\n", pmu_name, _perfmon2_pfm_pmu_type ); /* Setup presets */ retval = _papi_load_preset_table( pmu_name, _perfmon2_pfm_pmu_type, cidx ); if ( retval ) return retval; /* Fill in cmp_info */ SUBDBG( "pfm_get_num_events(%p)\n", &ncnt ); if ( ( retval = pfm_get_num_events( &ncnt ) ) != PFMLIB_SUCCESS ) { PAPIERROR( "pfm_get_num_events(%p): %s\n", &ncnt, pfm_strerror( retval ) ); return PAPI_ESYS; } SUBDBG( "pfm_get_num_events: %d\n", ncnt ); my_vector->cmp_info.num_native_events = ncnt; num_native_events = ncnt; pfm_get_num_counters( ( unsigned int * ) &my_vector->cmp_info.num_cntrs ); SUBDBG( "pfm_get_num_counters: %d\n", my_vector->cmp_info.num_cntrs ); if ( _papi_hwi_system_info.hw_info.vendor == PAPI_VENDOR_INTEL ) { /* Pentium4 */ if ( _papi_hwi_system_info.hw_info.cpuid_family == 15 ) { PAPI_NATIVE_EVENT_AND_MASK = 0x000000ff; PAPI_NATIVE_UMASK_AND_MASK = 0x0fffff00; PAPI_NATIVE_UMASK_SHIFT = 8; /* Itanium2 */ } else if ( _papi_hwi_system_info.hw_info.cpuid_family == 31 || _papi_hwi_system_info.hw_info.cpuid_family == 32 ) { PAPI_NATIVE_EVENT_AND_MASK = 0x00000fff; PAPI_NATIVE_UMASK_AND_MASK = 0x0ffff000; PAPI_NATIVE_UMASK_SHIFT = 12; } } return PAPI_OK; }
int main(void) { int ret; int type = 0; pid_t pid = getpid(); pfmlib_ita2_param_t ita_param; pfarg_reg_t pd[NUM_PMDS]; pfarg_context_t ctx[1]; pfmlib_options_t pfmlib_options; struct sigaction act; /* * Initialize pfm library (required before we can use it) */ if (pfm_initialize() != PFMLIB_SUCCESS) { fatal_error("Can't initialize library\n"); } /* * Let's make sure we run this on the right CPU */ pfm_get_pmu_type(&type); if (type != PFMLIB_ITANIUM2_PMU) { char *model; pfm_get_pmu_name(&model); fatal_error("this program does not work with %s PMU\n", model); } /* * Install the overflow handler (SIGPROF) */ memset(&act, 0, sizeof(act)); act.sa_handler = (sig_t)overflow_handler; sigaction (SIGPROF, &act, 0); /* * pass options to library (optional) */ memset(&pfmlib_options, 0, sizeof(pfmlib_options)); pfmlib_options.pfm_debug = 0; /* set to 1 for debug */ pfmlib_options.pfm_verbose = 0; /* set to 1 for debug */ pfm_set_options(&pfmlib_options); memset(pd, 0, sizeof(pd)); memset(ctx, 0, sizeof(ctx)); /* * prepare parameters to library. we don't use any Itanium * specific features here. so the pfp_model is NULL. */ memset(&evt,0, sizeof(evt)); memset(&ita_param,0, sizeof(ita_param)); /* * because we use a model specific feature, we must initialize the * model specific pfmlib parameter structure and link it to the * common structure. * The magic number is a simple mechanism used by the library to check * that the model specific data structure is decent. You must set it manually * otherwise the model specific feature won't work. */ ita_param.pfp_magic = PFMLIB_ITA2_PARAM_MAGIC; evt.pfp_model = &ita_param; /* * Before calling pfm_find_dispatch(), we must specify what kind * of branches we want to capture. We are interesteed in all the mispredicted branches, * therefore we program we set the various fields of the BTB config to: */ ita_param.pfp_ita2_btb.btb_used = 1; ita_param.pfp_ita2_btb.btb_ds = 0; ita_param.pfp_ita2_btb.btb_tm = 0x3; ita_param.pfp_ita2_btb.btb_ptm = 0x3; ita_param.pfp_ita2_btb.btb_ppm = 0x3; ita_param.pfp_ita2_btb.btb_brt = 0x0; ita_param.pfp_ita2_btb.btb_plm = PFM_PLM3; /* * To count the number of occurence of this instruction, we must * program a counting monitor with the IA64_TAGGED_INST_RETIRED_PMC8 * event. */ if (pfm_find_event_byname("BRANCH_EVENT", &evt.pfp_events[0].event) != PFMLIB_SUCCESS) { fatal_error("cannot find event BRANCH_EVENT\n"); } /* * set the (global) privilege mode: * PFM_PLM3 : user level only */ evt.pfp_dfl_plm = PFM_PLM3; /* * how many counters we use */ evt.pfp_event_count = 1; /* * let the library figure out the values for the PMCS */ if ((ret=pfm_dispatch_events(&evt)) != PFMLIB_SUCCESS) { fatal_error("cannot configure events: %s\n", pfm_strerror(ret)); } /* * for this example, we will get notified ONLY when the sampling * buffer is full. The monitoring is not to be inherited * in derived tasks */ ctx[0].ctx_flags = PFM_FL_INHERIT_NONE; ctx[0].ctx_notify_pid = getpid(); ctx[0].ctx_smpl_entries = SMPL_BUF_NENTRIES; ctx[0].ctx_smpl_regs[0] = smpl_regs = BTB_REGS_MASK; /* * now create the context for self monitoring/per-task */ if (perfmonctl(pid, PFM_CREATE_CONTEXT, ctx, 1) == -1 ) { if (errno == ENOSYS) { fatal_error("Your kernel does not have performance monitoring support!\n"); } fatal_error("Can't create PFM context %s\n", strerror(errno)); } printf("Sampling buffer mapped at %p\n", ctx[0].ctx_smpl_vaddr); smpl_vaddr = ctx[0].ctx_smpl_vaddr; /* * Must be done before any PMD/PMD calls (unfreeze PMU). Initialize * PMC/PMD to safe values. psr.up is cleared. */ if (perfmonctl(pid, PFM_ENABLE, NULL, 0) == -1) { fatal_error("perfmonctl error PFM_ENABLE errno %d\n",errno); } /* * indicate we want notification when buffer is full */ evt.pfp_pc[0].reg_flags |= PFM_REGFL_OVFL_NOTIFY; /* * Now prepare the argument to initialize the PMD and the sampling period */ pd[0].reg_num = evt.pfp_pc[0].reg_num; 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; /* * When our counter overflows, we want to BTB index to be reset, so that we keep * in sync. This is required to make it possible to interpret pmd16 on overflow * to avoid repeating the same branch several times. */ evt.pfp_pc[0].reg_reset_pmds[0] = M_PMD(16); /* * reset pmd16, short and long reset value are set to zero as well */ pd[1].reg_num = 16; pd[1].reg_value = 0UL; /* * Now program the registers * * We don't use the save variable to indicate the number of elements passed to * the kernel because, as we said earlier, pc may contain more elements than * the number of events we specified, i.e., contains more thann coutning monitors. */ if (perfmonctl(pid, PFM_WRITE_PMCS, evt.pfp_pc, evt.pfp_pc_count) == -1) { fatal_error("perfmonctl error PFM_WRITE_PMCS errno %d\n",errno); } if (perfmonctl(pid, PFM_WRITE_PMDS, pd, 2) == -1) { fatal_error("perfmonctl error PFM_WRITE_PMDS errno %d\n",errno); } /* * Let's roll now. */ do_test(100000); /* * We must call the processing routine to cover the last entries recorded * in the sampling buffer, i.e. which may not be full */ process_smpl_buffer(); /* * let's stop this now */ if (perfmonctl(pid, PFM_DESTROY_CONTEXT, NULL, 0) == -1) { fatal_error("perfmonctl error PFM_DESTROY errno %d\n",errno); } return 0; }
int main(void) { int ret; int type = 0; char *name; pid_t pid = getpid(); pfmlib_param_t evt; pfmlib_ita2_param_t ita2_param; pfarg_reg_t pd[NUM_PMDS]; pfarg_context_t ctx[1]; pfmlib_options_t pfmlib_options; /* * Initialize pfm library (required before we can use it) */ if (pfm_initialize() != PFMLIB_SUCCESS) { fatal_error("Can't initialize library\n"); } /* * Let's make sure we run this on the right CPU */ pfm_get_pmu_type(&type); if (type != PFMLIB_ITANIUM2_PMU) { char *model; pfm_get_pmu_name(&model); fatal_error("this program does not work with the %s PMU\n", model); } /* * pass options to library (optional) */ memset(&pfmlib_options, 0, sizeof(pfmlib_options)); pfmlib_options.pfm_debug = 0; /* set to 1 for debug */ pfmlib_options.pfm_verbose = 0; /* set to 1 for verbose */ pfm_set_options(&pfmlib_options); memset(pd, 0, sizeof(pd)); memset(ctx, 0, sizeof(ctx)); memset(&evt,0, sizeof(evt)); memset(&ita2_param,0, sizeof(ita2_param)); /* * because we use a model specific feature, we must initialize the * model specific pfmlib parameter structure and link it to the * common structure. * The magic number is a simple mechanism used by the library to check * that the model specific data structure is decent. You must set it manually * otherwise the model specific feature won't work. */ ita2_param.pfp_magic = PFMLIB_ITA2_PARAM_MAGIC; evt.pfp_model = &ita2_param; /* * We indicate that we are using the PMC8 opcode matcher. This is required * otherwise the library add PMC8 to the list of PMC to pogram during * pfm_dispatch_events(). */ ita2_param.pfp_ita2_pmc8.opcm_used = 1; /* * We want to match all the br.cloop in our test function. * This branch is an IP-relative branch for which the major * opcode (bits [40-37]=4) and the btype field is 5 (which represents * bits[6-8]) so it is included in the match/mask fields of PMC8. * It is necessarily in a B slot. * * We don't care which operands are used with br.cloop therefore * the mask field of pmc8 is set such that only the 4 bits of the * opcode and 3 bits of btype must match exactly. This is accomplished by * clearing the top 4 bits and bits [6-8] of the mask field and setting the * remaining bits. Similarly, the match field only has the opcode value and btype * set according to the encoding of br.cloop, the * remaining bits are zero. Bit 60 of PMC8 is set to indicate * that we look only in B slots (this is the only possibility for * this instruction anyway). * * So the binary representation of the value for PMC8 is as follows: * * 6666555555555544444444443333333333222222222211111111110000000000 * 3210987654321098765432109876543210987654321098765432109876543210 * ---------------------------------------------------------------- * 0001010000000000000000101000000000000011111111111111000111111000 * * which yields a value of 0x1400028003fff1f8. * * Depending on the level of optimization to compile this code, it may * be that the count reported could be zero, if the compiler uses a br.cond * instead of br.cloop. * * * The 0x1 sets the ig_ad field to make sure we ignore any range restriction. * Also bit 2 must always be set */ ita2_param.pfp_ita2_pmc8.pmc_val = 0x1400028003fff1fa; /* * To count the number of occurence of this instruction, we must * program a counting monitor with the IA64_TAGGED_INST_RETIRED_PMC8 * event. */ if (pfm_find_event_byname("IA64_TAGGED_INST_RETIRED_IBRP0_PMC8", &evt.pfp_events[0].event) != PFMLIB_SUCCESS) { fatal_error("cannot find event IA64_TAGGED_INST_RETIRED_IBRP0_PMC8\n"); } /* * set the privilege mode: * PFM_PLM3 : user level only */ evt.pfp_dfl_plm = PFM_PLM3; /* * how many counters we use */ evt.pfp_event_count = 1; /* * let the library figure out the values for the PMCS */ if ((ret=pfm_dispatch_events(&evt)) != PFMLIB_SUCCESS) { fatal_error("cannot configure events: %s\n", pfm_strerror(ret)); } /* * for this example, we have decided not to get notified * on counter overflows and the monitoring is not to be inherited * in derived tasks */ ctx[0].ctx_flags = PFM_FL_INHERIT_NONE; /* * now create the context for self monitoring/per-task */ if (perfmonctl(pid, PFM_CREATE_CONTEXT, ctx, 1) == -1 ) { if (errno == ENOSYS) { fatal_error("Your kernel does not have performance monitoring support!\n"); } fatal_error("Can't create PFM context %s\n", strerror(errno)); } /* * Must be done before any PMD/PMD calls (unfreeze PMU). Initialize * PMC/PMD to safe values. psr.up is cleared. */ if (perfmonctl(pid, PFM_ENABLE, NULL, 0) == -1) { fatal_error("perfmonctl error PFM_ENABLE errno %d\n",errno); } /* * Now prepare the argument to initialize the PMD. */ pd[0].reg_num = evt.pfp_pc[0].reg_num; /* * Now program the registers * * We don't use the save variable to indicate the number of elements passed to * the kernel because, as we said earlier, pc may contain more elements than * the number of events we specified, i.e., contains more thann coutning monitors. */ if (perfmonctl(pid, PFM_WRITE_PMCS, evt.pfp_pc, evt.pfp_pc_count) == -1) { fatal_error("perfmonctl error PFM_WRITE_PMCS errno %d\n",errno); } if (perfmonctl(pid, PFM_WRITE_PMDS, pd, evt.pfp_event_count) == -1) { fatal_error("perfmonctl error PFM_WRITE_PMDS errno %d\n",errno); } /* * Let's roll now. */ pfm_start(); do_test(100UL); pfm_stop(); /* * now read the results */ if (perfmonctl(pid, PFM_READ_PMDS, pd, evt.pfp_event_count) == -1) { fatal_error("perfmonctl error READ_PMDS errno %d\n",errno); } /* * print the results */ pfm_get_event_name(evt.pfp_events[0].event, &name); printf("PMD%u %20lu %s\n", pd[0].reg_num, pd[0].reg_value, name); if (pd[0].reg_value != 0) printf("compiler used br.cloop\n"); else printf("compiler did not use br.cloop\n"); /* * let's stop this now */ if (perfmonctl(pid, PFM_DESTROY_CONTEXT, NULL, 0) == -1) { fatal_error("perfmonctl error PFM_DESTROY errno %d\n",errno); } return 0; }
int main(void) { pfmlib_input_param_t inp; pfmlib_output_param_t outp; pfmlib_ita2_input_param_t ita2_inp; pfarg_reg_t pd[NUM_PMDS]; pfarg_reg_t pc[NUM_PMCS]; pfarg_context_t ctx[1]; pfarg_load_t load_args; pfmlib_options_t pfmlib_options; int ret; int type = 0; int id; unsigned int i; char name[MAX_EVT_NAME_LEN]; /* * Initialize pfm library (required before we can use it) */ if (pfm_initialize() != PFMLIB_SUCCESS) { fatal_error("Can't initialize library\n"); } /* * Let's make sure we run this on the right CPU */ pfm_get_pmu_type(&type); if (type != PFMLIB_ITANIUM2_PMU) { char model[MAX_PMU_NAME_LEN]; pfm_get_pmu_name(model, MAX_PMU_NAME_LEN); fatal_error("this program does not work with the %s PMU\n", model); } /* * pass options to library (optional) */ memset(&pfmlib_options, 0, sizeof(pfmlib_options)); pfmlib_options.pfm_debug = 0; /* set to 1 for debug */ pfmlib_options.pfm_verbose = 0; /* set to 1 for verbose */ pfm_set_options(&pfmlib_options); memset(pd, 0, sizeof(pd)); memset(pc, 0, sizeof(pc)); memset(ctx, 0, sizeof(ctx)); memset(&load_args, 0, sizeof(load_args)); memset(&inp,0, sizeof(inp)); memset(&outp,0, sizeof(outp)); memset(&ita2_inp,0, sizeof(ita2_inp)); /* * We indicate that we are using the PMC8 opcode matcher. This is required * otherwise the library add PMC8 to the list of PMC to pogram during * pfm_dispatch_events(). */ ita2_inp.pfp_ita2_pmc8.opcm_used = 1; /* * We want to match all the br.cloop in our test function. * This branch is an IP-relative branch for which the major * opcode (bits [40-37]=4) and the btype field is 5 (which represents * bits[6-8]) so it is included in the match/mask fields of PMC8. * It is necessarily in a B slot. * * We don't care which operands are used with br.cloop therefore * the mask field of pmc8 is set such that only the 4 bits of the * opcode and 3 bits of btype must match exactly. This is accomplished by * clearing the top 4 bits and bits [6-8] of the mask field and setting the * remaining bits. Similarly, the match field only has the opcode value and btype * set according to the encoding of br.cloop, the * remaining bits are zero. Bit 60 of PMC8 is set to indicate * that we look only in B slots (this is the only possibility for * this instruction anyway). * * So the binary representation of the value for PMC8 is as follows: * * 6666555555555544444444443333333333222222222211111111110000000000 * 3210987654321098765432109876543210987654321098765432109876543210 * ---------------------------------------------------------------- * 0001010000000000000000101000000000000011111111111111000111111000 * * which yields a value of 0x1400028003fff1f8. * * Depending on the level of optimization to compile this code, it may * be that the count reported could be zero, if the compiler uses a br.cond * instead of br.cloop. * * * The 0x1 sets the ig_ad field to make sure we ignore any range restriction. * Also bit 2 must always be set */ ita2_inp.pfp_ita2_pmc8.pmc_val = 0x1400028003fff1fa; /* * To count the number of occurence of this instruction, we must * program a counting monitor with the IA64_TAGGED_INST_RETIRED_PMC8 * event. */ if (pfm_find_full_event("IA64_TAGGED_INST_RETIRED_IBRP0_PMC8", &inp.pfp_events[0]) != PFMLIB_SUCCESS) { fatal_error("cannot find event IA64_TAGGED_INST_RETIRED_IBRP0_PMC8\n"); } /* * set the privilege mode: * PFM_PLM3 : user level only */ inp.pfp_dfl_plm = PFM_PLM3; /* * how many counters we use */ inp.pfp_event_count = 1; /* * let the library figure out the values for the PMCS */ if ((ret=pfm_dispatch_events(&inp, &ita2_inp, &outp, NULL)) != PFMLIB_SUCCESS) { fatal_error("cannot configure events: %s\n", pfm_strerror(ret)); } /* * now create the context for self monitoring/per-task */ if (perfmonctl(0, PFM_CREATE_CONTEXT, ctx, 1) == -1 ) { if (errno == ENOSYS) { fatal_error("Your kernel does not have performance monitoring support!\n"); } fatal_error("Can't create PFM context %s\n", strerror(errno)); } /* * extract the unique identifier for our context, a regular file descriptor */ id = ctx[0].ctx_fd; /* * Now prepare the argument to initialize the PMDs and PMCS. * We must pfp_pmc_count to determine the number of PMC to intialize. * We must use pfp_event_count to determine the number of PMD to initialize. * Some events causes extra PMCs to be used, so pfp_pmc_count may be >= pfp_event_count. * * This step is new compared to libpfm-2.x. It is necessary because the library no * longer knows about the kernel data structures. */ 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; } /* * the PMC controlling the event ALWAYS come first, that's why this loop * is safe even when extra PMC are needed to support a particular event. */ for (i=0; i < inp.pfp_event_count; i++) { pd[i].reg_num = pc[i].reg_num; } printf("event_count=%d id=%d\n", inp.pfp_event_count, id); /* * Now program the registers * * We don't use the save variable to indicate the number of elements passed to * the kernel because, as we said earlier, pc may contain more elements than * the number of events we specified, i.e., contains more thann coutning monitors. */ if (perfmonctl(id, PFM_WRITE_PMCS, pc, outp.pfp_pmc_count) == -1) { fatal_error("perfmonctl error PFM_WRITE_PMCS errno %d\n",errno); } if (perfmonctl(id, PFM_WRITE_PMDS, pd, inp.pfp_event_count) == -1) { fatal_error("perfmonctl error PFM_WRITE_PMDS errno %d\n",errno); } /* * now we load (i.e., attach) the context to ourself */ load_args.load_pid = getpid(); if (perfmonctl(id, PFM_LOAD_CONTEXT, &load_args, 1) == -1) { fatal_error("perfmonctl error PFM_LOAD_CONTEXT errno %d\n",errno); } /* * Let's roll now. */ pfm_self_start(id); do_test(100UL); pfm_self_stop(id); /* * now read the results */ if (perfmonctl(id, PFM_READ_PMDS, pd, inp.pfp_event_count) == -1) { fatal_error("perfmonctl error READ_PMDS errno %d\n",errno); } /* * print the results */ pfm_get_full_event_name(&inp.pfp_events[0], name, MAX_EVT_NAME_LEN); printf("PMD%u %20lu %s\n", pd[0].reg_num, pd[0].reg_value, name); if (pd[0].reg_value != 0) printf("compiler used br.cloop\n"); else printf("compiler did not use br.cloop\n"); /* * let's stop this now */ close(id); return 0; }
int main(int argc, char **argv) { pfmlib_input_param_t inp; pfmlib_output_param_t outp; pfmlib_core_input_param_t mod_inp; pfmlib_options_t pfmlib_options; pfarg_pmr_t pc[NUM_PMCS]; pfarg_pmd_attr_t pd[NUM_PMDS]; pfarg_sinfo_t sif; struct pollfd fds; smpl_arg_t buf_arg; pfarg_msg_t msg; smpl_hdr_t *hdr; void *buf_addr; uint64_t pebs_size; pid_t pid; int ret, fd, type; unsigned int i; uint32_t ctx_flags; if (argc < 2) fatal_error("you need to pass a program to sample\n"); if (pfm_initialize() != PFMLIB_SUCCESS) fatal_error("libpfm intialization failed\n"); /* * check we are on an Intel Core PMU */ pfm_get_pmu_type(&type); if (type != PFMLIB_INTEL_CORE_PMU && type != PFMLIB_INTEL_ATOM_PMU) fatal_error("This program only works with an Intel Core processor\n"); /* * pass options to library (optional) */ memset(&pfmlib_options, 0, sizeof(pfmlib_options)); pfmlib_options.pfm_debug = 0; /* set to 1 for debug */ pfmlib_options.pfm_verbose = 1; /* set to 1 for verbose */ pfm_set_options(&pfmlib_options); memset(pd, 0, sizeof(pd)); memset(pc, 0, sizeof(pc)); memset(&inp, 0, sizeof(inp)); memset(&outp, 0, sizeof(outp)); memset(&mod_inp, 0, sizeof(mod_inp)); memset(&sif, 0, sizeof(sif)); memset(&buf_arg, 0, sizeof(buf_arg)); memset(&fds, 0, sizeof(fds)); /* * search for our sampling event */ if (pfm_find_full_event(SMPL_EVENT, &inp.pfp_events[0]) != PFMLIB_SUCCESS) fatal_error("cannot find sampling event %s\n", SMPL_EVENT); inp.pfp_event_count = 1; inp.pfp_dfl_plm = PFM_PLM3; /* * important: inform libpfm we do use PEBS */ mod_inp.pfp_core_pebs.pebs_used = 1; /* * sampling buffer parameters */ pebs_size = 3 * getpagesize(); buf_arg.buf_size = pebs_size; /* * sampling period cannot use more bits than HW counter can supoprt */ buf_arg.cnt_reset = -SMPL_PERIOD; /* * We want a system-wide context for sampling */ ctx_flags = PFM_FL_SYSTEM_WIDE | PFM_FL_SMPL_FMT; /* * trigger notification (interrupt) when reaching the very end of * the buffer */ buf_arg.intr_thres = (pebs_size/sizeof(smpl_entry_t))*90/100; /* * we want to measure CPU0, thus we pin ourself to the CPU before invoking * perfmon. This ensures that the sampling buffer will be allocated on the * same NUMA node. */ ret = pin_cpu(getpid(), 0); if (ret) fatal_error("cannot pin on CPU0"); /* * create session and sampling buffer */ fd = pfm_create(ctx_flags, &sif, FMT_NAME, &buf_arg, sizeof(buf_arg)); if (fd == -1) { if (errno == ENOSYS) { fatal_error("Your kernel does not have performance monitoring support!\n"); } fatal_error("cannot create session %s, maybe you do not have the PEBS sampling format in the kernel.\nCheck /sys/kernel/perfmon/formats\n", strerror(errno)); } /* * map buffer into our address space */ buf_addr = mmap(NULL, (size_t)buf_arg.buf_size, PROT_READ, MAP_PRIVATE, fd, 0); printf("session [%d] buffer mapped @%p\n", fd, buf_addr); if (buf_addr == MAP_FAILED) fatal_error("cannot mmap sampling buffer errno %d\n", errno); hdr = (smpl_hdr_t *)buf_addr; printf("pebs_base=0x%llx pebs_end=0x%llx index=0x%llx\n" "intr=0x%llx version=%u.%u\n" "entry_size=%zu ds_size=%zu\n", (unsigned long long)hdr->ds.pebs_buf_base, (unsigned long long)hdr->ds.pebs_abs_max, (unsigned long long)hdr->ds.pebs_index, (unsigned long long)hdr->ds.pebs_intr_thres, PFM_VERSION_MAJOR(hdr->version), PFM_VERSION_MINOR(hdr->version), sizeof(smpl_entry_t), sizeof(hdr->ds)); if (PFM_VERSION_MAJOR(hdr->version) < 1) fatal_error("invalid buffer format version\n"); /* * get which PMC registers are available */ detect_unavail_pmu_regs(&sif, &inp.pfp_unavail_pmcs, NULL); /* * let libpfm figure out how to assign event onto PMU registers */ if (pfm_dispatch_events(&inp, &mod_inp, &outp, NULL) != PFMLIB_SUCCESS) fatal_error("cannot assign event %s\n", SMPL_EVENT); /* * propagate PMC setup from libpfm to perfmon */ 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; /* * must disable 64-bit emulation on the PMC0 counter. * PMC0 is the only counter useable with PEBS. We must disable * 64-bit emulation to avoid getting interrupts for each * sampling period, PEBS takes care of this part. */ if (pc[i].reg_num == 0) pc[i].reg_flags = PFM_REGFL_NO_EMUL64; } /* * propagate PMD set from libpfm to perfmon */ for (i=0; i < outp.pfp_pmd_count; i++) pd[i].reg_num = outp.pfp_pmds[i].reg_num; /* * setup sampling period for first counter * we want notification on overflow, i.e., when buffer is full */ pd[0].reg_flags = PFM_REGFL_OVFL_NOTIFY; pd[0].reg_value = -SMPL_PERIOD; pd[0].reg_long_reset = -SMPL_PERIOD; pd[0].reg_short_reset = -SMPL_PERIOD; /* * Now program the registers */ if (pfm_write(fd, 0, PFM_RW_PMC, pc, outp.pfp_pmc_count * sizeof(*pc)) == -1) fatal_error("pfm_write error errno %d\n",errno); if (pfm_write(fd, 0, PFM_RW_PMD_ATTR, pd, outp.pfp_pmd_count * sizeof(*pd)) == -1) fatal_error("pfm_write(PMD) error errno %d\n",errno); /* * attach the session to CPU0 */ if (pfm_attach(fd, 0, 0) == -1) fatal_error("pfm_attach error errno %d\n",errno); /* * Create the child task */ signal(SIGCHLD, handler); if ((pid=fork()) == -1) fatal_error("Cannot fork process\n"); if (pid == 0) { /* child does not inherit context file descriptor */ close(fd); /* if child is too short-lived we may not measure it */ child(argv+1); } /* * start monitoring */ if (pfm_set_state(fd, 0, PFM_ST_START) == -1) fatal_error("pfm_set_state(start) error errno %d\n",errno); fds.fd = fd; fds.events = POLLIN; /* * core loop */ for(;done == 0;) { /* * Must use a timeout to avoid a race condition * with the SIGCHLD signal */ ret = poll(&fds, 1, 500); /* * if timeout expired, then check done */ if (ret == 0) continue; if (ret == -1) { if(ret == -1 && errno == EINTR) { warning("read interrupted, retrying\n"); continue; } fatal_error("poll failed: %s\n", strerror(errno)); } ret = read(fd, &msg, sizeof(msg)); if (ret == -1) fatal_error("cannot read perfmon msg: %s\n", strerror(errno)); switch(msg.type) { case PFM_MSG_OVFL: /* the sampling buffer is full */ process_smpl_buf(hdr); /* * reactivate monitoring once we are done with the samples * in syste-wide, interface guarantees monitoring is active * upon return from the pfm_restart() syscall */ if (pfm_set_state(fd, 0, PFM_ST_RESTART) == -1) fatal_error("pfm_set_state(restart) error errno %d\n",errno); break; default: fatal_error("unknown message type %d\n", msg.type); } } /* * cleanup child */ waitpid(pid, NULL, 0); /* * stop monitoring, this is required in order to guarantee that the PEBS buffer * header is updated with the latest position, such that we see see the final * samples */ if (pfm_set_state(fd, 0, PFM_ST_STOP) == -1) fatal_error("pfm_set_state(stop) error errno %d\n",errno); /* * check for any leftover samples. Must have monitoring stopped * for this operation to have guarantee it is up to date */ process_smpl_buf(hdr); /* * close session */ close(fd); /* * unmap sampling buffer and actually free the perfmon session */ munmap(buf_addr, (size_t)buf_arg.buf_size); return 0; }