/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* bos720 src/bos/usr/sbin/perf/pmapi/pmtoolkit/cpi.c 1.7.1.3             */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* Restricted Materials of IBM                                            */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1999,2011              */
/* 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 rcsid []="@(#)77    1.7.1.3     src/bos/usr/sbin/perf/pmapi/pmtoolkit/cpi.c, pmapi, bos720 7/14/11 18:35:18 ";
/*
 *   COMPONENT_NAME: PMAPI
 *
 *   FUNCTIONS: main
 *		search_cpi_group
 *		search_cpi
 *
 *   ORIGINS: 27, 83
 *
 *                    -- (                            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.
 */

#include <stdio.h>
#include <string.h>
#include <values.h>
#include <signal.h>
#include <sys/dr.h>
#include "pmapi.h"

#define CYC_INDEX	0
#define INST_INDEX	1
#define MAX_EVENTS	2

char 	*Search_evs[MAX_EVENTS] = {"PM_CYC", "PM_INST_CMPL"};

/* *************************************************************************
 * Name:        search_cpi_group
 *
 * Function:    searches for events PM_CYC and PM_INST_CMPL
 *		within tables of event groups and events.
 *
 * Input:	myginfo = list of event groups to search
 *		myinfo  = list of events used in myginfo
 *
 * Output:	pmc[CYC_INDEX]  = counter selected for PM_CYC
 *		pmc[INST_INDEX] = counter selected for PM_INST_CMPL
 *		evs[0]          = selected group number
 *
 * Returns	0  if PM_CYC and PM_INST_CMPL were found
 *		-1 if not found
 * *************************************************************************
 */
int
search_cpi_group(pm_info2_t *myinfo, pm_groups_info_t *myginfo, int evs[], int pmc[])
{
	int		i, j, counter, event, s_index;
	pm_events2_t	*evp;

	if (myginfo->event_groups == NULL) {
		return -1;
	}

	/* check each of the available groups */
	for (i = 0; i < myginfo->maxgroups; i++) {
		pmc[CYC_INDEX]  = -1; 
		pmc[INST_INDEX] = -1;

		/* check each of the counter setting within the current group */
		for (counter = 0; counter < myinfo->maxpmcs; counter++) {
			/* get the event id from the list */
			event = myginfo->event_groups[i].events[counter];

			if (!((event == COUNT_NOTHING) || (myinfo->maxevents[counter] == 0))) {
				/* retrieve pointer to the event */
				evp = &(myinfo->list_events[counter][event]);

				for (s_index = 0; s_index < MAX_EVENTS; s_index++) {
					if (strcmp(Search_evs[s_index], evp->short_name)== 0) {
						if (s_index == CYC_INDEX)
							pmc[CYC_INDEX]  = counter;
						else if (s_index == INST_INDEX)
							pmc[INST_INDEX] = counter;
						break;
					}
				}

				if (pmc[CYC_INDEX] != -1 && pmc[INST_INDEX] != -1) {
					evs[0] = i;
					return(0);
				}
			} /* if */
		} /* for (counter = 0; ... */
	} /* for groups */
	return(-1);
}

/* *************************************************************************
 * Name:        search_cpi
 *
 * Function:    searches for events PM_CYC and PM_INST_CMPL
 *              within a table of events, while excluding
 *		group_only events.
 *
 * Input:	myinfo  = list of events used in myginfo
 *
 * Output:	pmc[CYC_INDEX]  = counter selected for PM_CYC
 *		pmc[INST_INDEX] = counter selected for PM_INST_CMPL
 *		evs[0..myinfo->maxpmcs] = selected event numbers
 *
 * Returns	0  if PM_CYC and PM_INST_CMPL were found
 *		-1 if not found
 * *************************************************************************
 */
int
search_cpi(pm_info2_t *myinfo, int evs[], int pmc[])
{
	int		s_index, counter, ev;
	int		i, j;
	int		cyclfound, instfound;
	int		evstmp[MAX_COUNTERS][MAX_EVENTS];
	pm_events2_t	*wevp;

	for (counter = 0; counter < myinfo->maxpmcs; counter++) {
		evstmp[counter][CYC_INDEX]  = -1;
		evstmp[counter][INST_INDEX] = -1;
		evs[counter] = 0;
	}

	/* check each of the hardware counter */
	for (counter = 0; counter < myinfo->maxpmcs; counter++) {
		wevp = myinfo->list_events[counter];
		cyclfound = 0;
		instfound = 0;

		/* check all available events for current counter */
		for (ev = 0; ev < myinfo->maxevents[counter]; ev++, wevp++) {
			/* skip group-only events */
			if (!wevp->status.b.group_only) {

				/* if not yet found, compare current event to both events searched */
				if (!cyclfound) {
					if (strcmp(Search_evs[CYC_INDEX], wevp->short_name) == 0) {
						cyclfound = 1;
						evstmp[counter][CYC_INDEX] = wevp->event_id;
					}
				}
				if (!instfound) {
					if (strcmp(Search_evs[INST_INDEX], wevp->short_name) == 0) {
						instfound = 1;
						evstmp[counter][INST_INDEX] = wevp->event_id;
					}
				}

				/* if both events found, stop the search */
				if (cyclfound && instfound)
					break;
			}

			/* if either event searched still not found, continue with next pmc */
			if (!cyclfound || !instfound)
				continue;
		} /* for ev */

		/* search for a couple of pmcs containing the two events */
		for (i = 0; i <= counter; i++)
		{
			/* check the first event */
			if (evstmp[i][CYC_INDEX] == -1)
				continue;

			/* check the second event in any other pmc than
			   the one containing the first event */
			for (j = 0; j <= counter; j++) {
				if ((i != j) && (evstmp[j][INST_INDEX] != -1))
					break;
			}

			/* couple found */
			if (j <= counter) {
				pmc[CYC_INDEX] = i;
				pmc[INST_INDEX]   = j;
				evs[i] = evstmp[i][CYC_INDEX];
				evs[j] = evstmp[j][INST_INDEX];
				return(0);
			}
		}
	} /* for counter */
	return(-1);
}

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");
    (void)dr_reconfig(DR_RECONFIG_DONE, &dri);
    return;
  }

  if (!dri.migrate && !dri.hibernate)
    return;	/* not a partition migration/hibernation, skip */

  if (dri.pre || dri.check) {
    printf("Counting stopped by a partition migration/hibernation operation\n");
    (void)dr_reconfig(DR_RECONFIG_DONE, &dri);
    exit(-1);

  } /*No need to handle POST* signals, they are received on a partition restore*/
}

main(int argc, char *argv[])
{
	pm_info2_t	myinfo;
	pm_prog_t	myprog;
	pm_data_t	mydata;
	pm_groups_info_t myginfo;
    struct sigaction sa;
	int		i, rc, opt;
	int		stime = 5;
	int		maxiter = MAXINT;
	int		verbose = 0;
	int		pmc[MAX_EVENTS];
	extern char	*optarg;
	extern int	optind;
	long long	last_inst, last_cycles, delt_inst, delt_cycles;

	/* retrieve processor specific tables of events and groups */
	if ((rc = pm_initialize(PM_VERIFIED|PM_UNVERIFIED|PM_CAVEAT|PM_GET_GROUPS,
	    &myinfo, &myginfo, PM_CURRENT)) > 0) {
		pm_error("pm_initialize", rc);
		exit(-1);
	}

  /* 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(-1);
  }

	while ((opt = getopt(argc, argv, "v")) != EOF) {
		switch(opt) {
		case 'v':
			verbose = 1;
			break;

		default:
			printf("usage: cpi [-v] [interval [count]]\n");
			exit(1);
		}
	}

	/* skip arguments already processed */
	argc -= optind;
	argv += optind;

	/* grab optional arguments */
	if (argc > 0) {
		/* sample duration */
		stime = atoi(argv[0]);
		if (argc > 1) {
			/* number of iterations */
			maxiter = atoi(argv[1]);
		}
	}

	/* set to count in all modes */
	myprog.mode.w            = 0;
	myprog.mode.b.kernel     = 1;
	myprog.mode.b.user       = 1;

	/* also count in hypervisor mode if this processor supports it */
	if (myinfo.proc_feature.b.hypervisor) {
		myprog.mode.b.hypervisor = 1;
	}

	/* initialize events to count */
	for (i = 0; i < myinfo.maxpmcs; i++) {
		myprog.events[i] = 0;
	}

	/* search for events PM_CYC and PM_INST_CMPL first in the event table */
	/* while skipping group-only events. If the pair of events cannot be  */
	/* found in the reduced event table, search for it in the group table */
	if (search_cpi(&myinfo, myprog.events, pmc) < 0) {
		if (search_cpi_group(&myinfo, &myginfo, myprog.events, pmc) < 0) {
			printf("Could not find cpi events for this processor\n");
			exit(-1);
		}
		myprog.mode.b.is_group = 1;
	}
	/* pmc[CYC_INDEX]  (selected pmc number to count PM_CYC),       */
	/* pmc[INST_INDEX] (selected pmc number to count PM_INST_CMPL), */
	/* and myprog.events have all been updated in either search_cpi */
	/* or search_cpi_group */

	if (verbose) {
		int ev1, ev2, grp, pmc1, pmc2;

		pmc1 = pmc[CYC_INDEX];
		pmc2 = pmc[INST_INDEX];

		if (myprog.mode.b.is_group) {
			grp = myprog.events[0];
			ev1 = myginfo.event_groups[grp].events[pmc1];
			ev2 = myginfo.event_groups[grp].events[pmc2];

			printf("using group number %d : %s\n",  grp,
			    myginfo.event_groups[grp].short_name);
		}
		else {
			ev1 = myprog.events[pmc1];
			ev2 = myprog.events[pmc2];
		}

		printf("pmc%d is counting event number %d : %s\n",
		    pmc1+1, ev1, myinfo.list_events[pmc1][ev1].short_name);

		printf("pmc%d is counting event number %d : %s\n",
		    pmc2+1, ev2, myinfo.list_events[pmc2][ev2].short_name);

		printf("\n");
	}

	printf(" Instructions       Cycles       CPI    IPC\n");
	printf("--------------  --------------  -----  -----\n");

	/* set the PM programmation */
	if ((rc = pm_set_program(&myprog)) > 0) {
		pm_error("pm_set_program", rc);
		exit(-1);
	}

	/* start the counters */
	if ((rc = pm_start()) > 0) {
		pm_error("pm_start", rc);
		exit(-1);
	}

	last_inst   = 0;
	last_cycles = 0;
	for (i = 0; i < maxiter; i++) {
		sleep(stime);

		/* get the data */
		if ((rc = pm_get_data(&mydata)) > 0) {
			pm_error("pm_get_data", rc);
			exit(-1);
		}

		delt_inst   = mydata.accu[pmc[INST_INDEX]]  - last_inst;
		delt_cycles = mydata.accu[pmc[CYC_INDEX]] - last_cycles;

		printf("%-14lld  %-14lld  %5.2f  %5.2f\n",
		    delt_inst,
		    delt_cycles,
		    (double) delt_cycles / (double) delt_inst,
		    (double) delt_inst   / (double) delt_cycles);

		last_inst   = mydata.accu[pmc[INST_INDEX]];
		last_cycles = mydata.accu[pmc[CYC_INDEX]];
	}
}