/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* bos720 src/bos/usr/sbin/perf/pmapi/pmtoolkit/tcount.c 1.28.1.3 */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* Restricted Materials of IBM */ /* */ /* COPYRIGHT International Business Machines Corp. 1999,2015 */ /* All Rights Reserved */ /* */ /* US Government Users Restricted Rights - Use, duplication or */ /* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* IBM_PROLOG_END_TAG */ static char sccsid[] = "@(#)76 1.28.1.3 src/bos/usr/sbin/perf/pmapi/pmtoolkit/tcount.c, pmapi, bos720, 1511A_720 3/3/15 02:46:53"; /* * COMPONENT_NAME: PMAPI * * FUNCTIONS: filter_to_numeric * main * pm_usage * print_data * print_data_mx * print_events * print_group * print_mode * print_timestamp * process_args * run_workload * * ORIGINS: 27 * * -- ( when * combined with the aggregated modules for this product) * OBJECT CODE ONLY SOURCE MATERIALS * * (C) COPYRIGHT International Business Machines Corp. 1999, 2001. * All Rights Reserved * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* Description: * This is a sample program using the system-level API. * It accepts event numbers, counting flags, and a workload to execute. * It accepts time slice mode for groups counting. */ #include #include #include #include #include #include #include #include #include #include "pmapi.h" #define False 0 #define True 1 #define ERROR_CODE -1 #define OK_CODE 0 #define OPTIONSTR "abe:f:g:hHkmMnprs:t:Tu@:" #define PM_DEBUG 0x40000000 /* cut-n-pasted from pminternal.h */ #define MAXGRP 512 #define EPM_LAST 17 #define EPM_BAD_CPU (EPM_LAST+38) /* Should be in sync with pm_internal.h, this can't be included */ #define PM_BROKEN 16 pm_info2_t Myinfo; pm_groups_info_t My_group_info; int ProcIndex; double Cycles; pm_mode_t Default_mode; int Wpar_all = 0; int Process_tree = 0; int Bind_cpu = 0; int Group_counting = 0; int Timestamping = 0; int valid_count_tbl[MAX_COUNTERS]; int Time_slice_mode = 0; int Nb_slice = 0; int threadapi = 0; char *WparName = NULL; cid_t cid; pm_prog_t Setprog; pm_prog_t Getprog; pm_prog_mm_t Setprog_mm; pm_prog_mm_t Getprog_mm; timebasestruct_t start_time; extern int errno; void print_group(int, pm_mode_t); void print_events(int *, pm_mode_t); void print_mode(pm_mode_t); void pm_usage(char *); /* Function to convert filter of character form (entered from command line, * e.g. -t v,u) to a numeric form. */ int filter_to_numeric(char char_filter, int *filter) { switch (char_filter) { case 'v' : *filter |= PM_VERIFIED; break; case 'u' : *filter |= PM_UNVERIFIED; break; case 'c' : *filter |= PM_CAVEAT; break; case 'd' : *filter |= PM_DEBUG; break; default : printf("Invalid filter %c. Valid filters are v, u, c\n", char_filter); exit(ERROR_CODE); } return(OK_CODE); } char * parse_opt(char *progname, char *opt, pm_mode_t *mode) { unsigned long thresh_value; char *end; mode->w = 0; while (*opt != '\0' && *opt != ',') { switch (*opt) { case 'h': mode->b.hypervisor = 1; break; case 'k': mode->b.kernel = 1; break; case 'm': if (mode->b.thresh_hres) { fprintf(stderr, "Can't specify both 'm' and 'M' counting modes\n"); exit(ERROR_CODE); } mode->b.thresh_res = 1; break; case 'M': if (mode->b.thresh_res) { fprintf(stderr, "Can't specify both 'm' and 'M' counting modes\n"); exit(ERROR_CODE); } mode->b.thresh_hres = 1; break; case 'n': mode->b.nointerrupt = 1; break; case 'r': mode->b.runlatch = 1; break; case 't': thresh_value = strtoul(opt + 1, &end, 10); /* check validity */ if (thresh_value < MIN_THRESH_VALUE || thresh_value > MAX_THRESH_VALUE) { fprintf(stderr, "Invalid threshold value %lu\n", thresh_value); exit(ERROR_CODE); /* invalid threshold */ } mode->b.threshold = thresh_value; opt = end - 1; break; case 'u': mode->b.user = 1; break; default: fprintf(stderr, "Invalid counting mode '%c'\n", *opt); exit(ERROR_CODE); } opt++; } return opt; } void parse_groups(char *progname, char *opt) { pm_prog_t *prog; unsigned long grp; char *end; Setprog_mm.prog_set = calloc(MAXGRP, sizeof (pm_prog_t)); if (Setprog_mm.prog_set == NULL) { fprintf(stderr, "group list allocation failure\n"); exit(ERROR_CODE); } Nb_slice = 0; for (prog = Setprog_mm.prog_set; ; prog++) { grp = strtoul(opt, &end, 10); opt = end; Setprog.events[0] = prog->events[0] = grp; Nb_slice++; if (*opt == ':') { opt = parse_opt(progname, opt + 1, &prog->mode); Setprog.mode.w = prog->mode.w; } else { /* no ':' given, will use command line options */ Setprog.mode.w = prog->mode.w = 0; } if (*opt == '\0') break; if (*opt != ',') { printf("error\n"); exit(ERROR_CODE); } opt++; /* skip ',' */ } if (Nb_slice > 1) Time_slice_mode = 1; Setprog_mm.nb_set_prog = Nb_slice; } void parse_events(char *progname, char *opt) { static pm_prog_t *prog; char *end; unsigned long evt, thresh_value; int i; if (Setprog_mm.prog_set == NULL) { Setprog_mm.prog_set = calloc(MAXGRP, sizeof (pm_prog_t)); if (Setprog_mm.prog_set == NULL) { fprintf(stderr, "group list allocation failure\n"); exit(ERROR_CODE); } prog = Setprog_mm.prog_set; } for (i = 0; i < MAX_COUNTERS; i++) { evt = strtoul(opt, &end, 10); opt = end; Setprog.events[i] = prog->events[i] = evt; if (*opt != ',') { /* pad with COUNT_NOTHING */ for (++i; i < MAX_COUNTERS; i++) { Setprog.events[i] = prog->events[i] = COUNT_NOTHING; } break; } opt++; /* skip ',' */ } if (*opt == ':') { opt = parse_opt(progname, opt + 1, &prog->mode); Setprog.mode.w = prog->mode.w; } else { /* no ':' given, will use command line options */ Setprog.mode.w = prog->mode.w = 0; } /* one more slice.. */ prog++; Nb_slice++; if (Nb_slice > 1) Time_slice_mode = 1; Setprog_mm.nb_set_prog = Nb_slice; } /* Process command line arguments */ void process_args(int argc, char **argv, char ***execvector, int *filter, int *threadapi) { extern char *optarg; extern int optind; int c, opt, event_num; int mflag = 0; int Mflag = 0; int thresh_value = 0; int ev_spec = False; char *index, *groups; int i; /* init the mode */ Default_mode.w = 0; /* init all counting events to COUNT_NOTHING */ for (c = 0; c < MAX_COUNTERS; c++) Setprog.events[c] = COUNT_NOTHING; Setprog_mm.prog_set = NULL; Setprog_mm.slice_duration = 0; while ((opt = getopt(argc, argv, OPTIONSTR)) != EOF) { switch(opt) { case 'a': *threadapi = 1; break; case 'b': Bind_cpu = 1; break; case 'f': /* filter */ /* reset filter if one is specified */ *filter = 0; /* convert filter from character to numeric */ filter_to_numeric(*(optarg), filter); /* next filter */ index = strchr(optarg, ','); for (i = 1; i < PM_MAX_FILTER; i++) { if (index == NULL) break; filter_to_numeric(index[1], filter); index = strchr(index+1, ','); } break; case 'k': /* count in kernel mode */ Default_mode.b.kernel = 1; break; case 'u': /* count in user mode */ Default_mode.b.user = 1; break; case 'h': /* count in hypervisor mode */ Default_mode.b.hypervisor = 1; break; case 'p': /* process tree */ Process_tree = 1; break; case 'r': /* use runlatch */ Default_mode.b.runlatch = 1; break; case 'n': /* count in nointerrupt mode */ Default_mode.b.nointerrupt = 1; break; case 's': /* slice duration */ Setprog_mm.slice_duration = strtoul(optarg, (char **)NULL, 10); break; case 't': /* threshold value */ thresh_value = strtoul(optarg, (char**)NULL, 10); /* check validity */ if (thresh_value < MIN_THRESH_VALUE || thresh_value > MAX_THRESH_VALUE) { fprintf(stderr, "Invalid threshold value %lu\n", thresh_value); exit(ERROR_CODE); /* invalid threshold */ } Default_mode.b.threshold = thresh_value; break; case 'm': if (Mflag) { fprintf(stderr, "Can't specify both 'm' and 'M' counting modes\n"); exit(ERROR_CODE); } mflag = 1; Default_mode.b.thresh_res = 1; break; case 'M': if (mflag) { fprintf(stderr, "Can't specify both 'm' and 'M' counting modes\n"); exit(ERROR_CODE); } Mflag = 1; Default_mode.b.thresh_hres = 1; break; case 'e': /* count event */ parse_events(argv[0], optarg); /* an event has been specified */ ev_spec = True; /* Not counting via event group */ Group_counting = 0; break; case 'g': /* count event group */ /* a group has been specified */ ev_spec = True; /* Turn on flag */ Group_counting = 1; groups = optarg; break; case 'T': /* turn on timestamped calls */ Timestamping = 1; break; case '@': /* count specified WPAR */ if (strcmp(optarg, "ALL") == 0) Wpar_all = 1; else WparName = optarg; break; case 'H': default: pm_usage(argv[0]); exit(OK_CODE); } /* switch (opt) */ } /* while (opt = ...) */ if (Bind_cpu && (WparName != NULL || Wpar_all)) { fprintf(stderr, "Options -b and -@ cannot be used together\n"); exit(ERROR_CODE); } if (WparName != NULL) { if (getcorralid(WparName, &cid) != 0) { fprintf(stderr, "Unknown WPAR name '%s'\n", WparName); exit(ERROR_CODE); } } if (!ev_spec) { fprintf(stderr, "Need to specify an event to count\n"); exit(ERROR_CODE); } if (Group_counting) parse_groups(argv[0], groups); *execvector = &argv[optind]; /* Check if a workload is specified */ if (**execvector == NULL) { fprintf(stderr, "Need to specify a workload to execute\n"); exit(ERROR_CODE); } } /* process_args() */ /* * */ void print_timestamp(timebasestruct_t *start_time, timebasestruct_t *end_time) { int rc, secs, nsecs; /* convert times */ if ((rc = time_base_to_time(end_time, TIMEBASE_SZ)) < 0) { fprintf(stderr,"time_base_to_time(end_time) failed : rc = %d\n", rc); return; } if ((rc = time_base_to_time(start_time, TIMEBASE_SZ)) < 0) { fprintf(stderr,"time_base_to_time(start_time) failed : rc = %d\n", rc); return; } /* calculate deltas */ secs = end_time->tb_high - start_time->tb_high; nsecs = end_time->tb_low - start_time->tb_low; /* undo any carry over */ if (nsecs < 0) { secs--; nsecs += 1000000000; } printf("\nDelta time = %u sec, %u ns\n", secs, nsecs); } /* * */ int cmp_wpars(const void *v1, const void *v2) { const pm_wpar_ctx_info_t *w1 = v1; const pm_wpar_ctx_info_t *w2 = v2; if (strcmp(w1->name, "Global") == 0) return -1; if (strcmp(w2->name, "Global") == 0) return +1; return(strcmp(w1->name, w2->name)); } /* * */ void print_data(timebasestruct_t *start_time, int threadapi) { int i, w, rc, len, cpuid, secs, nsecs, nwpars; int ncpus = _system_configuration.ncpus; int warn = 0; char str[256]; pm_data_t data; pm_wpar_ctx_info_t *info; timebasestruct_t end_time; long long totcounts[MAX_COUNTERS]; if (threadapi) { if ((rc = pm_get_tdata_mygroup(&data, &end_time)) != OK_CODE) { pm_error("pm_get_data_mygroup", rc); exit(ERROR_CODE); } printf("\n*** Results :\n"); str[0] = '\0'; for (i=0; i 1) { for (w = 0; w < nwpars; w++) { for (i = 0; i < MAX_COUNTERS; i++) totcounts[i] = 0; if (Wpar_all) printf("\n*** Results @%s :\n", info[w].name); else printf("\n*** Results :\n"); str[0] = '\0'; printf("CPU "); sprintf(str,"%s","==== "); for (i=0; i (NS_PER_SEC - 1)) { \ (rvp).tb_low -= NS_PER_SEC; \ (rvp).tb_high++; \ } \ } while (0) /* * */ int print_data_mx(timebasestruct_t *start_time, int threadapi) { int i, w, grp, nb, rc, len, cpuid, secs, nsecs, nwpars; int ncpus = _system_configuration.ncpus; int warn = 0; char str[256]; pm_data_mx_t *data_mx; pm_wpar_ctx_info_t *info; timebasestruct_t end_time; pm_accu_mx_t *totcount_mx; long long totround = 0; nb = threadapi ? 1 : _system_configuration.max_ncpus; data_mx = malloc(nb * sizeof(pm_data_mx_t)); if (data_mx == NULL) { fprintf(stderr, "data accumulators allocation failure\n"); exit(ERROR_CODE); } totcount_mx = malloc(Getprog_mm.nb_set_prog * sizeof(pm_accu_mx_t)); if (totcount_mx == NULL) { fprintf(stderr, "global accumulators allocation failure\n"); exit(ERROR_CODE); } if (threadapi) { /* get data */ if ((rc = pm_get_tdata_mygroup_mx(data_mx, &end_time)) != OK_CODE) { pm_error("pm_get_tdata_mygroup_mx", rc); exit(ERROR_CODE); } /* */ printf("\n*** Results :\n"); printf("\nNumber of rounds: %d\n", data_mx->nb_mx_round); /* print results for each slice */ for (grp = 0; grp < Getprog_mm.nb_set_prog; grp++) { print_mode(Getprog_mm.prog_set[grp].mode); if (Getprog_mm.prog_set[0].mode.b.is_group) { print_group(Getprog_mm.prog_set[grp].events[0], Getprog_mm.prog_set[grp].mode); } else { print_events(Getprog_mm.prog_set[grp].events, Getprog_mm.prog_set[grp].mode); } time_base_to_time(&data_mx->accu_set[grp].accu_time, TIMEBASE_SZ); time_base_to_time(&data_mx->accu_set[grp].accu_purr, TIMEBASE_SZ); time_base_to_time(&data_mx->accu_set[grp].accu_spurr, TIMEBASE_SZ); printf("accumulated time(TB/PURR/SPURR) %3d.%03d/%3d.%03d/%3d.%03d\n", data_mx->accu_set[grp].accu_time.tb_high, data_mx->accu_set[grp].accu_time.tb_low / 1000000, data_mx->accu_set[grp].accu_purr.tb_high, data_mx->accu_set[grp].accu_purr.tb_low / 1000000, data_mx->accu_set[grp].accu_spurr.tb_high, data_mx->accu_set[grp].accu_spurr.tb_low / 1000000); str[0] = '\0'; for (i=0; iaccu_set[grp].accu_data[i]); } else { warn = 1; printf(" -------- "); } } printf("\n"); } free(data_mx->accu_set); } else { /* !threadapi */ if (WparName != NULL) { nwpars = 1; info = malloc(nwpars * sizeof (pm_wpar_ctx_info_t)); if (info == NULL) { fprintf(stderr, "WPARs info allocation failure\n"); exit(ERROR_CODE); } if ((rc = pm_get_wplist(WparName, info, &nwpars)) != OK_CODE) { pm_error("pm_get_wplist", rc); exit(ERROR_CODE); } } else if (Wpar_all) { nwpars = 0; /* retrieve the number of counted WPARs */ (void)pm_get_wplist(NULL, NULL, &nwpars); /* allocate an array to store the list of counted WPARs */ info = malloc(nwpars * sizeof (pm_wpar_ctx_info_t)); if (info == NULL) { fprintf(stderr, "WPARs info allocation failure\n"); exit(ERROR_CODE); } /* retrieve the list of counted WPARs */ if ((rc = pm_get_wplist(NULL, info, &nwpars)) != OK_CODE) { pm_error("pm_get_wplist", rc); exit(ERROR_CODE); } /* sort the WPAR list by name (with "Global" on top) */ qsort(info, nwpars, sizeof (pm_wpar_ctx_info_t), cmp_wpars); } else nwpars = 1; if (ncpus > 1) { for (w = 0; w < nwpars; w++) { /* get data */ if (Bind_cpu) { /* Bind_cpu not supported w/ -@ option */ for (cpuid = 0; cpuid < ncpus; cpuid++) { if ((rc = pm_get_tdata_cpu_mx(cpuid, &data_mx[cpuid], &end_time)) != OK_CODE) { pm_error("pm_get_tdata_cpu_mx", rc); exit(ERROR_CODE); } } } else { /* not Bind_cpu number */ for (cpuid = 0; cpuid < _system_configuration.max_ncpus; cpuid++) { if (WparName != NULL || Wpar_all) { data_mx[cpuid].nb_accu_mx = Getprog_mm.nb_set_prog; if ((rc = pm_get_tdata_lcpu_wp_mx(info[w].hwpar, cpuid, &data_mx[cpuid], &end_time)) != OK_CODE) { if (rc == EPM_BAD_CPU) { data_mx[cpuid].accu_set = NULL; continue; } pm_error("pm_get_tdata_lcpu_wp_mx", rc); exit(ERROR_CODE); } } else { if ((rc = pm_get_tdata_lcpu_mx(cpuid, &data_mx[cpuid], &end_time)) != OK_CODE) { if (rc == EPM_BAD_CPU) { data_mx[cpuid].accu_set = NULL; continue; } pm_error("pm_get_tdata_lcpu_mx", rc); exit(ERROR_CODE); } } } } /* clear total */ for (grp = 0; grp < Getprog_mm.nb_set_prog; grp++) { totcount_mx[grp].accu_time.tb_high = 0; totcount_mx[grp].accu_time.tb_low = 0; totcount_mx[grp].accu_time.flag = RTC_POWER_PC; totcount_mx[grp].accu_purr.tb_high = 0; totcount_mx[grp].accu_purr.tb_low = 0; totcount_mx[grp].accu_purr.flag = RTC_POWER_PC; totcount_mx[grp].accu_spurr.tb_high = 0; totcount_mx[grp].accu_spurr.tb_low = 0; totcount_mx[grp].accu_spurr.flag = RTC_POWER_PC; for (i = 0; i < MAX_COUNTERS; i++) totcount_mx[grp].accu_data[i] = 0; } if (Wpar_all) printf("\n*** Results @%s :\n", info[w].name); else printf("\n*** Results :\n"); for (grp = 0; grp < Getprog_mm.nb_set_prog; grp++) { print_mode(Getprog_mm.prog_set[grp].mode); if (Getprog_mm.prog_set[0].mode.b.is_group) { print_group(Getprog_mm.prog_set[grp].events[0], Getprog_mm.prog_set[grp].mode); } else { print_events(Getprog_mm.prog_set[grp].events, Getprog_mm.prog_set[grp].mode); } str[0] = '\0'; printf("CPU "); sprintf(str,"%s","==== "); for (i = 0; i < Myinfo.maxpmcs; i++) { printf(" PMC%2d ", i+1); len = strlen(str); str[len] = ' '; sprintf(str+len,"%s","============ "); } printf(" TB/PURR/SPURR NB ROUNDS"); len = strlen(str); str[len] = ' '; sprintf(str+len,"%s","========================= ========="); printf("\n%s\n", str); totround = 0; for (cpuid = 0; cpuid < _system_configuration.max_ncpus; cpuid++) { if (data_mx[cpuid].accu_set == NULL) { /* no count for this entry ==> skip it */ continue; } printf("[%2d] ",cpuid); for (i = 0; i < Myinfo.maxpmcs; i++) { if (valid_count_tbl[i]) { printf("%-12lld ", data_mx[cpuid].accu_set[grp].accu_data[i]); totcount_mx[grp].accu_data[i] += data_mx[cpuid].accu_set[grp].accu_data[i]; } else { warn = 1; printf(" -------- "); } } time_base_to_time(&data_mx[cpuid].accu_set[grp].accu_time, TIMEBASE_SZ); time_base_to_time(&data_mx[cpuid].accu_set[grp].accu_purr, TIMEBASE_SZ); time_base_to_time(&data_mx[cpuid].accu_set[grp].accu_spurr, TIMEBASE_SZ); printf("%3d.%03d/%3d.%03d/%3d.%03d %d", data_mx[cpuid].accu_set[grp].accu_time.tb_high, data_mx[cpuid].accu_set[grp].accu_time.tb_low / 1000000, data_mx[cpuid].accu_set[grp].accu_purr.tb_high, data_mx[cpuid].accu_set[grp].accu_purr.tb_low / 1000000, data_mx[cpuid].accu_set[grp].accu_spurr.tb_high, data_mx[cpuid].accu_set[grp].accu_spurr.tb_low / 1000000, data_mx[cpuid].nb_mx_round); timeradd(data_mx[cpuid].accu_set[grp].accu_time, totcount_mx[grp].accu_time, totcount_mx[grp].accu_time); timeradd(data_mx[cpuid].accu_set[grp].accu_purr, totcount_mx[grp].accu_purr, totcount_mx[grp].accu_purr); timeradd(data_mx[cpuid].accu_set[grp].accu_spurr, totcount_mx[grp].accu_spurr, totcount_mx[grp].accu_spurr); totround += data_mx[cpuid].nb_mx_round; printf("\n"); } printf("%s\n", str); printf("ALL "); for (i = 0; i < Myinfo.maxpmcs; i++) { if (valid_count_tbl[i]) { printf("%-12lld ", totcount_mx[grp].accu_data[i]); } else { warn = 1; printf(" -------- "); } } printf("%3d.%03d/%3d.%03d/%3d.%03d %lld", totcount_mx[grp].accu_time.tb_high, totcount_mx[grp].accu_time.tb_low / 1000000, totcount_mx[grp].accu_purr.tb_high, totcount_mx[grp].accu_purr.tb_low / 1000000, totcount_mx[grp].accu_spurr.tb_high, totcount_mx[grp].accu_spurr.tb_low / 1000000, totround); printf("\n"); } free(data_mx[cpuid].accu_set); } } else { /* ncpus == 1 */ for (w = 0; w < nwpars; w++) { if (WparName != NULL || Wpar_all) { data_mx[0].nb_accu_mx = Getprog_mm.nb_set_prog; if ((rc = pm_get_tdata_wp_mx(info[w].hwpar, &data_mx[0], &end_time)) != OK_CODE) { pm_error("pm_get_tdata_wp_mx", rc); exit(ERROR_CODE); } } else { if ((rc = pm_get_tdata_mx(&data_mx[0], &end_time)) != OK_CODE) { pm_error("pm_get_tdata_mx", rc); exit(ERROR_CODE); } } if (Wpar_all) printf("\n*** Results @%s :\n", info[w].name); else printf("\n*** Results :\n"); printf("\nnumber of rounds: %d\n", data_mx->nb_mx_round); /* print results for each slice */ for (grp = 0; grp < Getprog_mm.nb_set_prog; grp++) { if (Getprog_mm.prog_set[0].mode.b.is_group) { print_group(Getprog_mm.prog_set[grp].events[0], Getprog_mm.prog_set[grp].mode); } else { print_events(Getprog_mm.prog_set[grp].events, Getprog_mm.prog_set[grp].mode); } time_base_to_time(&data_mx->accu_set[grp].accu_time, TIMEBASE_SZ); time_base_to_time(&data_mx->accu_set[grp].accu_purr, TIMEBASE_SZ); time_base_to_time(&data_mx->accu_set[grp].accu_spurr, TIMEBASE_SZ); printf("acumulated time(TB/PURR/SPURR) %3d.%03d/%3d.%03d/%3d.%03d\n", data_mx->accu_set[grp].accu_time.tb_high, data_mx->accu_set[grp].accu_time.tb_low / 1000000, data_mx->accu_set[grp].accu_purr.tb_high, data_mx->accu_set[grp].accu_purr.tb_low / 1000000, data_mx->accu_set[grp].accu_spurr.tb_high, data_mx->accu_set[grp].accu_spurr.tb_low / 1000000); str[0] = '\0'; for (i=0; iaccu_set[grp].accu_data[i]); } else { warn = 1; printf(" -------- "); } } printf("\n"); } free(data_mx->accu_set); } } } /* threadapi or sysapi */ if (warn) printf ("\n--- pmc5 and pmc6 counters are not usable in the current counting mode ---\n"); if (Timestamping) print_timestamp(start_time, &end_time); } void print_mode(pm_mode_t mode) { int thresh_value; int need_sep = 0; printf("Mode = "); if (mode.b.user) { if (need_sep) printf(","); printf("user"); need_sep = 1; } if (mode.b.kernel) { if (need_sep) printf(","); printf("kernel"); need_sep = 1; } if (mode.b.hypervisor) { if (need_sep) printf(","); printf("hypervisor"); need_sep = 1; } if (mode.b.runlatch) { if (need_sep) printf(","); printf("runlatch"); need_sep = 1; } if (mode.b.nointerrupt) { if (need_sep) printf(","); printf("nointerrupt"); need_sep = 1; } printf("; Thresholding = "); if (mode.b.threshold > 0) { if (mode.b.thresh_res) thresh_value = Myinfo.hthresholdmult; else if (mode.b.thresh_hres) thresh_value = Myinfo.Hthresholdmult; else thresh_value = Myinfo.thresholdmult; printf("%d cycles (%d x %d)\n", mode.b.threshold * thresh_value, mode.b.threshold, thresh_value); } else printf("off\n"); } /* print_mode() */ void print_config(pm_mode_t mode) { printf("*** Configuration :\n"); if (mode.b.is_group) printf ("Event Group is specified; "); printf("Process tree = "); if (mode.b.proctree) printf("on\n"); else printf("off\n"); } void print_group(int evid, pm_mode_t mode) { int i; pm_events2_t *evp; pm_groups_t *grp = NULL; /* Find pointer to the group */ for (i = 0; i < My_group_info.maxgroups; i++) { grp = My_group_info.event_groups+i; if (evid == grp->group_id) break; grp = NULL; } if(grp != NULL) { /* Print the group name */ printf("Group %2d: %s\n", evid, grp->short_name); /* Next print the events in that group. */ print_events(grp->events, mode); } else { printf("Error in identifing groups \n"); } } /* print_group () */ void print_events(int *ev_list, pm_mode_t mode) { /* for each event (pmc), print short name, accu */ int pmcid; /* which pmc */ int evid; /* event id */ pm_events2_t *evp; char str[100]; int len; int i; /* go through evs, get sname from table of events, print it */ for (pmcid = 0; pmcid < Myinfo.maxpmcs; pmcid++) { printf("Counter %2d, ", pmcid+1); valid_count_tbl[pmcid] = 1; /* get the event id from the list */ evid = ev_list[pmcid]; if ((evid == COUNT_NOTHING) || (Myinfo.maxevents[pmcid] == 0)) printf("event %2d: No event\n", evid); else { /* find pointer to the event */ for (i = 0; i < Myinfo.maxevents[pmcid]; i++) { evp = Myinfo.list_events[pmcid]+i; if (evid == evp->event_id) { break; } } printf("event %2d: %s", evid, evp->short_name); if (evp->status.b.shared) printf(" [shared core]"); else if (evp->status.b.sharedchip) printf(" [shared chip]"); printf("\n"); if (evp->status.b.support_mode && (!mode.b.user || !mode.b.kernel || !mode.b.hypervisor)) valid_count_tbl[pmcid] = 0; } } /* for (pmcid = 0; ... */ } /* print_events () */ pid_t run_workload(char **execvector) { pid_t pid; if ((pid = fork()) == 0) { if (execvp(execvector[0], execvector) < 0) { fprintf(stderr, "execvp error: %s, %d\n", execvector[0], errno); exit(-1); } } else return(pid); } void pm_usage(char *name) { printf("usage: %s -H\n", name); printf(" %s [-h] [-a] [-k] [-u] [-p] [-r] [-m|-M] [-n] [-t value] \n", name); printf(" [-f filter] [-e event{,event}...] [-g group-id{,group-id}] [-T] workload\n\n"); printf("where: -H this help screen\n"); printf(" -a count in thread-level mode\n"); printf(" -b use the bind cpu to display values\n"); printf(" -h count in hypervisor mode\n"); printf(" -k count in kernel mode\n"); printf(" -u count in user mode\n"); printf(" -p count in process tree mode (default is global)\n"); printf(" -r count in runlatch mode\n"); printf(" -s slice duration in milliseconds\n"); printf(" -t value threshold value\n"); printf(" -m use upper threshold multiplier, if available\n"); printf(" -M use third threshold multiplier, if available\n"); printf(" -n count in nointerrupt mode\n"); printf(" -f v,u,c event filter (default is v,u,c)\n"); printf(" -e event,event comma-separated list of events to count\n"); printf(" (one occurence for each time-slice)\n"); printf(" -g group-id,group-id event group ID\n"); printf(" (when multiple groups are specified the\n"); printf(" multiplexing mode is activated)\n"); printf(" -T retrieve timestamps\n"); printf(" -@ WparName count only the specified WPAR\n"); printf(" -@ ALL count all active WPARs separately\n"); printf(" workload workload to execute\n"); printf("\n Default counting modes are user, kernel, hypervisor, global.\n"); printf(" Valid filters are: verified, unverified, caveat.\n"); printf(" These represent the testing status of an event.\n"); printf(" When using the multi-mode counting, a list of mode flags can be added to\n"); printf(" a group id (or an event list).\n"); printf(" The mode flags allowed are h, k, u, r, n, t, m, M.\n"); printf(" Mode flags must be preceded by \":\" ex: tcount -g 1:ukh,2:u,3\n"); printf("\n"); } void set_program_and_start(void) { char *funcname; int rc; /* program the counters */ if (!Time_slice_mode) { if (threadapi) { funcname = "main: pm_set_program_mygroup"; rc = pm_set_program_mygroup(&Setprog); } else if (WparName != NULL) { funcname = "pm_set_program_wp"; rc = pm_set_program_wp(cid, &Setprog); } else { funcname = "pm_set_program"; rc = pm_set_program(&Setprog); } } else { /* in time slice mode */ if (threadapi) { funcname = "main: pm_set_program_mygroup_mm"; rc = pm_set_program_mygroup_mm(&Setprog_mm); } else if (WparName != NULL) { funcname = "pm_set_program_wp_mm"; rc = pm_set_program_wp_mm(cid, &Setprog_mm); } else { funcname = "pm_set_program_mm"; rc = pm_set_program_mm(&Setprog_mm); } } if (rc != OK_CODE) { pm_error(funcname, rc); exit(ERROR_CODE); } /* start the counters and record start time */ if (threadapi) { funcname = "pm_tstart_mygroup"; rc = pm_tstart_mygroup(&start_time); } else if (WparName != NULL) { funcname = "pm_tstart_wp"; rc = pm_tstart_wp(cid, &start_time); } else { funcname = "pm_tstart"; rc = pm_tstart(&start_time); } if (rc != OK_CODE) { pm_error(funcname, rc); exit(ERROR_CODE); } } void stop_and_print_results(void) { char *funcname; int rc; if (!Time_slice_mode) { if (threadapi) { funcname = "pm_get_program_mygroup"; rc = pm_get_program_mygroup(&Getprog); } else if (WparName != NULL) { funcname = "pm_get_program_wp"; rc = pm_get_program_wp(cid, &Getprog); } else { funcname = "pm_get_program"; rc = pm_get_program(&Getprog); } } else { /* in time slice mode */ if (threadapi) { funcname = "pm_get_program_mygroup_mm"; rc = pm_get_program_mygroup_mm(&Getprog_mm); } else if (WparName != NULL) { funcname = "pm_get_program_wp_mm"; Getprog_mm.nb_set_prog = Setprog_mm.nb_set_prog; rc = pm_get_program_wp_mm(cid, &Getprog_mm); } else { funcname = "pm_get_program_mm"; rc = pm_get_program_mm(&Getprog_mm); } } if (rc != OK_CODE) { pm_error(funcname, rc); exit(ERROR_CODE); } if (threadapi) { funcname = "pm_stop_mygroup"; rc = pm_stop_mygroup(); } else if (WparName != NULL) { funcname = "pm_stop_wp"; rc = pm_stop_wp(cid); } else { funcname = "pm_stop"; rc = pm_stop(); } if (rc != OK_CODE) { pm_error(funcname, rc); exit(ERROR_CODE); } /* print the results */ if (!Time_slice_mode) { print_config(Getprog.mode); if (Group_counting) print_group(Getprog.events[0], Getprog.mode); else print_events(Getprog.events, Getprog.mode); print_data(&start_time, threadapi); } else { /* in time slice mode */ print_config(Getprog_mm.prog_set[0].mode); print_data_mx(&start_time, threadapi); } } void sigreconfig_handler(int sig) { dr_info_t dri; double new_cycles; if (dr_reconfig(DR_QUERY, &dri) != 0) { /* ignore signal if DR_QUERY fails */ perror("dr_reconfig"); return; } if (!dri.migrate && !dri.hibernate) goto done; /* not a partition migration/hibernation, skip */ if (dri.pre) { printf("Counting stopped by a partition migration/hibernation operation\n"); /* print the report */ stop_and_print_results(); /* delete the programmation */ if (threadapi) pm_delete_program_mygroup(); else if (WparName != NULL) pm_delete_program_wp(cid); else pm_delete_program(); } else if (dri.post) { /* we must resume on the new partition only when running in global mode * and if the processors in the destination partition are of the same * type as in the source partition. */ if (threadapi || pm_get_procindex() != ProcIndex) { (void)dr_reconfig(DR_RECONFIG_DONE, &dri); exit(OK_CODE); } /* if different processor frequencies, print old and new frequencies */ new_cycles = pm_cycles(); if (Cycles != new_cycles) { printf("Old frequency: %g\n" "New frequency: %g\n", Cycles, new_cycles); } /* restart counting */ set_program_and_start(); } done: (void)dr_reconfig(DR_RECONFIG_DONE, &dri); } int main(int argc, char *argv[]) { pm_events2_t *evp; unsigned int tmp; extern int optind; extern char *optarg; int i, j, rc; char **execvector; pid_t child; union wait status; struct sigaction sa; int filter = PM_VERIFIED|PM_UNVERIFIED|PM_CAVEAT|PM_BROKEN; int found = 0; pm_groups_t *grp; /* get the arguments */ if (argc > 1) { process_args(argc, argv, &execvector, &filter, &threadapi); } else { pm_usage(argv[0]); exit(OK_CODE); } /* register SIGRECONFIG signal */ bzero(&sa, sizeof (struct sigaction)); sa.sa_handler = sigreconfig_handler; sa.sa_flags = SA_RESTART; /* if interrupted during wait, restart wait */ if (sigaction(SIGRECONFIG, &sa, NULL) != 0) { perror("sigaction"); exit(ERROR_CODE); } /* pm_initialize */ /* Need to specify that event groups are requested. */ filter |= PM_GET_GROUPS; if ((rc = pm_initialize(filter, &Myinfo, &My_group_info, PM_CURRENT)) != OK_CODE) { pm_error("pm_initialize", rc); exit(ERROR_CODE); } /* save processor type and frequency so we can check if they change after * partition migration/hibernation */ ProcIndex = pm_get_procindex(); Cycles = pm_cycles(); if (!Default_mode.b.user && !Default_mode.b.kernel && !Default_mode.b.hypervisor) { Default_mode.b.user = Default_mode.b.kernel = 1; Default_mode.b.hypervisor = Myinfo.proc_feature.b.hypervisor; } if (!Time_slice_mode) { if (Setprog.mode.w == 0) Setprog.mode.w = Default_mode.w; if (!Group_counting) { /* check validity of the event number */ for (i = 0; i < Myinfo.maxpmcs; i++) { /* with the filter, it's possible that a counter has no * matching event */ if (Myinfo.maxevents[i] == 0) continue; evp = Myinfo.list_events[i]; evp += Myinfo.maxevents[i] - 1; if (Setprog.events[i] < COUNT_NOTHING || Setprog.events[i] > evp->event_id) { fprintf(stderr,"Event %d is invalid in counter %d\n", Setprog.events[i], i+1); exit(ERROR_CODE); } } } else { /* Find pointer to the group */ for (i = 0; i < My_group_info.maxgroups; i++) { grp = My_group_info.event_groups+i; if (Setprog.events[0] == grp->group_id) { found = 1; break; } } if (!found) { fprintf(stderr,"Group %d does not match filter\n",Setprog.events[0]); exit (ERROR_CODE); } } if (Group_counting) Setprog.mode.b.is_group = 1; if (Wpar_all) Setprog.mode.b.wpar_all = 1; if (Process_tree) Setprog.mode.b.proctree = 1; } else { /* Time_slice_mode */ if (Group_counting) { for (j = 0; j < Setprog_mm.nb_set_prog; j++) { if (Setprog_mm.prog_set[j].mode.w == 0) Setprog_mm.prog_set[j].mode.w = Default_mode.w; /* check validity of the event number */ for (i = 0; i < Myinfo.maxpmcs; i++) { /* with the filter, it's possible that a counter has no * matching event */ if (Myinfo.maxevents[i] == 0) continue; evp = Myinfo.list_events[i]; evp += Myinfo.maxevents[i] - 1; if (Setprog_mm.prog_set[j].events[i] < COUNT_NOTHING || Setprog_mm.prog_set[j].events[i] > evp->event_id) { fprintf(stderr,"Event %d is invalid in counter %d\n", Setprog_mm.prog_set[j].events[i], i+1); exit(ERROR_CODE); } } } } else { for (j = 0; j < Setprog_mm.nb_set_prog; j++) { if (Setprog_mm.prog_set[j].mode.w == 0) Setprog_mm.prog_set[j].mode.w = Default_mode.w; /* Find pointer to the group */ for (i = 0; i < My_group_info.maxgroups; i++) { grp = My_group_info.event_groups+i; if (Setprog_mm.prog_set[j].events[0] == grp->group_id) { found = 1; break; } } if (!found) { fprintf(stderr,"Group %d (set %d) does not match filter\n", Setprog_mm.prog_set[j].events[0], j); exit (ERROR_CODE); } } } if (Group_counting) Setprog_mm.prog_set[0].mode.b.is_group = 1; if (Wpar_all) Setprog_mm.prog_set[0].mode.b.wpar_all = 1; if (Process_tree) Setprog_mm.prog_set[0].mode.b.proctree = 1; } set_program_and_start(); /* execute the workload */ child = run_workload(execvector); wait(&(status.w_status)); stop_and_print_results(); exit(OK_CODE); } /* main () */