diff --git a/src/dev/Makefile.Vx b/src/dev/Makefile.Vx index e2666b597..e5372d667 100644 --- a/src/dev/Makefile.Vx +++ b/src/dev/Makefile.Vx @@ -14,6 +14,7 @@ SRCS.c += ../devAiDvx2502.c SRCS.c += ../devAiKscV215.c SRCS.c += ../devAiSoft.c SRCS.c += ../devAiSoftRaw.c +SRCS.c += ../devAiStats.c SRCS.c += ../devAiSymb.c SRCS.c += ../devAiTestAsyn.c SRCS.c += ../devAiXy566Di.c diff --git a/src/dev/devAiStats.c b/src/dev/devAiStats.c new file mode 100644 index 000000000..70566323f --- /dev/null +++ b/src/dev/devAiStats.c @@ -0,0 +1,639 @@ + +/* devAiStats.c - Device Support Routines for vxWorks statistics */ +/* + * 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$ + */ + +/* + add the following to devSup.ascii: + + "ai" VME_IO "devAiStats" "VX stats" + "ao" VME_IO "devAoStats" "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") + } + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef VX52 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* length in seconds */ +#define HARDCODED_LOAD_LENGTH 15 +/* rate in times per second */ +#define SPY_SAMPLING_RATE 80 + +#define MAX_TASK 70 +#define WORD_SIZE 2 + +#define MEMORY_TYPE 0 +#define LOAD_TYPE 1 +#define TCP_TYPE 2 +#define TOTAL_TYPES 3 + +#define START_ME 0 +#define STOP_ME 1 + +typedef void (*statGetFunc)(unsigned long*,double*); +typedef void (*statPutFunc)(int,unsigned long); +typedef struct vmeio vmeio; + +#ifdef VX52 +typedef MEM_PART_STATS MEM_PART_STATS_P; +#else +typedef struct +{ + 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 + +struct aStats +{ + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + 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; + +static long ai_init(int pass); +static long ai_init_record(aiRecord*); +static long ai_read(aiRecord*); +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 devAiStats={ 6,NULL,ai_init,ai_init_record,ai_ioint_info,ai_read,NULL }; +aStats devAoStats={ 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; + +/* ---------------------------------------------------------------------- */ + +static long ai_init(int pass) +{ + int i; + + if(pass) return 0; + + for(i=0;iinp.value); + pvtArea* pvt = NULL; + + if(pr->inp.type!=VME_IO) + { + recGblRecordError(S_db_badField,(void*)pr, + "devAiStats (init_record) Illegal INP field"); + return S_db_badField; + } + + 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; + } + + /* Make sure record processing routine does not perform any conversion*/ + pr->linr=0; + pr->dpvt=pvt; + return 0; +} + +static long ao_init_record(aoRecord* pr) +{ + long status; + int i; + vmeio* io = (vmeio*)&(pr->out.value); + pvtArea* pvt = NULL; + + if(pr->out.type!=VME_IO) + { + recGblRecordError(S_db_badField,(void*)pr, + "devAiStats (init_record) Illegal OUT field"); + return S_db_badField; + } + + 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; + } + + /* Make sure record processing routine does not perform any conversion*/ + pr->linr=0; + pr->dpvt=pvt; + return 0; +} + +static void load_time_callback(CALLBACK* cb) +{ + 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); +} + +static long ai_ioint_info(int cmd,aiRecord* pr,IOSCANPVT* iopvt) +{ + pvtArea* pvt=(pvtArea*)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; +} + +static long ao_write(aoRecord* pr) +{ + pvtArea* pvt=(pvtArea*)pr->dpvt; + statsPutParms[pvt->index].func(pvt->type,(unsigned long)pr->val); + 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; + + pr->udf=0; + return 2; /* don't convert */ +} + +/* -------------------------------------------------------------------- */ + +static void read_mem_stats(void) +{ + time_t nt; + time(&nt); + + 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 + } +} + +static void read_load_stats(void) +{ + WIND_TCB* wt; + int task_list[MAX_TASK]; + int tot_task,i; + unsigned long tot_ticks=0; + + 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); +} + +static void read_tcp_stats(void) +{ + time_t nt; + int i,tot; + time(&nt); + + 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