/************************************************************
 *
 * COPYRIGHT (C) HITACHI,LTD. 2001-2003 ALL RIGHTS RESERVED.
 * WRITTEN BY HITACHI SYSTEMS DEVELOPMENT LABORATORY,
 *            HITACHI CENTRAL RESEARCH LABORATORY.
 *
 * Created by K.SERIZAWA <serizawa@sdl.hitachi.co.jp>
 * Updated by M.Hiramatsu <hiramatu@sdl.hitachi.co.jp>
 *
 ************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/lkst_buffer.h>
#include <linux/lkst_evhandler.h>
#include <linux/wait.h>
#include <string.h>		/* for strerror(),memset(),strcpy() */

#define	EVHANDLER_NAME_COUNT "lkst_evhandler_event_count"
#define EVHANDLER_NAME_STOPPER	"lkst_evhandler_event_count_stopper"

#define LKST_ETYPE_VERIFY(event_type)	(!(0xfffff000&(event_type)))

				/* Array of mnemonics of event types */
#define LKST_ETYPE_DEF_MNEMONICS

#include <linux/lkst_events.h>

#define BUFF_SZ (LKST_MASKSET_ID_MAX+1)
struct lkst_log_record buffer[BUFF_SZ];
struct lkst_log_record buffer_merged[BUFF_SZ]={{{0}}};

void usage(const char *emsg){
	fprintf(stderr, "Error: %s\n", emsg);
	fprintf(stdout, "Display and clear counters.\n");
	fprintf(stdout, "Usage: lkst_mod_event_count_read [-c | -k] [-C] [-l count_limit]\n");
	fprintf(stdout, "       -c, --clear \n");
	fprintf(stdout, "         Clear counter ONLY (don't display counters)\n");
	fprintf(stdout, "       -k, --keep \n");
	fprintf(stdout, "         Display counter ONLY (don't clear counters)\n");
	fprintf(stdout, "       -C, --csv \n");
	fprintf(stdout, "         Display as csv format\n");
	fprintf(stdout, "       -l, --limit=count_limit \n");
	fprintf(stdout, "         Set limit number of counter\n");
}


#define ACT_READ	1
#define ACT_CLEAR	2
#define ACT_CSV		4
#define ACT_LIMIT	8

#define MODE_KEEPCOUNT (ACT_READ)
#define MODE_CLEARONLY (ACT_CLEAR)
#define MODE_READCLEAR ((ACT_READ)|(ACT_CLEAR))

int main(int argc, char** argv)
{
	int retval;
	int fd;
	int cpu;
	int iCnt, max_count, mode;
	unsigned int	*pArgs;
	struct lkst_log_buffer lbuffer;
	unsigned int event_type,
		time_min=0xffffffff, time_max=0, jiffies;
	u_int64_t  event_count_sum=0;
	u_int64_t  limit_count=0;
	lkst_buffer_id count_buf[LKST_CPU_MAX];
	struct lkst_evhandler_ctrl_param param;

	mode = MODE_READCLEAR;
	if ( argc >= 2 ) {
		for (iCnt=1;iCnt<argc;iCnt++) { 
			if ( !strcmp(argv[iCnt],"-c") || 
			     !strcmp(argv[iCnt],"--clear") ){
				mode = MODE_CLEARONLY;
			} else if ( !strcmp(argv[iCnt],"-k") || 
				    !strcmp(argv[iCnt],"--keep") ){
				mode = MODE_KEEPCOUNT;
			} else if ( !strcmp(argv[iCnt],"-C") || 
				    !strcmp(argv[iCnt],"--csv") ){
				mode |= ACT_CSV;
			} else if ( !strcmp(argv[iCnt],"-l") ) {
				mode |= ACT_LIMIT;
				limit_count = strtoll(argv[++iCnt],NULL,0);
			} else if ( !strcmp(argv[iCnt],"--limit=") ){
				mode |= ACT_LIMIT;
				limit_count = strtoll((argv[iCnt]+8),NULL,0);
			} else {
				usage("Invalid argument");
				exit(1);
			}
		}
	}
	
	if (( fd = open("/dev/lkst" ,O_RDONLY)) < 0){
		perror("open");
		exit (1);
	}
	
	/* set limitation number */
	if (mode & ACT_LIMIT){
		struct lkst_evhandler_getparam gp;
		strcpy(gp.name, EVHANDLER_NAME_STOPPER);
		if (ioctl(fd, LKST_IOC_EVHANDLER_GETID, &gp) < 0) {
			perror("ioctl(LKST_IOC_EVHANDLER_GETID)");
			exit (1);
		}
		if ( (param.id = gp.id) == LKST_EVHANDLER_ID_VOID ) {
			fprintf(stderr,"\"%s\" is not found\n", 
				EVHANDLER_NAME_COUNT);
			exit(1);
		}
		param.bufsize = sizeof(u_int64_t);
		param.buf = &limit_count;
		retval = ioctl(fd, LKST_IOC_EVHANDLER_CTRL, &param);
		if ( retval < 0 ) {
			perror("ioctl(LKST_IOC_EVHANDLER_CTRL)");
			exit (1);
		}

		if(!(mode & ~ACT_LIMIT)){
			goto end_proc;
		}
	}
	
	/* get the id of event handler*/{
		struct lkst_evhandler_getparam gp;
		strcpy(gp.name, EVHANDLER_NAME_COUNT);
		if (ioctl(fd, LKST_IOC_EVHANDLER_GETID, &gp) < 0) {
			perror("ioctl(LKST_IOC_EVHANDLER_GETID)");
			exit (1);
		}
		if ( (param.id = gp.id) == LKST_EVHANDLER_ID_VOID ) {
			fprintf(stderr,"\"%s\" is not found\n", 
				EVHANDLER_NAME_COUNT);
			exit(1);
		}
	}

	if (mode & ACT_READ) {
		/* get buffers for counting */
		param.bufsize = sizeof(lkst_buffer_id)*LKST_CPU_MAX;
		param.buf = count_buf;
		if (ioctl(fd, LKST_IOC_EVHANDLER_CTRL, &param) < 0) {
			perror("ioctl(LKST_IOC_EVHANDLER_CTRL)");
			exit (1);
		} else if ( param.ret != 0 ) {
			fprintf(stderr, "evhandler ctrl error %d\n",param.ret);
			exit(1);
		}
		
		lbuffer.read_size = BUFF_SZ*sizeof(struct lkst_log_record);
		lbuffer.buffer = buffer;
		
		for (cpu=0; cpu < 32; cpu++) {
			lbuffer.cpu = cpu;
			lbuffer.id = count_buf[cpu];
			if (lbuffer.id >= LKST_BUFFER_ID_VOID) continue;
			
			/* buff read */
			retval = ioctl(fd, LKST_IOC_BUFFER_READ, &lbuffer);
#if DEBUG
			printf("ioctl retval=%d result_read_size=%d id=%d\n", 
			       retval,lbuffer.result_read_size, lbuffer.id);
#endif /* DEBUG */

			if (retval < 0){
				perror("ioctl(LKST_IOC_BUFFER_READ)");
				break;
			}
			
			max_count = lbuffer.result_read_size / 
				sizeof(struct lkst_log_record);
			/* merge counters on each events */
			for (iCnt = 0; iCnt < max_count; iCnt++) {
				event_type = buffer[iCnt].posix.log_event_type;
				if (!(buffer[iCnt].posix.log_recid) || 
				    !(LKST_ETYPE_VERIFY(event_type)))
					continue;
				buffer_merged[iCnt].posix.log_event_type
					= event_type;
				buffer_merged[iCnt].posix.log_recid
					= buffer[iCnt].posix.log_recid;
				
				buffer_merged[iCnt].log_arg1 += buffer[iCnt].log_arg1;
				jiffies = LKST_ARG32_L(buffer[iCnt].log_arg3);
				if (jiffies>time_max) time_max = jiffies;
				if (jiffies<time_min) time_min = jiffies;
				event_count_sum += buffer[iCnt].log_arg1;
				
				if (LKST_ARG32_L(buffer_merged[iCnt].log_arg3) <
				    LKST_ARG32_L(buffer[iCnt].log_arg3))
					buffer_merged[iCnt].log_arg3 = buffer[iCnt].log_arg3;
			}
		}
		
		if (time_max == time_min) time_max++;/* avoid div0 error */
		printf((mode&ACT_CSV)? "%s,%s,%s,%s\n":
		       "    %25s(%2s):\t%16s\t%10s\n",
		       "event_type","ID", "count", "count/msec");
		for (iCnt = 0; iCnt < max_count; iCnt++) {
			event_type = buffer_merged[iCnt].posix.log_event_type;
			if ((buffer_merged[iCnt].posix.log_recid)&&
			    (LKST_ETYPE_VERIFY(event_type)) && 
			    lkst_event_type_mnemonics[event_type] ) {
				pArgs = (unsigned int*)&buffer_merged[iCnt].log_arg1;
				printf((mode&ACT_CSV)?"%s,0x%x,%llu,%8.05g\n":
				       "%25s(0x%04x):\t%16llu\t%8.05g\n", 
				       lkst_event_type_mnemonics[event_type],
				       event_type,
				       buffer_merged[iCnt].log_arg1,
				       (double) buffer_merged[iCnt].log_arg1/((time_max - time_min)*10));
			}
		}
		if (mode&ACT_CSV) { 
			printf("total time, total events, rate\n");
			printf ("%10.04g,%16llu,%10.05g\n",
				(time_max - time_min)/100.0, 
				event_count_sum,
				event_count_sum/(double)((time_max - time_min)*10));
		}else {
			printf ("  Total time[s] = %16.04g\n", (time_max - time_min)/100.0);
			printf ("  Total events  = %16llu\n", event_count_sum);
			printf ("  rate [n/msec] = %16.05g\n",
				event_count_sum/(double)((time_max - time_min)*10));
		}
	} /*(mode & ACT_READ)*/

	if (mode & ACT_CLEAR) {
		/* reset buffers for counting */
		for (cpu=0; cpu < 32; cpu++) {
			if (count_buf[cpu] >= LKST_BUFFER_ID_VOID) continue;
			param.buf = (void*)cpu;/*it's OK*/
			param.bufsize = 0;
			retval = ioctl(fd, LKST_IOC_EVHANDLER_CTRL, &param);
			if (retval < 0) {
				perror("ioctl(LKST_IOC_EVHANDLER_CTRL)");
				exit (1);
			}
		}
	} /*(mode & ACT_CLEAR)*/
 end_proc:
	close(fd);
	return 0;
}
	
