/* 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/dr.h>
#include <sys/m_wait.h>
#include <sys/systemcfg.h>
#include <sys/processor.h>
#include <wpars/wparcfg.h>
#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<Myinfo.maxpmcs; i++) {
      printf("    PMC%2d     ", i+1);
      len = strlen(str);
      str[len] = ' ';
      sprintf(str+len,"%s","============  ");
    }
    printf("\n%s\n", str);

    for (i=0; i<Myinfo.maxpmcs; i++) {
      if (valid_count_tbl[i]) {
        printf("%-12lld  ", data.accu[i]); 
      } else {
        warn = 1;
        printf("  --------    ");
      }
    }
    printf("\n");

  } else {

    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++) {
        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<Myinfo.maxpmcs; i++) {
          printf("    PMC%2d     ", i+1);
          len = strlen(str);
          str[len] = ' ';
          sprintf(str+len,"%s","============  ");
        }
        printf("\n%s\n", str);

        if (Bind_cpu) {
	  /* Bind_cpu not supported w/ -@ option */
	  for (cpuid = 0; cpuid < _system_configuration.ncpus; cpuid++) {
            if ((rc = pm_get_tdata_cpu(cpuid, &data, &end_time)) != OK_CODE) {
              pm_error("pm_get_tdata_cpu", rc);
              exit(ERROR_CODE);
            }

	    printf("[%2d]  ",cpuid);
	    for (i = 0; i < Myinfo.maxpmcs; i++) {
              if (valid_count_tbl[i]) {
                printf("%-12lld  ", data.accu[i]);
                totcounts[i] += data.accu[i];
	      } else {
	        warn = 1;
	        printf("  --------    ");
	      }
	    }
	    printf("\n");
	  }

        } else { /* not Bind_cpu */
          for (cpuid = 0; cpuid < _system_configuration.max_ncpus; cpuid++) {
	    if (WparName != NULL || Wpar_all) {
	      if ((rc = pm_get_tdata_lcpu_wp(info[w].hwpar, cpuid, &data,
						&end_time)) != OK_CODE) {
	        if (rc == EPM_BAD_CPU)
	          continue;
	        pm_error("pm_get_tdata_lcpu_wp", rc);
                exit(ERROR_CODE);
	      }
	    } else {
              if ((rc = pm_get_tdata_lcpu(cpuid, &data, &end_time))
							    != OK_CODE) {
	        if (rc == EPM_BAD_CPU)
	          continue;
	        pm_error("pm_get_tdata_lcpu", rc);
                exit(ERROR_CODE);
              }
	    }

	    printf("[%2d]  ",cpuid);
	    for (i = 0; i < Myinfo.maxpmcs; i++) {
              if (valid_count_tbl[i]) {
                printf("%-12lld  ", data.accu[i]);
                totcounts[i] += data.accu[i];
	      } else {
	        warn = 1;
	        printf("  --------    ");
	      }
	    }
	    printf("\n");
	  }
        }

        printf("%s\n", str);	
        printf("ALL   ");
        for (i = 0; i < Myinfo.maxpmcs; i++) {
	  if (valid_count_tbl[i]) {
             printf("%-12lld  ", totcounts[i]);
	  } else {
            warn = 1;
            printf("  --------    ");
	  }
        }
        printf("\n");
      }
    } else {	/* ncpus == 1 */
      for (w = 0; w < nwpars; w++) {
        if (WparName != NULL || Wpar_all) {
	  if ((rc = pm_get_tdata_wp(info[w].hwpar, &data, &end_time))
						    != OK_CODE)
	    pm_error("pm_get_tdata_wp", rc);
        } else {
          if ((rc = pm_get_tdata(&data, &end_time)) != OK_CODE)
            pm_error("pm_get_tdata", rc);
        }

	if (Wpar_all)
	  printf("\n*** Results @%s :\n", info[w].name);
	else
          printf("\n*** Results :\n");

        str[0] = '\0';
        for (i=0; i<Myinfo.maxpmcs; i++) {
          printf("    PMC%2d     ", i+1);
          len = strlen(str);
          str[len] = ' ';
          sprintf(str+len,"%s","============  ");
        }
        printf("\n%s\n", str);

        for (i=0; i<Myinfo.maxpmcs; i++) {
	  if (valid_count_tbl[i]) {
            printf("%-12lld  ", data.accu[i]); 
	  } else {
            warn = 1;
            printf("  --------    ");
	  }
        }
        printf("\n");
      }
    }
  } /* 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);
}


#define timeradd(tvp, svp, rvp)					\
do {								\
	(rvp).tb_high = (tvp).tb_high + (svp).tb_high;		\
	(rvp).tb_low = (tvp).tb_low + (svp).tb_low;		\
	if((rvp).tb_low > (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; i<Myinfo.maxpmcs; i++) {
        printf("    PMC%2d     ", i+1);
        len = strlen(str);
        str[len] = ' ';
        sprintf(str+len,"%s","============  ");
      }
      printf("\n%s\n", str);
      
      for (i=0; i<Myinfo.maxpmcs; i++) {
	if (valid_count_tbl[i]) {
          printf("%-12lld  ", data_mx->accu_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; i<Myinfo.maxpmcs; i++) {
            printf("    PMC%2d     ", i+1);
            len = strlen(str);
            str[len] = ' ';
            sprintf(str+len,"%s","============  ");
          }
          printf("\n%s\n", str);
      
          for (i=0; i<Myinfo.maxpmcs; i++) {
	    if (valid_count_tbl[i]) {
              printf("%-12lld  ", data_mx->accu_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<value>, 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 () */
