diff --git a/src/dev/devVXStats.c b/src/dev/devVXStats.c index e83639ba0..781aec7f2 100644 --- a/src/dev/devVXStats.c +++ b/src/dev/devVXStats.c @@ -1,154 +1,32 @@ - -/* devVX.c - Device Support Routines for vxWorks statistics */ +/* devVXStats.c - Device Support Routines for vxWorks statistics */ /* - * Author: Jim Kowalkowski + * Original Author: Jim Kowalkowski * Date: 2/1/96 - * - * Experimental Physics and Industrial Control System (EPICS) - * - * Copyright 1991, the Regents of the University of California, - * and the University of Chicago Board of Governors. - * - * This software was produced under U.S. Government contracts: - * (W-7405-ENG-36) at the Los Alamos National Laboratory, - * and (W-31-109-ENG-38) at Argonne National Laboratory. - * - * Initial development by: - * The Controls and Automation Group (AT-8) - * Ground Test Accelerator - * Accelerator Technology Division - * Los Alamos National Laboratory - * - * Co-developed with - * The Controls and Computing Group - * Accelerator Systems Division - * Advanced Photon Source - * Argonne National Laboratory - * - * Modification Log: - * ----------------- - * - * $Log$ - * Revision 1.2 1997/04/30 18:57:33 mrk - * Fixed most compiler warning messages - * - * Revision 1.1 1996/10/21 15:30:37 jbk - * Added ai/ao device support for vxWorks statics (memory/load/TCP con) - * + * Current Author: Marty Kraimer + * Date: 18NOV1997 */ - -/* - add the following to devSup.ascii: - - "ai" VME_IO "devAiVXStats" "VX stats" - "ao" VME_IO "devAoVXStats" "VX stats" - - -------------------------------------------------------------------- - Add TCP and CA connection information before release. - Add ability to adjust the rate of everything through ao records - - sample db file to use all data available: - Notive that the valid values for the parm field of the link - information are: - - ai: - free_bytes - number of bytes in IOC not allocated - allocated_bytes - number of bytes allocated - load - estimated percent CPU usage by tasks - tcp_connections - number of TCP connection currently in use - ao: - memoryScanRate - max rate at which new memory stats can be read - tcpScanRate - max rate at which TCP connections can be calculated - loadScanRate - max rate at which load can be calulated - - loadSampleRate - vxWorks spy facility samples-per-second for load - loadSampleLength - length of time in second that samples for load - calculation are taken. - - * scan rates are all in seconds - - default rates: - 10 - memory scan rate - 60 - load scan rate - 20 - tcp scan rate - 15 - sample length - 80 - sample rate - - # sample database using all the different types of statistics - record(ai,"$(PRE):freeBytes") - { - field(DTYP,"VX stats") - field(SCAN,"I/O Intr") - field(INP,"#C0 S0 @free_bytes") - } - record(ai,"$(PRE):allocatedBytes") - { - field(DTYP,"VX stats") - field(SCAN,"I/O Intr") - field(INP,"#C0 S0 @allocated_bytes") - } - record(ai,"$(PRE):load") - { - field(DTYP,"VX stats") - field(SCAN,"I/O Intr") - field(INP,"#C0 S0 @load") - field(PREC,"3") - } - record(ai,"$(PRE):tcpConnections") - { - field(DTYP,"VX stats") - field(SCAN,"I/O Intr") - field(INP,"#C0 S0 @tcp_connections") - } - record(ao,"$(PRE):memoryScanRate") - { - field(DTYP,"VX stats") - field(OUT,"#C0 S0 @memory_scan_rate") - } - record(ao,"$(PRE):tcpScanRate") - { - field(DTYP,"VX stats") - field(OUT,"#C0 S0 @tcp_scan_rate") - } - record(ao,"$(PRE):loadScanRate") - { - field(DTYP,"VX stats") - field(OUT,"#C0 S0 @load_scan_rate") - } - record(ao,"$(PRE):loadSampleRate") - { - field(DTYP,"VX stats") - field(OUT,"#C0 S0 @load_sample_rate") - } - record(ao,"$(PRE):loadSampleLength") - { - field(DTYP,"VX stats") - field(OUT,"#C0 S0 @load_length") - } - -*/ +/***************************************************************** + COPYRIGHT NOTIFICATION +***************************************************************** + +(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO + +This software was developed under a United States Government license +described on the COPYRIGHT_UniversityOfChicago file included as part +of this distribution. +**********************************************************************/ #include +#include #include #include #include -#include #include #include +#include #include -#include -#include -#include -#include -#include -#include -#ifndef VX52 -#include -#include -#endif #include -#include #include #include #include @@ -158,42 +36,114 @@ #include #include #include -#include -/* length in seconds */ -#define HARDCODED_LOAD_LENGTH 15 -/* rate in times per second */ -#define SPY_SAMPLING_RATE 80 +/* + The following device definitions must exist -#define MAX_TASK 70 -#define WORD_SIZE 2 + device(ai,INST_IO,devAiVXStats,"VX stats") + device(ao,INST_IO,devAoVXStats,"VX stats") + -------------------------------------------------------------------- + sample db file to use all data available: + Notive that the valid values for the parm field of the link + information are: + + ai: + memory - % memory used + cpu - % CPU usage by tasks + fd - % of file descripters used + ao: + memoryScanRate - Set memory scan rate + cpuScanRate - Set cpu scan rate + fdScanRate - Set fd scan rate + + + # sample database using all the different types of statistics + record(ai,"$(IOC):memory") + { + field(DTYP,"VX stats") + field(SCAN,"I/O Intr") + field(INP,"@memory") + field(HOPR,"100.0") + field(HIHI,"90.0") + field(HIGH,"60.0") + field(HHSV, "MAJOR") + field(HSV, "MINOR") + } + record(ai,"$(IOC):cpu") + { + field(DTYP,"VX stats") + field(SCAN,"I/O Intr") + field(INP,"@cpu") + field(HOPR,"100.0") + field(HIHI,"90.0") + field(HIGH,"60.0") + field(HHSV, "MAJOR") + field(HSV, "MINOR") + } + record(ai,"$(IOC):fd") + { + field(DTYP,"VX stats") + field(SCAN,"I/O Intr") + field(INP,"@fd") + field(HOPR,"100.0") + field(HIHI,"90.0") + field(HIGH,"60.0") + field(HHSV, "MAJOR") + field(HSV, "MINOR") + } + record(ao,"$(IOC):memoryScanRate") + { + field(DTYP,"VX stats") + field(OUT,"@memoryScanRate") + } + record(ao,"$(IOC):fdScanRate") + { + field(DTYP,"VX stats") + field(OUT,"@fdScanRate") + } + record(ao,"$(IOC):cpuScanRate") + { + field(DTYP,"VX stats") + field(OUT,"@cpuScanRate") + } +*/ + #define MEMORY_TYPE 0 -#define LOAD_TYPE 1 -#define TCP_TYPE 2 +#define CPU_TYPE 1 +#define FD_TYPE 2 #define TOTAL_TYPES 3 -#define START_ME 0 -#define STOP_ME 1 +#define SECONDS_TO_BURN 5 -typedef void (*statGetFunc)(unsigned long*,double*); -typedef void (*statPutFunc)(int,unsigned long); -typedef struct vmeio vmeio; +/* default rate in seconds (memory,cpu,fd) */ +static int default_scan_rate[] = { 10,50,10 }; -#ifdef VX52 -typedef MEM_PART_STATS MEM_PART_STATS_P; -#else -typedef struct +static char *aiparmValue[TOTAL_TYPES] = {"memory","cpu","fd"}; +static char *aoparmValue[TOTAL_TYPES] = { + "memoryScanRate","cpuScanRate","fdScanRate"}; + +typedef struct devPvt { - unsigned long numBytesFree, /* Number of Free Bytes in Partition */ - numBlocksFree, /* Number of Free Blocks in Partition */ - maxBlockSizeFree,/* Maximum block size that is free. */ - numBytesAlloc, /* Number of Allocated Bytes in Partition */ - numBlocksAlloc; /* Number of Allocated Blocks in Partition */ -} MEM_PART_STATS_P; -#endif + int type; +}devPvt; -struct aStats +typedef struct scanInfo +{ + IOSCANPVT ioscanpvt; + WDOG_ID wd; + int rate_tick; /* ticks */ +}scanInfo; + +static scanInfo scan[TOTAL_TYPES]; + +static double getMemory(void); +static double getCpu(void); +static double getFd(void); +static void wdCallback(int type); +static void cpuUsageInit(void); + +typedef struct aiaodset { long number; DEVSUPFUN report; @@ -202,61 +152,7 @@ struct aStats DEVSUPFUN get_ioint_info; DEVSUPFUN read_write; DEVSUPFUN special_linconv; -}; -typedef struct aStats aStats; - -struct pvtArea -{ - int index; - int type; -}; -typedef struct pvtArea pvtArea; - -struct mem_info -{ - MEM_PART_STATS_P m_stat; -}; -typedef struct mem_info mem_info; - -struct spy_info -{ - CALLBACK cb; - WDOG_ID wd; /* watch dog to stop spy clock */ - volatile double load_average; /* last calculated load average */ - volatile int type; /* start/stop indicator */ - volatile unsigned long length_ticks; /* length of sampling in ticks */ - volatile unsigned long sample_rate; /* sampling rate for spy (per sec) */ -}; -typedef struct spy_info spy_info; - -struct validGetParms -{ - char* name; - statGetFunc func; - int type; -}; -typedef struct validGetParms validGetParms; - -struct validPutParms -{ - char* name; - statPutFunc func; - int type; -}; -typedef struct validPutParms validPutParms; - -struct glob_vars -{ - IOSCANPVT ioscan; - WDOG_ID wd; - volatile int total; /* total users connected */ - volatile int on; /* watch dog on? */ - volatile time_t last_read_sec; /* last time seconds */ - volatile unsigned long rate_sec; /* seconds */ - volatile unsigned long rate_tick; /* ticks */ -}; -typedef struct glob_vars glob_vars; - +}aiaodset; static long ai_init(int pass); static long ai_init_record(aiRecord*); static long ai_read(aiRecord*); @@ -264,380 +160,243 @@ static long ai_ioint_info(int cmd,aiRecord* pr,IOSCANPVT* iopvt); static long ao_init_record(aoRecord* pr); static long ao_write(aoRecord*); - -static void load_time_callback(CALLBACK*); -static void read_mem_stats(void); -static void read_tcp_stats(void); -static void read_load_stats(void); -static void scan_time(int); - -static void statsFreeBytes(unsigned long*,double*); -static void statsFreeBlocks(unsigned long*,double*); -static void statsAllocBytes(unsigned long*,double*); -static void statsAllocBlocks(unsigned long*,double*); -static void statsProcessLoad(unsigned long*,double*); -static void statsTcpConnects(unsigned long*,double*); -static void statsRate(int type,unsigned long); -static void statsLoadSampleRate(int type,unsigned long); -static void statsLoadLength(int type,unsigned long); - -/* rate in seconds (memory,load,tcp) */ -static int scan_rate_sec[] = { 10,60,30 }; - -static validGetParms statsGetParms[]={ - { "free_bytes", statsFreeBytes, MEMORY_TYPE }, - { "free_blocks", statsFreeBlocks, MEMORY_TYPE }, - { "allocated_bytes", statsAllocBytes, MEMORY_TYPE }, - { "allocated_blocks", statsAllocBlocks, MEMORY_TYPE }, - { "load", statsProcessLoad, LOAD_TYPE }, - { "tcp_connections", statsTcpConnects, TCP_TYPE }, - { NULL,NULL,0 } -}; - -static validPutParms statsPutParms[]={ - { "memory_scan_rate", statsRate, MEMORY_TYPE }, - { "tcp_scan_rate", statsRate, TCP_TYPE }, - { "load_scan_rate", statsRate, LOAD_TYPE }, - { "load_sample_rate", statsLoadSampleRate, LOAD_TYPE }, - { "load_length", statsLoadLength, LOAD_TYPE }, - { NULL,NULL,0 } -}; - -aStats devAiVXStats={ 6,NULL,ai_init,ai_init_record,ai_ioint_info,ai_read,NULL }; -aStats devAoVXStats={ 6,NULL,NULL,ao_init_record,NULL,ao_write,NULL }; - -static glob_vars globs[3]; -static spy_info spyinfo; -static mem_info meminfo; -static int tcpinfo; - -/* ---------------------------------------------------------------------- */ - +aiaodset devAiVXStats={ 6,NULL,ai_init,ai_init_record,ai_ioint_info,ai_read,NULL }; +aiaodset devAoVXStats={ 6,NULL,NULL,ao_init_record,NULL,ao_write,NULL }; + static long ai_init(int pass) { - int i; + int type; - if(pass) return 0; - - for(i=0;iinp.value); - pvtArea* pvt = NULL; + int type; + devPvt* pvt = NULL; + char *parm; - if(pr->inp.type!=VME_IO) - { - recGblRecordError(S_db_badField,(void*)pr, - "devAiStats (init_record) Illegal INP field"); - return S_db_badField; + if(pr->inp.type!=INST_IO) { + recGblRecordError(S_db_badField,(void*)pr, + "devAiStats (init_record) Illegal INP field"); + return S_db_badField; + } + parm = pr->inp.value.instio.string; + for(type=0; type < TOTAL_TYPES; type++) { + if(strcmp(parm,aiparmValue[type])==0) { + pvt=(devPvt*)malloc(sizeof(devPvt)); + pvt->type=type; } - - for(i=0;statsGetParms[i].name && pvt==NULL;i++) - { - if(strcmp(io->parm,statsGetParms[i].name)==0) - { - pvt=(pvtArea*)malloc(sizeof(pvtArea)); - pvt->index=i; - pvt->type=statsGetParms[i].type; - } - } - - if(pvt==NULL) - { - recGblRecordError(S_db_badField,(void*)pr, - "devAiStats (init_record) Illegal INP parm field"); - return S_db_badField; - } - + } + if(pvt==NULL) { + recGblRecordError(S_db_badField,(void*)pr, + "devAiStats (init_record) Illegal INP parm field"); + return S_db_badField; + } /* Make sure record processing routine does not perform any conversion*/ pr->linr=0; pr->dpvt=pvt; + pr->hopr = 100.0; + pr->lopr = 0.0; + pr->prec = 2; return 0; } static long ao_init_record(aoRecord* pr) { - int i; - vmeio* io = (vmeio*)&(pr->out.value); - pvtArea* pvt = NULL; + int type; + devPvt* pvt = NULL; + char *parm; - if(pr->out.type!=VME_IO) - { - recGblRecordError(S_db_badField,(void*)pr, - "devAiStats (init_record) Illegal OUT field"); - return S_db_badField; + if(pr->out.type!=INST_IO) { + recGblRecordError(S_db_badField,(void*)pr, + "devAiStats (init_record) Illegal OUT field"); + return S_db_badField; + } + parm = pr->out.value.instio.string; + for(type=0; type < TOTAL_TYPES; type++) { + if(strcmp(parm,aoparmValue[type])==0) { + pvt=(devPvt*)malloc(sizeof(devPvt)); + pvt->type=type; } - - for(i=0;statsPutParms[i].name && pvt==NULL;i++) - { - if(strcmp(io->parm,statsPutParms[i].name)==0) - { - pvt=(pvtArea*)malloc(sizeof(pvtArea)); - pvt->index=i; - pvt->type=statsPutParms[i].type; - } - } - - if(pvt==NULL) - { - recGblRecordError(S_db_badField,(void*)pr, - "devAiStats (init_record) Illegal INP parm field"); - return S_db_badField; - } - + } + if(pvt==NULL) { + recGblRecordError(S_db_badField,(void*)pr, + "devAiStats (init_record) Illegal INP parm field"); + return S_db_badField; + } /* Make sure record processing routine does not perform any conversion*/ pr->linr=0; pr->dpvt=pvt; - return 0; + return 2; } -static void load_time_callback(CALLBACK* cb) +static void wdCallback(int type) { - if(spyinfo.type==START_ME) - { - spyClkStart(spyinfo.sample_rate); - spyinfo.type=STOP_ME; - wdStart(spyinfo.wd,spyinfo.length_ticks, - (FUNCPTR)callbackRequest,(int)cb); - } - else - { - spyClkStop(); - spyinfo.type=START_ME; - read_load_stats(); - scanIoRequest(globs[LOAD_TYPE].ioscan); - } -} - -static void scan_time(int type) -{ - scanIoRequest(globs[type].ioscan); - - if(type==LOAD_TYPE) - callbackRequest(&(spyinfo.cb)); - else - scanIoRequest(globs[type].ioscan); - - if(globs[type].on) - wdStart(globs[type].wd,globs[type].rate_tick,(FUNCPTR)scan_time,type); + scanIoRequest(scan[type].ioscanpvt); + wdStart(scan[type].wd,scan[type].rate_tick, (FUNCPTR)wdCallback,type); } static long ai_ioint_info(int cmd,aiRecord* pr,IOSCANPVT* iopvt) { - pvtArea* pvt=(pvtArea*)pr->dpvt; + devPvt* pvt=(devPvt*)pr->dpvt; - if(cmd==0) /* added */ - { - if(globs[pvt->type].total++ == 0) - { - /* start a watchdog */ - wdStart(globs[pvt->type].wd,globs[pvt->type].rate_tick, - (FUNCPTR)scan_time,pvt->type); - globs[pvt->type].on=1; - } - } - else /* deleted */ - { - if(--globs[pvt->type].total == 0) - globs[pvt->type].on=0; /* stop the watchdog */ - } - - *iopvt=globs[pvt->type].ioscan; - return 0; + *iopvt=scan[pvt->type].ioscanpvt; + return 0; } static long ao_write(aoRecord* pr) { - pvtArea* pvt=(pvtArea*)pr->dpvt; - statsPutParms[pvt->index].func(pvt->type,(unsigned long)pr->val); - return 0; + devPvt* pvt=(devPvt*)pr->dpvt; + + if(!pvt) return(0); + scan[pvt->type].rate_tick = (int)pr->val * sysClkRateGet(); + return 0; } static long ai_read(aiRecord* pr) { - unsigned long val; - double aver; - pvtArea* pvt=(pvtArea*)pr->dpvt; - - statsGetParms[pvt->index].func(&val,&aver); - - if(pvt->type==LOAD_TYPE) - pr->val=aver; - else - pr->val=(double)val; + double value = 0.0; + devPvt* pvt=(devPvt*)pr->dpvt; + if(!pvt) return(0); + switch(pvt->type) { + case MEMORY_TYPE: value = getMemory(); break; + case CPU_TYPE: value = getCpu(); break; + case FD_TYPE: value = getFd(); break; + default: recGblRecordError(S_db_badField,(void*)pr,"Illegal type"); + } + pr->val=value; pr->udf=0; return 2; /* don't convert */ } + +/*Memory statistics*/ +#include +static MEM_PART_STATS meminfo; /* -------------------------------------------------------------------- */ -static void read_mem_stats(void) +static double getMemory(void) { - time_t nt; - time(&nt); + double nfree,nalloc; - if((nt-globs[MEMORY_TYPE].last_read_sec)>=globs[MEMORY_TYPE].rate_sec) - { -#ifdef VX52 - if(memPartInfoGet(memSysPartId,&(meminfo.m_stat))==OK) - globs[MEMORY_TYPE].last_read_sec=nt; -#else - meminfo.m_stat.numBytesFree= - 2*(memSysPartId->totalWords-memSysPartId->curWordsAllocated); - meminfo.m_stat.numBytesAlloc=2*memSysPartId->curWordsAllocated; - meminfo.m_stat.numBlocksFree=0; - meminfo.m_stat.maxBlockSizeFree=0; - meminfo.m_stat.numBlocksAlloc=0; - globs[MEMORY_TYPE].last_read_sec=nt; -#endif + memPartInfoGet(memSysPartId,&(meminfo)); + nfree = meminfo.numBytesFree; + nalloc = meminfo.numBytesAlloc; + return(100.0 * nalloc/(nalloc + nfree)); +} + + +/* fd usage */ +#include +static double getFd() +{ + int i,tot; + + for(tot=0,i=0;istartSem,WAIT_FOREVER); + pcpuUsage->ticksNow=0; + pcpuUsage->nBurnNow=0; + tickStart = tickGet(); + for(i=0; i< pcpuUsage->nBurnNoContention; i++) { + cpuBurn(); + pcpuUsage->ticksNow = tickGet() - tickStart; + ++pcpuUsage->nBurnNow; } + tickEnd = tickGet(); + pcpuUsage->didNotComplete = FALSE; + pcpuUsage->ticksNow = tickEnd - tickStart; + } } -static void read_load_stats(void) + +static double getCpu() { - WIND_TCB* wt; - int task_list[MAX_TASK]; - int tot_task,i; - unsigned long tot_ticks=0; + if(pcpuUsage->didNotComplete && pcpuUsage->nBurnNow==0) { + pcpuUsage->usage = 100.0; + } else { + double temp; + double ticksNow,nBurnNow; - if((tot_task=taskIdListGet(task_list,MAX_TASK))==0) return; - - for(i=0;itaskTicks+wt->taskIncTicks; - } - - spyinfo.load_average=(double)(tot_ticks)/(double)(spyinfo.length_ticks); + ticksNow = (double)pcpuUsage->ticksNow; + nBurnNow = (double)pcpuUsage->nBurnNow; + ticksNow *= (double)pcpuUsage->nBurnNoContention/nBurnNow; + temp = ticksNow - (double)pcpuUsage->ticksNoContention; + temp = 100.0 * temp/ticksNow; + if(temp<0.0 || temp>100.0) temp=0.0;/*take care of tick overflow*/ + pcpuUsage->usage = temp; + } + pcpuUsage->didNotComplete = TRUE; + semGive(pcpuUsage->startSem); + return(pcpuUsage->usage); } -static void read_tcp_stats(void) +static void cpuUsageInit(void) { - time_t nt; - int i,tot; - time(&nt); + unsigned long tickStart,tickNow; + int nBurnNoContention=0; + int ticksToWait; - if((nt-globs[TCP_TYPE].last_read_sec)>=globs[TCP_TYPE].rate_sec) - { - /* this is bogus */ - /* tcpinfo=tcpstat.tcps_connects- - (tcpstat.tcps_closed-tcpstat.tcps_connattempt); */ - - for(tot=0,i=0;i=ticksToWait) break; + cpuBurn(); + nBurnNoContention++; + } + pcpuUsage->nBurnNoContention = nBurnNoContention; + pcpuUsage->startSem = semBCreate (SEM_Q_FIFO,SEM_EMPTY); + pcpuUsage->ticksNoContention = ticksToWait; + pcpuUsage->didNotComplete = TRUE; + taskSpawn("cpuUsageTask",255,VX_FP_TASK,1000,(FUNCPTR)cpuUsageTask, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }