EPICS Internals Nick Rees Outline • • • • • Introduction Libraries and modules Data Structures Process types Examples of processing INTRODUCTION Background • When I accepted this challenge I thought there was an EPICS internals talk I could copy. – It turns out there wasn’t. • I decided the aim of the talk was to provide EPICS developers with a vague idea of what to expect to see when they look at a running EPICS system. – It turns out this wasn’t that easy – It also was very difficult to do this in 40 minutes. – By focussing on this I missed out more than I covered. • The result is something which even I find a bit difficult to understand (and I wrote the talk), so I don’t know about the rest of you. – For the non-programmers out there, good luck. What is EPICS core? What is EPICS core? • 109 directories • 1567 files, 239241 lines of all types – – – – 281 files, 93451 lines of C 203 files, 49807 lines of C++ 329 files, 34985 lines of header files 28 files, 14964 lines of html • Over 20 years of development by a competent world-wide team. • Not an enormous body of code. • A pretty reliable body of code that does what it is designed to do. • A reasonably useful, flexible and portable tool What isn’t EPICS core? • • • • • • Consistently designed Designed Buzzword compliant, or even object-oriented Well understood by lots of people. Stylistically consistent. Pretty, elegant or pure Another view • The Application Developers guide states: – “EPICS consists of a set of core software and a set of optional components. The core software, i.e. the components of EPICS without which EPICS would not function, are: • • • • • • Channel Access - Client and Server software IOC Database Scanners Monitors Database Definition Tools Source/Release” • This talk focuses on the first 4 points, with most emphasis on the IOC Database. LIBRARIES AND MODULES Helper libraries • If you write EPICS base software you must know, understand and use the basic helper libraries it provides. For example: – ellLib – doubly linked list library identical to VxWorks listLib – gpHash – efficient hashes – freeList – malloc replacement giving efficient handling of fixed size blocks. – epicsRingBytes – ring buffers. – epicsThread – thead creation and control – epicsMutex – OS independent mutexes – etc... Essential EPICS code • dbStaticLib.c – Static database access routines • Used during EPICS development or before iocInit. • dbAccess.c – Run-time database access routines. – dbNameToAddr, dbPutField, dbPut, dbGetField, dbGet, dbProcess etc. • dbConvert.c – Type conversions • camessage.c – Channel Access server function table implementations. • dbEvent.c – Implements database event handling • etc..... What EPICS libraries are there? • Common – – – – – Cap5 ca cas Com gdd • Host and IOC: – – – – asHost asIoc dbStaticHost dbStaticIoc • IOC only – – – – – – – – dbIoc dbtoolsIoc miscIoc recIoc registryIoc rsrvIoc softDevIoc testDevIoc EPICS library dependencies Conclusion • If you want to know more, consult the source. DATA STRUCTURES dbBase.h • Basic database data structures are in dbBase.h • These define the structures that hold the database. • The are defined in the dbd and db files, and created by dbStaticLib. • At run-time, they are accessed by dbAccess routines. dbBase.h record tree • dbBase └ recordTypeList └ dbRecordType └ recordList └ dbRecordNode └ name └ precord └ dset └ lset etc. └ ... └ deviceList └ ... └ dbFldDes[] └ rset └ ... dbBase.h tree dbBase definition typedef struct dbBase { ELLLIST menuList; ELLLIST recordTypeList; ELLLIST drvList; ELLLIST registrarList; ELLLIST functionList; ELLLIST variableList; ELLLIST bptList; void *pathPvt; struct dbPvd *ppvd; struct gphPvt *pgpHash; short ignoreMissingMenus; short loadCdefs; }dbBase; dbRecordType definition typedef struct dbRecordType { ELLNODE node; ELLLIST attributeList; /*LIST of attributes*/ ELLLIST recList; /*LIST of sorted dbRecordNodes*/ ELLLIST devList; /*LIST of device support*/ ELLLIST cdefList; /*LIST of Cdef text items*/ char *name; short no_fields; /* number of fields defined */ short no_prompt; /* number of fields to configure*/ short no_links; /* number of links */ short no_aliases; /* number of aliases in recList */ short *link_ind; /* addr of array of ind in papFldDes*/ char **papsortFldName;/* ptr to array of ptr to fld names*/ short *sortFldInd; /* addr of array of ind in papFldDes*/ dbFldDes *pvalFldDes; /*pointer dbFldDes for VAL field*/ short indvalFlddes; /*ind in papFldDes*/ dbFldDes **papFldDes; /* ptr to array of ptr to fldDes*/ /*The following are only available on run time system*/ struct rset *prset; int rec_size; /*record size in bytes */ }dbRecordType; dbRecordNode definition typedef struct dbRecordNode { ELLNODE node; void *precord; char *recordname; ELLLIST infoList; /*LIST of info nodes*/ int flags; }dbRecordNode; Record (dbCommon) definition typedef struct dbCommon { char name[61]; /* Record Name */ char desc[41]; /* Descriptor */ char asg[29]; /* Access Security Group */ epicsEnum16 scan; /* Scan Mechanism */ epicsEnum16 pini; /* Process at iocInit */ epicsInt16 phas;/* Scan Phase */ epicsInt16 evnt; /* Event Number */ epicsInt16 tse; /* Time Stamp Event */ DBLINK tsel; /* Time Stamp Link */ epicsEnum16 dtyp; /* Device Type */ epicsInt16 disv; /* Disable Value */ epicsInt16 disa; /* Disable */ DBLINK sdis; /* Scanning Disable */ epicsMutexId mlok; /* Monitor lock */ ELLLIST mlis; /* Monitor List */ epicsUInt8 disp; /* Disable putField */ epicsUInt8 proc; /* Force Processing */ epicsEnum16 stat; /* Alarm Status */ epicsEnum16 sevr; /* Alarm Severity */ epicsEnum16 nsta; /* New Alarm Status */ epicsEnum16 nsev; /* New Alarm Severity */ epicsEnum16 acks; /* Alarm Ack Severity */ epicsEnum16 ackt; /* Alarm Ack Transient */ epicsEnum16 diss; /* Disable Alarm Sevrty */ epicsUInt8 lcnt; /* Lock Count */ epicsUInt8 pact; /* Record active */ epicsUInt8 putf; /* dbPutField process */ epicsUInt8 rpro; /* Reprocess */ struct asgMember *asp; /* Access Security Pvt */ struct putNotify *ppn; /* addr of PUTNOTIFY */ struct putNotifyRecord *ppnr; struct scan_element *spvt; /* Scan Private */ struct rset *rset; /* Address of RSET */ struct dset *dset;/* DSET address */ void *dpvt; /* Device Private */ struct dbRecordType *rdes; /* dbRecordType */ struct lockRecord *lset; /* Lock Set */ epicsEnum16 prio; /* Scheduling Priority */ epicsUInt8 tpro; /* Trace Processing */ char bkpt; /* Break Point */ epicsUInt8 udf; /* Undefined */ epicsTimeStamp time; /* Time */ DBLINK flnk; /* Forward Process Link */ } dbCommon; dbAddr definition • Virtually all runtime access to the database is via a dbAddr handle. typedef struct dbAddr { struct dbCommon *precord; /* address of record void *pfield; /* address of field struct dbFldDes *pfldDes; /* address of fldDes long no_elements; /* number of elements short field_type; /* type of field short field_size; /* size of the field short special; /* special processing short dbr_field_type; /* request type /* DBR_STRING,...,DBR_ENUM,DBR_NOACCESS } dbAddr; */ */ */ */ */ */ */ */ */ PROCESS TYPES EPICS and threads • A typical C/C++ programmer’s first reaction is that EPICS has lots of threads • However, they all have there uses, and once you understand them they are not so confusing. • The need arises from the need to have nonblocking processing and strict priorities. • It is actually simpler and more efficient than a lot of select() calls. EPICS Threads Name cbHigh timerQueue scanOnce scan0.1 scan0.2 cbMedium scan0.5 scan1 scan2 scan5 scan10 cbLow CAC-event dbCaLink CAS-client CAS-event CAS-TCP CAS-beacon CAS-UDP errlog taskwd Priority 71 70 70 66 65 64 64 63 62 61 60 59 51 50 20 19 18 17 16 10 10 • IOC management: – timerQueue – taskwd – errlog • Database processing: – cb* – scan* • Channel Access: – CAS-* – CAC-event – dbCaLink • Plus various driver threads... IOC management threads • timerQueue – Implements all EPICS base delays • taskwd – Monitors tasks for any suspensions • errlog – Handles asynchronous processing of log messages. – Forwards log messages to the console or to files at low priority. Database processing threads • cb* – callback tasks – used for event and I/O Interrupt scanned records. – Calls any specified function asynchronously – 3 priorities (low, medium high) • scan* – periodic scan tasks – Calls record process() routines at regular intervals. – Higher scan rates have higher priorities. • + all driver threads – Every asyn port has its own thread Channel Access threads • CAS-* – Channel access server tasks • • • • CAS-UDP listens for channel lookups CAS-TCP listens for TCP connections CAS-beacon looks for CA beacons One CAS-client and CAS-event task per client • dbCaLink – Channel Access client that processes CA requests on behalf of the database. • CAC-event – Handles the CA client callback events (I think) EXAMPLES OF PROCESSING Example sequence diagram Task name C filename C function Client IOC tasks IOC function calls CA server call processing • Every CA client message has a function code defined in caProto.h of the form CA_PROTO_XXXX – E.g.: CA_PROTO_WRITE • This will convert into a function in one of the CA jump tables (tcpJumpTable or udpJumpTable). Functions are off the form xxxx_action(mp,pPayload,client) – Parameters are pointers to • CA header block • Data • client structure. – Performs: • Net to host byte swapping • Access security checks – E.g.: write_action(mp,pPayload,client) CA server call processing • xxxx_action often calls a “CA db_access” routine with underscores, which handles an ugly “old to new” type enumeration conversion – E.g.: db_put_field(pAddr, oldDataType,pPayload,count) • This calls a dbAccess routine which is part of the database code. – E.g.: dbPutField(pAddr,newDataType,pDate,count) • This locks the database and implements the appropriate action. caget from a record Channel life cycle caput to an ai record dbPutField long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, { long status = 0; dbFldDes *pfldDes = paddr->pfldDes; dbCommon *precord = paddr->precord; short dbfType = paddr->field_type; const void *pbuffer, long nRequest) /* Various sanity tests deleted here... */ dbScanLock(precord); status = dbPut(paddr, dbrType, pbuffer, nRequest); if (status == 0) { if (paddr->pfield == (void *)&precord->proc || (pfldDes->process_passive && precord->scan == 0 && dbrType < DBR_PUT_ACKT)) { if (precord->pact) { precord->rpro = TRUE; } else { /* indicate that dbPutField called dbProcess */ precord->putf = TRUE; status = dbProcess(precord); } } } dbScanUnlock(precord); return status; } dbPut dbPut(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { long special = paddr->special; short field_type = paddr->field_type; long no_elements = paddr->no_elements; if (special) dbPutSpecial(paddr, 0); if ( IS AN ARRAY ) prset->get_array_info(paddr, &dummy, &offset); dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, nRequest, no_elements, offset); if ( IS AN ARRAY ) prset->put_array_info(paddr, nRequest); if (special) dbPutSpecial(paddr,1); if ( not ( VALUE FIELD and PP )) db_post_events(precord, paddr->pfield, DBE_VALUE | DBE_LOG); return; } camonitor from an ai record caput callback from an ai record What’s the stack trace like? Call Location clone() libc.so.6 start_thread() CAS-client thread start_routine(arg) libpthread.so.0 osdThread.c:320 Socket recv call (caput starts here) camsgtask.c:123 camessage(client) Process CA messages camessage.c:2442 write_action(mp,pPayload,client) caput CA action camessage.c:810 camsgtask(pParm) db_put_field(paddr,src_type,psrc,no_elements) db_access.c:1216 dbPutField(paddr,dbrType,pbuffer,nRequest) dbAccess.c:1257 dbProcess(precord) dbAccess.c:630 process(pao) librecIoc.so writeValue(pao) aoRecord.c:537 write_ao(pao) devAoSoft.c:72 caput to an ai record Call Location clone() libc.so.6 start_thread() CAS-client thread start_routine(arg) libpthread.so.0 osdThread.c:320 Socket recv call (caput starts here) camsgtask.c:123 camessage(client) Process CA messages camessage.c:2442 write_action(mp,pPayload,client) caput CA action camessage.c:810 camsgtask(pParm) db_put_field(paddr,src_type,psrc,no_elements) db_access.c:1216 dbPutField(paddr,dbrType,pbuffer,nRequest) dbAccess.c:1257 dbProcess(precord) dbAccess.c:630 process(pao) librecIoc.so writeValue(pao) aoRecord.c:537 write_ao(pao) devAoSoft.c:72 caput callback to an ai record Call Location clone() libc.so.6 start_thread() CAS-client task libpthread.so.0 start_routine(arg) osdThread.c:282 camsgtask(pParm) camsgtask.c:123 camessage(client) camessage.c:2508 write_notify_action(mp,pPayload,client) camessage.c:1823 dbPutNotify(ppn) dbNotify.c:342 putNotifyCommon(ppn,precord) dbNotify.c:236 dbProcess(precord) dbAccess.c:643 process(precord) aiRecord.c:177 recGblFwdLink(precord) recGbl.c:267 dbNotifyCompletion(precord) dbNotify.c:422 callbackRequest(pcallback) callback.c:154 camonitor from an ai record Call Location clone() start_thread() scan1 task start_routine(arg) periodicTask(arg) scanList(psl) dbProcess(precord) process(prec) recGblFwdLink(precord) dbScanFwdLink(plink) dbScanPassive(pfrom,pto) dbProcess(precord) process(precord) monitor(prec) db_post_events(pRecord,pField,caEventMask) db_post_single_event_private(event) libc.so.6 libpthread.so.0 osdThread.c:282 dbScan.c:553 dbScan.c:658 dbAccess.c:643 calcRecord.c:126 recGbl.c:265 dbAccess.c:519 dbAccess.c:490 dbAccess.c:643 aiRecord.c:175 aiRecord.c:403 dbEvent.c:767 dbEvent.c:715 Camonitor event Call Location clone() start_thread() CAS-event task start_routine(arg) event_task(pParm) event_read(ev_que) read_reply(pArg,paddr,eventsRemaining,pfl) cas_send_bs_msg(pclient,lock_needed) send() libc.so.6 libpthread.so.0 osdThread.c:282 dbEvent.c:941 dbEvent.c:865 camessage.c:627 caserverio.c:63 libc.so.6 caget from an ai record Call Location clone() libc.so.6 start_thread() libpthread.so.0 CAS-client task start_routine(arg) osdThread.c:282 camsgtask(pParm) camsgtask.c:123 camessage(client) camessage.c:2508 read_notify_action(mp,pPayload,client) camessage.c:757 read_reply(pArg,paddr,eventsRemaining,pfl) camessage.c:557 db_get_field(paddr,buffer_type,pbuffer,no_elements,pfl) db_access.c:543 dbGetField(paddr,dbrType,pbuffer,options,nRequest,pflin) dbAccess.c:989 dbGet(paddr,dbrType,pbuffer,options,nRequest,pflin) dbAccess.c:1009 getOptions(paddr,poriginal,options,pflin) dbAccess.c:393 dbFastGetConvertRoutine[field_type][dbrType] dbAccess.c:1069 (localAddr.pfield, pbuffer, &localAddr) The end • What did I miss? – – – – – – – – – – – – IOC initialisation Database definition Static database access dbStatic Test facilities Access security IOC shell Registry OSI libCom Build rules Record, device and driver support • Basically everything..... Resources • EPICS Application Developers Guide • Channel Access Protocol Specification – http://epics.cosylab.com/cosyjava/JCACommon/Documentation/CAproto.html • Training slides on EPICS web site • Eclipse is your friend... • “module load ddd” gives you the ddd graphical debugger.