From 8c60e7e2cb40efc1f1ea300b03bd327b6bd500d0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 001/204] track INP/OUT field in struct dbFldDes avoid string comparisons --- src/ioc/db/dbAccess.c | 3 +-- src/ioc/dbStatic/dbBase.h | 1 + src/ioc/dbStatic/dbLexRoutines.c | 2 ++ src/ioc/dbStatic/dbStaticLib.c | 3 +-- src/ioc/misc/iocInit.c | 3 +-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index bf0f00dec..2abbe8ea2 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -942,8 +942,7 @@ static long dbPutFieldLink(DBADDR *paddr, if (status) goto finish; isDevLink = ellCount(&precord->rdes->devList) > 0 && - (strcmp(pfldDes->name, "INP") == 0 || - strcmp(pfldDes->name, "OUT") == 0); + pfldDes->isDevLink; dbLockSetGblLock(); dbLockSetRecordLock(precord); diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h index 691953442..2aa9a6084 100644 --- a/src/ioc/dbStatic/dbBase.h +++ b/src/ioc/dbStatic/dbBase.h @@ -81,6 +81,7 @@ typedef struct dbFldDes{ /* field description */ ctType base; /*base for integer to string conversions*/ short promptgroup; /*prompt, i.e. gui group */ short interest; /*interest level */ + short isDevLink; /* true for INP/OUT fields */ asLevel as_level; /*access security level */ char *initial; /*initial value */ /*If (DBF_MENU,DBF_DEVICE) ftPvt is (pdbMenu,pdbDeviceMenu) */ diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index cf2406556..063701751 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -484,6 +484,8 @@ static void dbRecordtypeFieldHead(char *name,char *type) allocTemp(pdbFldDes); pdbFldDes->name = epicsStrDup(name); pdbFldDes->as_level = ASL1; + pdbFldDes->isDevLink = strcmp(pdbFldDes->name, "INP")==0 || + strcmp(pdbFldDes->name, "OUT")==0; for(i=0; ifield_type = pamapdbfType[i].value; diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 43a371b7a..9f953a673 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2106,8 +2106,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) goto done; } - if (strcmp(pflddes->name, "INP") == 0 || - strcmp(pflddes->name, "OUT") == 0) { + if (pflddes->isDevLink) { status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */ if (status) { errMessage(status,"in dbPutString from setLinkType"); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 73f8ebd00..1e224d2f5 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -484,8 +484,7 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - if (ellCount(&precord->rdes->devList) > 0 && - (strcmp(pdbFldDes->name, "INP") == 0 || strcmp(pdbFldDes->name, "OUT") == 0)) { + if (ellCount(&precord->rdes->devList) > 0 && pdbFldDes->isDevLink) { devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); if (pdevSup) { From 99626f03cd30550f3ca822fa1a0a1d4428f7e649 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 002/204] dbLink: remove struct lset and dbDb_lset unnecessary exposure of internal API --- src/ioc/db/dbLink.c | 6 ------ src/ioc/db/dbLink.h | 24 ------------------------ 2 files changed, 30 deletions(-) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index be0a3b70a..af23d7615 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -379,12 +379,6 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } -lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink, - dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, - dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, - dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, - dbDbPutValue, dbDbScanFwdLink }; - /***************************** Generic Link API *****************************/ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index ff0de3003..ee097757f 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -25,30 +25,6 @@ extern "C" { #endif -typedef struct lset { - long (*initLink)(struct link *plink, short dbfType); - long (*addLink)(struct link *plink, short dbfType); - long (*loadLink)(struct link *plink, short dbrType, void *pbuffer); - void (*removeLink)(struct link *plink); - int (*isLinkConnected)(const struct link *plink); - int (*getDBFtype)(const struct link *plink); - long (*getElements)(const struct link *plink, long *nelements); - long (*getValue)(struct link *plink, short dbrType, void *pbuffer, - epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); - long (*getControlLimits)(const struct link *plink, double *lo, double *hi); - long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); - long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, - double *hi, double *hihi); - long (*getPrecision)(const struct link *plink, short *precision); - long (*getUnits)(const struct link *plink, char *units, int unitsSize); - long (*getAlarm)(const struct link *plink, epicsEnum16 *status, - epicsEnum16 *severity); - long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); - long (*putValue)(struct link *plink, short dbrType, - const void *pbuffer, long nRequest); - void (*scanFwdLink)(struct link *plink); -} lset; - #define dbGetSevr(PLINK, PSEVERITY) \ dbGetAlarm((PLINK), NULL, (PSEVERITY)); From 470a9f9fc975dd6e7ea9383d772e7a66d2de2027 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 003/204] Introduce struct dbLinkInfo and dbParseLink() This structure holds the processed form of any link type. It is populated by dbParseLink(), which can be called w/o record locking. dbCanSetLink() checks the the parsed link, and a DBLINK field have compatible types for assignment. Record locking is required, but this does not modify the field. dbSetLink() makes the actual assignment. --- src/ioc/dbStatic/dbStaticLib.c | 295 +++++++++++++++++++++++++++++++++ src/ioc/dbStatic/dbStaticPvt.h | 31 ++++ 2 files changed, 326 insertions(+) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 9f953a673..5ca23493b 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2024,6 +2024,301 @@ static void cvtDecimalOrHexToShort(char *from,short *value) } } +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) +{ + char *pstr, *pend; + size_t len, i; + + /* Strip leading blanks and tabs */ + while (*str && (*str == ' ' || *str == '\t')) str++; + + memset(pinfo, 0, sizeof(*pinfo)); + + len = strlen(str); + pinfo->target = pstr = malloc(len+1); + if(!pstr) + return S_dbLib_outMem; + strcpy(pstr, str); + + /* Strip trailing blanks and tabs */ + for(i=len; i>0 && (pstr[i-1]==' ' || pstr[i-1]=='\t'); i--) {} + pstr[i]='\0'; + + /* check for HW link types */ + if(pstr[0]=='@') { + pinfo->ltype = INST_IO; /* can only be INST_IO */ + + /* move string up by one to overwrite '@' + * so it can be re-used later in struct instio + */ + ssize_t N = strlen(pstr)-1; + memmove(pstr, pstr+1, N); + pstr[N] = '\0'; + + return 0; + + } else if(pstr[0]=='#') { /* some other HW link type */ + int ret; + char junk = 0; + char *patstr = strchr(pstr, '@'); /* find start of parm string */ + + if(patstr) + *patstr++ = '\0'; /* isolate the parm string for later processing */ + + /* generalized extraction of ID charactor and integer pairs (eg. "#C15 S14") */ + ret = sscanf(pinfo->target, "# %c%d %c%d %c%d %c%d %c%d %c", + &pinfo->hwid[0], &pinfo->hwnums[0], + &pinfo->hwid[1], &pinfo->hwnums[1], + &pinfo->hwid[2], &pinfo->hwnums[2], + &pinfo->hwid[3], &pinfo->hwnums[3], + &pinfo->hwid[4], &pinfo->hwnums[4], + &junk); + + /* ret<0 when pattern not matched + * ret==11 when extra non-space before '@'. + * ret is odd when a number is missing + */ + if(ret<0 || ret>10 || ret%2==1) goto fail; + + if(strcmp(pinfo->hwid, "CS")==0) pinfo->ltype = VME_IO; + else if(strcmp(pinfo->hwid, "BCNAF")==0)pinfo->ltype = CAMAC_IO; + else if(strcmp(pinfo->hwid, "RMDE")==0) pinfo->ltype = RF_IO; + else if(strcmp(pinfo->hwid, "LACS")==0) pinfo->ltype = AB_IO; + else if(strcmp(pinfo->hwid, "LA")==0) pinfo->ltype = GPIB_IO; + else if(strcmp(pinfo->hwid, "LNPS")==0) pinfo->ltype = BITBUS_IO; + else if(strcmp(pinfo->hwid, "LBG")==0) pinfo->ltype = BBGPIB_IO; + else if(strcmp(pinfo->hwid, "VCS")==0) pinfo->ltype = VXI_IO; + else if(strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO; + else goto fail; + + if(patstr && pinfo->ltype!=RF_IO) { + /* move the parm string forward. + * pinfo->target will be re-used as ->parm + */ + memmove(pinfo->target, patstr, strlen(patstr)+1); + } else if(!patstr && pinfo->ltype==RF_IO) { + /* RF_IO only. the string isn't needed anymore */ + free(pinfo->target); + pinfo->target = NULL; + } else { + /* missing parm when required, or found parm when not expected */ + free(pinfo->target); + pinfo->target = NULL; + return S_dbLib_badField; + } + + return 0; + } + + /* A string is a constant if epicsStrtod or strtol consumes entire string */ + (void)epicsStrtod(pstr, &pend); + if(*pend!='\0') + (void)strtol(pstr, &pend, 0); + if(*pend=='\0') { + pinfo->ltype = CONSTANT; + return 0; /* This is a CONSTANT link */ + } + + pinfo->ltype = PV_LINK; + pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */ + if(pstr) { + *pstr++ = '\0'; /* isolate modifiers. pinfo->target is PV name only for re-use in struct pv_link */ + + /* Space seperation of modifiers isn't required, and other chars are ignored. + * Order of comparisons resolves ambiguity by checking for + * longer matches first. + * eg. "QQCPPXMSITT" is pvlOptCPP|pvlOptMS + */ + + if (strstr(pstr, "NPP")) pinfo->modifiers = 0; + else if (strstr(pstr, "CPP")) pinfo->modifiers = pvlOptCPP; + else if (strstr(pstr, "PP")) pinfo->modifiers = pvlOptPP; + else if (strstr(pstr, "CA")) pinfo->modifiers = pvlOptCA; + else if (strstr(pstr, "CP")) pinfo->modifiers = pvlOptCP; + + if (strstr(pstr, "NMS")) pinfo->modifiers |= pvlOptNMS; + else if (strstr(pstr, "MSI")) pinfo->modifiers |= pvlOptMSI; + else if (strstr(pstr, "MSS")) pinfo->modifiers |= pvlOptMSS; + else if (strstr(pstr, "MS")) pinfo->modifiers |= pvlOptMS; + + /* filter modifiers based on link type */ + switch(ftype) { + case DBF_INLINK: /* accept all */ break; + case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break; + case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break; + } + } + + return 0; +fail: + free(pinfo->target); + return S_dbLib_badField; +} + +long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) +{ + /* consume allocated string pinfo->target on failure */ + + int link_type = CONSTANT; + if(devsup) + link_type = devsup->link_type; + if(link_type==pinfo->ltype) + return 0; + switch(pinfo->ltype) { + case CONSTANT: + case PV_LINK: + if(link_type==CONSTANT || link_type==PV_LINK) + return 0; + else if(link_type==INST_IO && pinfo->target[0]!='\0') + /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ + return 0; + default: + free(pinfo->target); + pinfo->target = NULL; + return 1; + } +} + +static +void dbSetLinkConst(DBLINK *plink, dbLinkInfo *pinfo) +{ + plink->type = CONSTANT; + plink->value.constantStr = pinfo->target; + + pinfo->target = NULL; +} + +static +void dbSetLinkPV(DBLINK *plink, dbLinkInfo *pinfo) +{ + plink->type = PV_LINK; + plink->value.pv_link.pvname = pinfo->target; + plink->value.pv_link.pvlMask = pinfo->modifiers; + + pinfo->target = NULL; +} + +static +void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo) +{ + + switch(pinfo->ltype) { + case INST_IO: + plink->value.instio.string = pinfo->target; + break; + case VME_IO: + plink->value.vmeio.card = pinfo->hwnums[0]; + plink->value.vmeio.signal = pinfo->hwnums[1]; + plink->value.vmeio.parm = pinfo->target; + break; + case CAMAC_IO: + plink->value.camacio.b = pinfo->hwnums[0]; + plink->value.camacio.c = pinfo->hwnums[1]; + plink->value.camacio.n = pinfo->hwnums[2]; + plink->value.camacio.a = pinfo->hwnums[3]; + plink->value.camacio.f = pinfo->hwnums[4]; + plink->value.camacio.parm = pinfo->target; + break; + case RF_IO: + plink->value.rfio.cryo = pinfo->hwnums[0]; + plink->value.rfio.micro = pinfo->hwnums[1]; + plink->value.rfio.dataset = pinfo->hwnums[2]; + plink->value.rfio.element = pinfo->hwnums[3]; + break; + case AB_IO: + plink->value.abio.link = pinfo->hwnums[0]; + plink->value.abio.adapter = pinfo->hwnums[1]; + plink->value.abio.card = pinfo->hwnums[2]; + plink->value.abio.signal = pinfo->hwnums[3]; + plink->value.abio.parm = pinfo->target; + break; + case GPIB_IO: + plink->value.gpibio.link = pinfo->hwnums[0]; + plink->value.gpibio.addr = pinfo->hwnums[1]; + plink->value.gpibio.parm = pinfo->target; + break; + case BITBUS_IO: + plink->value.bitbusio.link = pinfo->hwnums[0]; + plink->value.bitbusio.node = pinfo->hwnums[1]; + plink->value.bitbusio.port = pinfo->hwnums[2]; + plink->value.bitbusio.signal = pinfo->hwnums[3]; + plink->value.bitbusio.parm = pinfo->target; + break; + case BBGPIB_IO: + plink->value.bbgpibio.link = pinfo->hwnums[0]; + plink->value.bbgpibio.bbaddr = pinfo->hwnums[1]; + plink->value.bbgpibio.gpibaddr = pinfo->hwnums[2]; + plink->value.bbgpibio.parm = pinfo->target; + break; + case VXI_IO: + if(strcmp(pinfo->hwid, "VCS")==0) { + plink->value.vxiio.flag=VXIDYNAMIC; + plink->value.vxiio.frame = pinfo->hwnums[0]; + plink->value.vxiio.slot = pinfo->hwnums[1]; + plink->value.vxiio.signal = pinfo->hwnums[2]; + } else if(strcmp(pinfo->hwid, "VS")==0) { + plink->value.vxiio.flag=VXISTATIC; + plink->value.vxiio.la = pinfo->hwnums[0]; + plink->value.vxiio.signal = pinfo->hwnums[1]; + } else { + cantProceed("dbSetLinkHW: logic error, unknown VXI_IO variant"); + } + plink->value.vxiio.parm = pinfo->target; + break; + + default: + cantProceed("dbSetLinkHW: logic error, unhandled link type"); + return; + } + + plink->type = pinfo->ltype; + + pinfo->target = NULL; /* now owned by link field */ +} + +long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) +{ + int ret = 0; + int link_type = CONSTANT; + + if(devsup) + link_type = devsup->link_type; + + if(link_type==CONSTANT || link_type==PV_LINK) { + switch(pinfo->ltype) { + case CONSTANT: + dbFreeLinkContents(plink); + dbSetLinkConst(plink, pinfo); break; + case PV_LINK: + dbFreeLinkContents(plink); + dbSetLinkPV(plink, pinfo); break; + default: + errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error"); + goto fail; /* can't assign HW link */ + } + + } else if(link_type==pinfo->ltype) { + dbFreeLinkContents(plink); + dbSetLinkHW(plink, pinfo); + + } else if(link_type==INST_IO && (pinfo->ltype==CONSTANT || pinfo->ltype==PV_LINK) && pinfo->target[0]!='\0') { + /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ + pinfo->target[0] = '\0'; + pinfo->ltype = INST_IO; + + dbFreeLinkContents(plink); + dbSetLinkHW(plink, pinfo); + + } else + goto fail; + + return ret; +fail: + free(pinfo->target); + pinfo->target = NULL; + return S_dbLib_badField; +} + long dbPutString(DBENTRY *pdbentry,const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index 8f8664dc1..f3e6a932e 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -36,6 +36,37 @@ char *dbRecordName(DBENTRY *pdbentry); char *dbGetStringNum(DBENTRY *pdbentry); long dbPutStringNum(DBENTRY *pdbentry,const char *pstring); +typedef struct dbLinkInfo { + short ltype; + + /* full link string for CONSTANT and PV_LINK, + * parm string for HW links*/ + char *target; + + /* for PV_LINK */ + short modifiers; + + /* HW links */ + char hwid[6]; /* one extra element for a nil */ + int hwnums[5]; +} dbLinkInfo; + +/* Parse link string. no record locks needed. + * on success caller must free pinfo->target + */ +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo); +/* Check if link type allow the parsed link value pinfo + * to be assigned to the given link. + * Record containing plink must be locked. + * Frees pinfo->target on failure. + */ +long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup); +/* Set link field. source record must be locked (target record too + * when a DB_LINK is created) + * Unconditionally takes ownership of pinfo->target + */ +long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset); + /* The following is for path */ typedef struct dbPathNode { ELLNODE node; From 480797c0e001fb6d27df8b07b5f76de16e0b6b6e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 004/204] db/test: extend dbPutLinkTest to cover dbParseLink() --- src/ioc/db/test/dbPutLinkTest.c | 122 +++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 75e01b2d7..31563466f 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -26,10 +26,128 @@ #include "xRecord.h" +#include "dbStaticPvt.h" + #include "testMain.h" +static +int testStrcmp(int expect, const char *A, const char *B) { + static const char op[] = "<=>"; + int ret = strcmp(A,B); + testOk(ret==expect, "\"%s\" %c= \"%s\"", + A, op[expect+1], B); + return ret==expect; +} + void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); +#define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}} +#define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}} +#define TEST_HW(SET, TYPE, ID, PARM, ...) {SET, {TYPE, PARM, 0, ID, {__VA_ARGS__}}} + +static const struct testParseDataT { + const char * const str; + dbLinkInfo info; +} testParseData[] = { + TEST_CONSTANT("", ""), + TEST_CONSTANT("0.1", "0.1"), + TEST_CONSTANT(" 0.2\t ", "0.2"), + + TEST_PV_LINK("0.1a", "0.1a", 0), + TEST_PV_LINK(" hello ", "hello", 0), + TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI), + TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP), + + TEST_HW("#C14 S145 @testing", VME_IO, "CS", "testing", 14, 145), + TEST_HW("#B11 C12 N13 A14 F15 @cparam", CAMAC_IO, "BCNAF", "cparam", 11, 12, 13, 14, 15), + TEST_HW("@hello world", INST_IO, "", "hello world"), + {NULL} +}; + +static void testLinkParse(void) +{ + const struct testParseDataT *td = testParseData; + dbLinkInfo info; + testDiag("link parsing"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + for(;td->str; td++) { + int i, N; + testDiag("Parse \"%s\"", td->str); + testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0); + testOk1(info.ltype==td->info.ltype); + if(td->info.target) + testStrcmp(0, info.target, td->info.target); + if(info.ltype==td->info.ltype) { + switch(info.ltype) { + case PV_LINK: + testOk1(info.modifiers==td->info.modifiers); + break; + case VME_IO: + testStrcmp(0, info.hwid, td->info.hwid); + N = strlen(td->info.hwid); + for(i=0; iinfo.hwnums[i], "%d == %d", + info.hwnums[i], td->info.hwnums[i]); + } + } + free(info.target); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + +static const char *testParseFailData[] = { + "#", + "#S", + "#ABC", + "#A0 B", + "#A0 B @", + "#A0 B C @", + "#R1 M2 D3 E4 @oops", /* RF_IO has no parm */ + "#C1 S2", /* VME_IO needs parm */ + NULL +}; + +static void testLinkFailParse(void) +{ + const char * const *td = testParseFailData; + dbLinkInfo info; + testDiag("link parsing of invalid input"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + for(;*td; td++) { + testDiag("Expect failure \"%s\"", *td); + testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + static const struct testDataT { const char * const linkstring; short linkType; @@ -387,7 +505,9 @@ static void testLinkFail(void) MAIN(dbPutLinkTest) { - testPlan(200); + testPlan(245); + testLinkParse(); + testLinkFailParse(); testCADBSet(); testHWInitSet(); testHWMod(); From 200355bc0030267431161018547180c292378fff Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 005/204] dbStatic: re-write dbPutString() to use new link parsing dbPutString() now has two behaviors. If link_type==CONSTANT and constantStr==NULL, the link has not yet been initialized. Just set the ->text field. If the link has been initialized, then use dbSetLink() to make the assignment. Eliminate special handling for DBF_DEVICE. --- src/ioc/dbStatic/dbStaticLib.c | 489 ++------------------------------- 1 file changed, 21 insertions(+), 468 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 5ca23493b..e7807bc7a 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -44,6 +44,8 @@ #include "dbStaticLib.h" #include "dbStaticPvt.h" +#include "dbCommon.h" + int dbStaticDebug = 0; static char *pNullString = ""; #define messagesize 100 @@ -84,12 +86,9 @@ static int mapDBFtoDCT[DBF_NOACCESS+1] = { /*forward references for private routines*/ static FILE *openOutstream(const char *filename); static void finishOutstream(FILE *stream); -static long setLinkType(DBENTRY *pdbentry); -static long putParmString(char **pparm,const char *pstring); static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess); static void zeroDbentry(DBENTRY *pdbentry); static char *getpMessage(DBENTRY *pdbentry); -static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname); static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length); /* internal routines*/ @@ -114,103 +113,6 @@ static void finishOutstream(FILE *stream) } } -static long setLinkType(DBENTRY *pdbentry) -{ - DBENTRY dbEntry; - dbRecordType *precordType; - devSup *pdevSup; - DBLINK *plink; - long status=0; - int link_type,ind,type; - - dbCopyEntryContents(pdbentry, &dbEntry); - status = dbFindField(&dbEntry, "DTYP"); - if (status) { - epicsPrintf("field DTYP does not exist for recordtype %s\n", - dbGetRecordTypeName(&dbEntry)); - status = S_dbLib_fieldNotFound; - goto done; - } - - precordType = dbEntry.precordType; - if (!precordType) { - status = S_dbLib_badField; - goto done; - } - - if (ellCount(&precordType->devList) == 0) goto done; - - ind = dbGetMenuIndex(&dbEntry); - if (ind == -1) { - char *pstring; - - pstring = dbGetString(&dbEntry); - if (strstr(pstring, "$(") || strstr(pstring, "${")) { - link_type = MACRO_LINK; - } else { - status = S_dbLib_badField; - goto done; - } - } else { - pdevSup = (devSup *)ellNth(&precordType->devList, ind + 1); - if (!pdevSup) { - status = S_dbLib_badField; - goto done; - } - link_type = pdevSup->link_type; - } - - plink = (DBLINK *)pdbentry->pfield; - if (plink->type == link_type) goto done; - - if (plink->text) - { - /* re-parse link text when DTYP has changed */ - char * link_text; - link_text = plink->text; - plink->text = NULL; - dbFreeLinkContents(plink); - plink->type = link_type; - dbPutString(pdbentry, link_text); - free(link_text); - goto done; - } - - type = plink->type; - if ((type == CONSTANT || type == PV_LINK || - type == PN_LINK || type == DB_LINK || type == CA_LINK) && - (link_type == CONSTANT || link_type == PV_LINK)) goto done; - - dbFreeLinkContents(plink); - plink->type = link_type; - switch (plink->type) { - case VME_IO: plink->value.vmeio.parm = pNullString; break; - case CAMAC_IO: plink->value.camacio.parm = pNullString; break; - case AB_IO: plink->value.abio.parm = pNullString; break; - case GPIB_IO: plink->value.gpibio.parm = pNullString; break; - case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break; - case INST_IO: plink->value.instio.string = pNullString; break; - case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break; - case VXI_IO: plink->value.vxiio.parm = pNullString; break; - } -done: - dbFinishEntry(&dbEntry); - return(status); -} - -static long putParmString(char **pparm,const char *pstring) -{ - if (*pparm && *pparm != pNullString) { - free(*pparm); - *pparm = pNullString; - } - if (!pstring) return 0; - pstring = strchr(pstring, '@'); - if (!pstring || !*++pstring) return 0; - *pparm = epicsStrDup(pstring); - return 0; -} - void dbFreeLinkContents(struct link *plink) { char *parm = NULL; @@ -308,35 +210,6 @@ static char *getpMessage(DBENTRY *pdbentry) return msg; } -static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname) -{ - dbFldDes *pflddes; - DBLINK *plink; - char *pname; - - dbGetFieldAddress(pdbentry); - pflddes = pdbentry->pflddes; - if(!pflddes) return(-1); - plink = (DBLINK *)pdbentry->pfield; - if(!plink) return(-1); - switch (pflddes->field_type) { - case DBF_INLINK: - case DBF_OUTLINK: - case DBF_FWDLINK: - if(plink->type != PV_LINK) return(S_dbLib_badLink); - pname = plink->value.pv_link.pvname; - if(pname) free((void *)pname); - pname = dbCalloc(strlen(pvname)+1,sizeof(char)); - plink->value.pv_link.pvname = pname; - strcpy(pname,pvname); - plink->value.pv_link.pvlMask = pvlMask; - return(0); - default: - errPrintf(-1,__FILE__, __LINE__,"Logic Error\n"); - } - return(S_dbLib_badLink); -} - /*Public only for dbStaticNoRun*/ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) { @@ -2015,15 +1888,6 @@ char * dbGetString(DBENTRY *pdbentry) return (message); } -static void cvtDecimalOrHexToShort(char *from,short *value) -{ - if(strspn(from,"0x")==2 || strspn(from,"0X")==2) { - sscanf(from,"%hi",value); - } else { - sscanf(from,"%hd",value); - } -} - long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) { char *pstr, *pend; @@ -2362,350 +2226,39 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) case DBF_FLOAT: case DBF_DOUBLE: case DBF_MENU: + case DBF_DEVICE: status = dbPutStringNum(pdbentry,pstring); break; - case DBF_DEVICE: { - DBENTRY dbEntry; - char *name; - - status = dbPutStringNum(pdbentry, pstring); - if (status) return status; - - name = dbGetRelatedField(pdbentry); - if (!name) return 0; - - dbCopyEntryContents(pdbentry, &dbEntry); - status = dbFindField(&dbEntry, name); - if (!status) - status = setLinkType(&dbEntry); - dbFinishEntry(&dbEntry); - return status; - } - case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: { - DBLINK *plink; - char string[80]; - char *pstr = string; + dbLinkInfo link_info; + DBLINK *plink = (DBLINK *)pfield; - if (!pfield) - return S_dbLib_fieldNotFound; + status = dbParseLink(pstring, pflddes->field_type, &link_info); - plink = (DBLINK *)pfield; - dbFreeLinkContents(plink); - if (stringHasMacro) { - plink->type = MACRO_LINK; - plink->value.macro_link.macroStr = epicsStrDup(pstring); - goto done; - } + if(status==0 && plink->type==CONSTANT && plink->value.constantStr==NULL) { + /* links not yet initialized by dbInitRecordLinks() */ + free(plink->text); + plink->text = epicsStrDup(pstring); + free(link_info.target); - if (pflddes->isDevLink) { - status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */ - if (status) { - errMessage(status,"in dbPutString from setLinkType"); - return status; - } - /* store link text in case DTYP changes later */ - plink->text = malloc(strlen(pstring) + 1); - if (plink->text) - strcpy(plink->text, pstring); - } - if (strlen(pstring) >= sizeof(string)) { - status = S_dbLib_badField; - errMessage(status, - "dbPutString received a string that is too long"); - return status; - } - strcpy(pstr, pstring); - /* Strip leading blanks and tabs */ - while (*pstr && (*pstr == ' ' || *pstr == '\t')) pstr++; - /* Strip trailing blanks and tabs */ - if (pstr) { - int ind; + } else if(status==0) { + /* assignment after init (eg. autosave restore) */ + struct dbCommon *prec = pdbentry->precnode->precord; + devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1); + status = dbCanSetLink(plink, &link_info, devsup); + if(status) + status = dbSetLink(plink, &link_info, devsup); + } - for (ind = (int) strlen(pstr) - 1; ind >= 0; ind--) { - if (pstr[ind] != ' ' && pstr[ind] != '\t') break; - pstr[ind] = '\0'; - } - } - if (!pstr || !*pstr) { - if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry); - if (plink->type != CONSTANT) return S_dbLib_badField; - free((void *)plink->value.constantStr); - plink->value.constantStr = NULL; - goto done; - } - switch (plink->type) { - case CONSTANT: - case PV_LINK: { - short ppOpt = 0; - short msOpt = 0; - char *end; - char *enddouble; - char *endlong; - - /* Check first to see if string is a constant*/ - /*It is a string if epicsStrtod or strtol eats entire string*/ - /*leading and trailing blanks have already been stripped*/ - (void) epicsStrtod(pstr, &enddouble); - (void) strtol(pstr, &endlong, 0); - - if (*enddouble == 0 || *endlong == 0) { - if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry); - if (!plink->value.constantStr || - strlen(plink->value.constantStr) < strlen(pstr)) { - free(plink->value.constantStr); - plink->value.constantStr = - dbCalloc(strlen(pstr) + 1, sizeof(char)); - } - strcpy(plink->value.constantStr, pstr); - goto done; - } - - if (plink->type==CONSTANT) dbCvtLinkToPvlink(pdbentry); - end = strchr(pstr,' '); - if (end) { - switch (pflddes->field_type) { - case DBF_INLINK: { - if (strstr(end, "NPP")) ppOpt = 0; - else if (strstr(end, "CPP")) ppOpt = pvlOptCPP; - else if (strstr(end, "PP")) ppOpt = pvlOptPP; - else if (strstr(end, "CA")) ppOpt = pvlOptCA; - else if (strstr(end, "CP")) ppOpt = pvlOptCP; - else ppOpt = 0; - if (strstr(end, "NMS")) msOpt = pvlOptNMS; - else if (strstr(end, "MSI")) msOpt = pvlOptMSI; - else if (strstr(end, "MSS")) msOpt = pvlOptMSS; -/*must be the last one:*/ else if (strstr(end, "MS")) msOpt = pvlOptMS; - else msOpt = 0; - *end = 0; - } - break; - - case DBF_OUTLINK: { - if (strstr(end, "NPP")) ppOpt = 0; - else if(strstr(end, "PP")) ppOpt = pvlOptPP; - else if(strstr(end, "CA")) ppOpt = pvlOptCA; - else ppOpt = 0; - if (strstr(end, "NMS")) msOpt = pvlOptNMS; - else if(strstr(end, "MSI")) msOpt = pvlOptMSI; - else if(strstr(end, "MSS")) msOpt = pvlOptMSS; -/*must be the last one:*/ else if(strstr(end, "MS")) msOpt = pvlOptMS; - else msOpt = 0; - *end = 0; - } - break; - - case DBF_FWDLINK: { - if (strstr(end, "NPP")) ppOpt = 0; - else if (strstr(end, "CA")) ppOpt = pvlOptCA; - else ppOpt = 0; - msOpt = 0; - *end = 0; - } - break; - - default: - epicsPrintf("dbPutString: Logic Error\n"); - } - } - status = putPvLink(pdbentry,ppOpt|msOpt,pstr); - goto done; - } - /*break; is unnecessary*/ - case VME_IO: { - char *end; - - if (!(end = strchr(pstr,'#'))) return S_dbLib_badField; - pstr = end + 1; - if (!(end = strchr(pstr,'C'))) return S_dbLib_badField; - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vmeio.card); - if (!(end = strchr(pstr,'S'))) return S_dbLib_badField; - pstr = end + 1; - cvtDecimalOrHexToShort(pstr, &plink->value.vmeio.signal); - status = putParmString(&plink->value.vmeio.parm, pstr); - } - break; - - case CAMAC_IO: { - char *end; - - if (!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if (!(end = strchr(pstr,'B'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.b); - if (!(end = strchr(pstr,'C'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.c); - if (!(end = strchr(pstr,'N'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.n); - if (!(end = strchr(pstr,'A'))) { - plink->value.camacio.a = 0; - } else { - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.a); - } - if (!(end = strchr(pstr,'F'))) { - plink->value.camacio.f = 0; - } else { - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.f); - } - status = putParmString(&plink->value.camacio.parm,pstr); - } - break; - - case RF_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'R'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.cryo); - if(!(end = strchr(pstr,'M'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.micro); - if(!(end = strchr(pstr,'D'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.dataset); - if(!(end = strchr(pstr,'E'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.element); - } - break; - case AB_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.link); - if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.adapter); - if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.card); - if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.signal); - status = putParmString(&plink->value.abio.parm,pstr); - } - break; - case GPIB_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.link); - if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.addr); - status = putParmString(&plink->value.gpibio.parm,pstr); - } - break; - case BITBUS_IO: { - /* jbk - the bbgpib struct uses unsigned char's instead - of short, so read values into short and then convert */ - - char *end; - short tmp_val; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.link=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'N'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.node=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'P'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.port=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.signal=(unsigned char)tmp_val; - status = putParmString(&plink->value.bitbusio.parm,pstr); - } - break; - case BBGPIB_IO: { - /* jbk - the bbgpib struct uses unsigned char's instead - of short, so read values into short and then convert */ - - char *end; - short tmp_val; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bbgpibio.link=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'B'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bbgpibio.bbaddr=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'G'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bbgpibio.gpibaddr=(unsigned char)tmp_val; - status = putParmString(&plink->value.bbgpibio.parm,pstr); - } - break; - case VXI_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - memset((char *)&plink->value.vxiio,0,sizeof(struct vxiio)); - plink->value.vxiio.parm = pNullString; - if(!((end = strchr(pstr,'C'))&&(end < strchr(pstr,'@')) )) { - plink->value.vxiio.flag = VXISTATIC; - if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.la); - } else { - plink->value.vxiio.flag = VXIDYNAMIC; - if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.frame); - if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.slot); - } - if((end = strchr(pstr,'S'))) { - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.signal); - } else { - plink->value.vxiio.signal = 0; - } - status = putParmString(&plink->value.vxiio.parm,pstr); - } - break; - case INST_IO: { - status = putParmString(&plink->value.instio.string, pstr); - } - break; - } - } + } break; default: return S_dbLib_badField; } -done: + if (!status && strcmp(pflddes->name, "VAL") == 0) { DBENTRY dbentry; From 33804bd7ead99c23549b3fa43504366c12900bec Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 006/204] iocInit: initialize links in iocBuild Done before any driver, record, or device supports run. Also before the hooks used by autosave. --- src/ioc/dbStatic/dbStaticLib.c | 59 ++++++++++++++++++++++++++++++++++ src/ioc/dbStatic/dbStaticPvt.h | 2 ++ src/ioc/misc/iocInit.c | 23 +++++++++---- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index e7807bc7a..ca7dfff83 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1888,6 +1888,65 @@ char * dbGetString(DBENTRY *pdbentry) return (message); } +long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) +{ + short i; + + for (i=0; ino_links; i++) { + dbLinkInfo link_info; + dbFldDes *pflddes = rtyp->papFldDes[rtyp->link_ind[i]]; + DBLINK *plink = (DBLINK *)(((char *)prec) + pflddes->offset); + devSup *devsup = NULL; + + /* link fields are zero'd on allocation. + * so are effectively CONSTANT, but with constantStr==NULL. + * Here we initialize them to have the correct link type, + * with zero values and empty (but non-NULL) strings. + */ + + if(pflddes->isDevLink) { + devsup = (devSup *)ellNth(&rtyp->devList, prec->dtyp+1); + } + if(devsup) + plink->type = devsup->link_type; + else + plink->type = CONSTANT; + + switch (plink->type) { + case CONSTANT: plink->value.constantStr = callocMustSucceed(1, 1, "init CONSTANT link"); break; + case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break; + case VME_IO: plink->value.vmeio.parm = pNullString; break; + case CAMAC_IO: plink->value.camacio.parm = pNullString; break; + case AB_IO: plink->value.abio.parm = pNullString; break; + case GPIB_IO: plink->value.gpibio.parm = pNullString; break; + case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break; + case INST_IO: plink->value.instio.string = pNullString; break; + case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break; + case VXI_IO: plink->value.vxiio.parm = pNullString; break; + } + + if(!plink->text) + continue; + + if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { + /* This was already parsed once when ->text was set. + * Any syntax error messages were printed at that time. + */ + + } else if(dbCanSetLink(plink, &link_info, devsup)!=0) { + errlogPrintf("Error: %s.%s: can't initialize link type %d with \"%s\" (type %d)\n", + prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + + } else if(dbSetLink(plink, &link_info, devsup)) { + errlogPrintf("Error: %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n", + prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + } + free(plink->text); + plink->text = NULL; + } + return 0; +} + long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) { char *pstr, *pend; diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index f3e6a932e..c249e73bb 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -51,6 +51,8 @@ typedef struct dbLinkInfo { int hwnums[5]; } dbLinkInfo; +long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); + /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 1e224d2f5..32b7fc524 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -56,6 +56,8 @@ #include "dbCommon.h" #include "dbLock.h" #include "dbAccess.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" #include "recGbl.h" #include "dbNotify.h" #include "dbCa.h" @@ -82,7 +84,14 @@ static void initDatabase(void); static void initialProcess(void); static void exitDatabase(void *dummy); - +/* + * Iterate through all record instances (but not aliases), + * calling a function for each one. + */ +typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user); + +static void iterateRecords(recIterFunc func, void *user); + /* * Initialize EPICS on the IOC. */ @@ -122,8 +131,14 @@ static int iocBuild_1(void) return 0; } +static void prepareLinks(dbRecordType *rtyp, dbCommon *prec, void *junk) +{ + dbInitRecordLinks(rtyp, prec); +} + static int iocBuild_2(void) { + iterateRecords(prepareLinks, NULL); initHookAnnounce(initHookAfterCaLinkInit); initDrvSup(); @@ -413,12 +428,6 @@ static void finishDevSup(void) } } } - -/* - * Iterate through all record instances (but not aliases), - * calling a function for each one. - */ -typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user); static void iterateRecords(recIterFunc func, void *user) { From c5da1e3f89b6a54219e2096aea5f3678cbbf83ec Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 007/204] dbAccess: update dbPutFieldLink() to use dbParseLink()/dbSetLink() Now returns early for invalid link strings w/o modifying the field, or detaching device support. --- src/ioc/db/dbAccess.c | 46 +++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 2abbe8ea2..cd69c97ec 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -35,6 +35,7 @@ #include "errMdef.h" #define epicsExportSharedSymbols #include "dbStaticLib.h" +#include "dbStaticPvt.h" #include "dbBase.h" #include "link.h" #include "dbFldTypes.h" @@ -909,15 +910,17 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) { static long dbPutFieldLink(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { + dbLinkInfo link_info; + DBADDR *pdbaddr = NULL; dbCommon *precord = paddr->precord; dbFldDes *pfldDes = paddr->pfldDes; long special = paddr->special; struct link *plink = (struct link *)paddr->pfield; const char *pstring = (const char *)pbuffer; - DBENTRY dbEntry; struct dsxt *old_dsxt = NULL; struct dset *new_dset = NULL; struct dsxt *new_dsxt = NULL; + devSup *new_devsup = NULL; long status; int isDevLink; short scan; @@ -936,16 +939,30 @@ static long dbPutFieldLink(DBADDR *paddr, return S_db_badDbrtype; } - dbInitEntry(pdbbase, &dbEntry); - status = dbFindRecord(&dbEntry, precord->name); - if (!status) status = dbFindField(&dbEntry, pfldDes->name); - if (status) goto finish; + status = dbParseLink(pstring, pfldDes->field_type, &link_info); + if (status) + return status; + + if (link_info.ltype==PV_LINK && !(link_info.modifiers&(pvlOptCA|pvlOptCP|pvlOptCPP))) { + DBADDR tempaddr; + if(dbNameToAddr(link_info.target, &tempaddr)==0) { + /* we will create a DB link. */ + pdbaddr = malloc(sizeof(*pdbaddr)); + if(!pdbaddr) { + status = S_db_noMemory; + goto cleanup; + } + *pdbaddr = tempaddr; /* struct copy */ + } + } isDevLink = ellCount(&precord->rdes->devList) > 0 && pfldDes->isDevLink; dbLockSetGblLock(); dbLockSetRecordLock(precord); + if(pdbaddr) + dbLockSetRecordLock(pdbaddr->precord); scan = precord->scan; @@ -954,10 +971,19 @@ static long dbPutFieldLink(DBADDR *paddr, if (pdevSup) { new_dset = pdevSup->pdset; new_dsxt = pdevSup->pdsxt; + new_devsup = pdevSup; } + } + if (dbCanSetLink(plink, &link_info, new_devsup)) { + /* link type mis-match prevents assignment */ + status = S_dbLib_badField; + goto unlock; + } + + if (isDevLink) { if (precord->dset) { - pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset); + devSup *pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset); if (pdevSup) old_dsxt = pdevSup->pdsxt; } @@ -1005,7 +1031,7 @@ static long dbPutFieldLink(DBADDR *paddr, if (special) status = dbPutSpecial(paddr, 0); - if (!status) status = dbPutString(&dbEntry, pstring); + if (!status) status = dbSetLink(plink, &link_info, new_devsup); if (!status && special) status = dbPutSpecial(paddr, 1); @@ -1032,7 +1058,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* New link type */ case PV_LINK: - dbAddLink(precord, plink, pfldDes->field_type); + dbAddLink(precord, plink, pfldDes->field_type, pdbaddr); break; case CONSTANT: @@ -1063,8 +1089,8 @@ postScanEvent: db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG); unlock: dbLockSetGblUnlock(); -finish: - dbFinishEntry(&dbEntry); +cleanup: + free(link_info.target); return status; } From a919d7e09d17bd4a0b49d4af58ee566a5fe58479 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 008/204] dbLink: pass DBADDR* to dbAddLink() Now we know whether a DB or CA link is created before calling dbAddLink(). --- src/ioc/db/dbLink.c | 36 +++++++++++------------------------- src/ioc/db/dbLink.h | 3 ++- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index af23d7615..bc4de8ec6 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -123,26 +123,6 @@ static long dbDbInitLink(struct link *plink, short dbfType) return 0; } -static long dbDbAddLink(struct link *plink, short dbfType) -{ - DBADDR dbaddr; - long status; - DBADDR *pdbAddr; - - status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr); - if (status) - return status; - - plink->type = DB_LINK; - pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /* structure copy */ - plink->value.pv_link.pvt = pdbAddr; - - dbLockSetRecordLock(pdbAddr->precord); - dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord); - return 0; -} - static void dbDbRemoveLink(struct link *plink) { free(plink->value.pv_link.pvt); @@ -411,17 +391,23 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) } } -void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType) +void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType, DBADDR *ptargetaddr) { plink->value.pv_link.precord = precord; if (plink == &precord->tsel) recGblTSELwasModified(plink); - if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { - /* Can we make it a DB link? */ - if (!dbDbAddLink(plink, dbfType)) - return; + if (ptargetaddr) { + /* make a DB link */ + + plink->type = DB_LINK; + plink->value.pv_link.pvt = ptargetaddr; + + /* target record is already locked in dbPutFieldLink() */ + dbLockSetMerge(plink->value.pv_link.precord, ptargetaddr->precord); + + return; } /* Make it a CA link */ diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index ee097757f..68a803dbb 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -20,6 +20,7 @@ #include "shareLib.h" #include "epicsTypes.h" #include "epicsTime.h" +#include "dbAddr.h" #ifdef __cplusplus extern "C" { @@ -31,7 +32,7 @@ extern "C" { epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType); epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink, - short dbfType); + short dbfType, DBADDR *ptargetaddr); epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, void *pbuffer); epicsShareFunc void dbRemoveLink(struct link *plink); From 5a8915c051320c861244bb9041bac86aa30901bd Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 009/204] INST_IO no longer accept invalid string as empty --- src/ioc/db/test/dbPutLinkTest.c | 7 +++---- src/ioc/dbStatic/dbStaticLib.c | 11 ----------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 31563466f..52ae41fb5 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -482,9 +482,8 @@ static void testLinkFail(void) /* INST_IO doesn't accept empty string */ testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, ""); - /* INST_IO accepts invalid input as empty string */ - testdbPutFieldOk("rINST_IO.INP", DBR_STRING, "abc"); - testdbGetFieldEqual("rINST_IO.INP", DBR_STRING, "@"); + /* INST_IO doesn't accept empty string */ + testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc"); /* syntax errors */ testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO"); @@ -505,7 +504,7 @@ static void testLinkFail(void) MAIN(dbPutLinkTest) { - testPlan(245); + testPlan(244); testLinkParse(); testLinkFailParse(); testCADBSet(); diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index ca7dfff83..ac4276e5d 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2092,9 +2092,6 @@ long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) case PV_LINK: if(link_type==CONSTANT || link_type==PV_LINK) return 0; - else if(link_type==INST_IO && pinfo->target[0]!='\0') - /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ - return 0; default: free(pinfo->target); pinfo->target = NULL; @@ -2224,14 +2221,6 @@ long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) dbFreeLinkContents(plink); dbSetLinkHW(plink, pinfo); - } else if(link_type==INST_IO && (pinfo->ltype==CONSTANT || pinfo->ltype==PV_LINK) && pinfo->target[0]!='\0') { - /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ - pinfo->target[0] = '\0'; - pinfo->ltype = INST_IO; - - dbFreeLinkContents(plink); - dbSetLinkHW(plink, pinfo); - } else goto fail; From 6ed997aaccfe26aaef5c0413b99e42f35640c88c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 010/204] Invalid HW links no longer partially initialized --- src/ioc/db/test/dbPutLinkTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 52ae41fb5..e6580b0a1 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -445,7 +445,7 @@ static void testLinkInitFail(void) testOk1(plink->type==VME_IO); testOk1(plink->value.vmeio.parm!=NULL); - testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @"); + testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @"); prec = (xRecord*)testdbRecordPtr("eVME_IO2"); plink = &prec->inp; From 5847f98f8922b5ea17c1dd7403c609a6195f3297 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 13:55:33 -0400 Subject: [PATCH 011/204] oops --- src/ioc/db/test/dbLinkdset.c | 5 +++-- src/ioc/dbStatic/dbStaticLib.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/test/dbLinkdset.c b/src/ioc/db/test/dbLinkdset.c index dce258a3f..c1de4cf00 100644 --- a/src/ioc/db/test/dbLinkdset.c +++ b/src/ioc/db/test/dbLinkdset.c @@ -12,9 +12,10 @@ long link_test_extend(struct dbCommon *junk) static dsxt xrecextend = {&link_test_extend, &link_test_extend}; static -long link_test_init(int junk) +long link_test_init(int pass) { - devExtend(&xrecextend); + if(pass==0) + devExtend(&xrecextend); return 0; } diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index ac4276e5d..bf1c65580 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2297,7 +2297,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) struct dbCommon *prec = pdbentry->precnode->precord; devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1); status = dbCanSetLink(plink, &link_info, devsup); - if(status) + if(status==0) status = dbSetLink(plink, &link_info, devsup); } From 3885c809a659282c334a8ca8b26a483c269f33a0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 22 Aug 2014 15:23:51 -0400 Subject: [PATCH 012/204] introduce genVersionHeader.pl --- configure/RULES_BUILD | 13 ++++++ src/tools/Makefile | 1 + src/tools/genVersionHeader.pl | 74 +++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 src/tools/genVersionHeader.pl diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 711da3434..0af9b6e55 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -359,6 +359,19 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES) @$(RM) $@ $(PERL) $(TOOLS)/makeTestfile.pl $@ $< +#--------------------------------------------------------------- +# Generate header with version number from VCS + +ifneq ($(VERHEADER),) +GENVERSIONHEADER ?= $(PERL) $(TOOLS)/genVersionHeader.pl +MODVERSION ?= undefined +MODMACRO ?= MODULEVERSION + +$(VERHEADER):: + $(GENVERSIONHEADER) -t $(TOP) -V $(MODVERSION) -N $(MODMACRO) $@ + +endif + #--------------------------------------------------------------- # Install rules for BIN_INSTALLS and LIB_INSTALLS diff --git a/src/tools/Makefile b/src/tools/Makefile index 496b0e4d1..ff5c55e39 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -49,6 +49,7 @@ PERL_SCRIPTS += dbdToMenuH.pl PERL_SCRIPTS += dbdToRecordtypeH.pl PERL_SCRIPTS += dbdExpand.pl PERL_SCRIPTS += dbdToHtml.pl +PERL_SCRIPTS += genVersionHeader.pl PERL_SCRIPTS += registerRecordDeviceDriver.pl include $(TOP)/configure/RULES diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl new file mode 100644 index 000000000..0fd6db2df --- /dev/null +++ b/src/tools/genVersionHeader.pl @@ -0,0 +1,74 @@ +#!/usr/bin/env perl +# +# Generate a C header file which +# defines a macro with a string +# describing the VCS revision +# + +use EPICS::Getopts; + +use strict; + +our($opt_v,$opt_t,$opt_N,$opt_V); + +$opt_N = "MODVERSION"; +$opt_V = "undefined"; +$opt_t = "."; +my $foundvcs = 0; +my $result; + +getopts("vt:N:V:") or + die "Usage: genVersionHeader.pl [-t top] [-N NAME] [-V VERSION] output.h"; + +my ($outfile) = @ARGV; + +if(-d "$opt_t/.hg") { # Mercurial + # v1-4-abcdef-dirty + # is 4 commits after tag 'v1' with short hash abcdef + # with uncommited modifications + $result = `cd "$opt_t" && hg tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`; + chomp($result); + if(length($result)>1) { + $opt_V = $result; + $foundvcs = 1; + } + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `cd "$opt_t" && hg status -m -a -r -d`; + chomp($hasmod); + if(length($hasmod)>0) { + $opt_V = "$opt_V-dirty"; + } +} +if(!$foundvcs && -d "$opt_t/.git") { + # same format as Mercurial + $result = `git --git-dir="$opt_t/.git" describe --tags --dirty`; + chomp($result); + if(length($result)>1) { + $opt_V = $result; + $foundvcs = 1; + } +} + +my $output = "#ifndef $opt_N\n# define $opt_N \"$opt_V\"\n#endif\n"; +print "== would\n$output" if $opt_v; + +my $DST; +if(open($DST, '+<', $outfile)) { + my $actual = join("", <$DST>); + print "== have\n$actual" if $opt_v; + + if($actual eq $output) { + print "keep existing $outfile\n"; + exit(0) + } + print "updating $outfile\n"; +} else { + print "create $outfile\n"; + open($DST, '>', $outfile) or die "Unable to open or create $outfile"; +} + +seek($DST,0,0); +truncate($DST,0); +print $DST $output; + +close($DST); From 55cf45364c69fc5d15d733ac24d2bc23c8641894 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Wed, 27 Aug 2014 11:03:54 -0700 Subject: [PATCH 013/204] tools: Make genVersionHeader.pl log more detailed and streamlined with other EPICS build output --- src/tools/genVersionHeader.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index 0fd6db2df..357c17acc 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -58,13 +58,13 @@ if(open($DST, '+<', $outfile)) { print "== have\n$actual" if $opt_v; if($actual eq $output) { - print "keep existing $outfile\n"; + print "Keeping existing VCS version header $outfile\n"; exit(0) } - print "updating $outfile\n"; + print "Updating VCS version header $outfile\n"; } else { - print "create $outfile\n"; - open($DST, '>', $outfile) or die "Unable to open or create $outfile"; + print "Creating VCS version header $outfile\n"; + open($DST, '>', $outfile) or die "Unable to open or create VCS version header $outfile"; } seek($DST,0,0); From 6a018dce35268a2ac5f9f8c337873dfd2082efe9 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Wed, 27 Aug 2014 11:06:57 -0700 Subject: [PATCH 014/204] tools: Add support for Darcs to genVersionHeader.pl --- src/tools/genVersionHeader.pl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index 357c17acc..d4f1c52dc 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -22,6 +22,22 @@ getopts("vt:N:V:") or my ($outfile) = @ARGV; +if(-d "$opt_t/_darcs") { # Darcs + # v1-4-dirty + # is tag 'v1' plus 4 patches + # with uncommited modifications + $result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`; + chomp($result); + if(length($result)>1) { + $opt_V = $result; + $foundvcs = 1; + } + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`; + if(!$?) { + $opt_V = "$opt_V-dirty"; + } +} if(-d "$opt_t/.hg") { # Mercurial # v1-4-abcdef-dirty # is 4 commits after tag 'v1' with short hash abcdef From 3f462bd032f499d4ff52ecf30380b7a6699e6675 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 27 Aug 2014 17:24:44 -0700 Subject: [PATCH 015/204] check errors --- src/tools/genVersionHeader.pl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index d4f1c52dc..42c0ee531 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -22,44 +22,44 @@ getopts("vt:N:V:") or my ($outfile) = @ARGV; -if(-d "$opt_t/_darcs") { # Darcs +if(!$foundvcs && -d "$opt_t/_darcs") { # Darcs # v1-4-dirty # is tag 'v1' plus 4 patches # with uncommited modifications $result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`; chomp($result); - if(length($result)>1) { + if(!$? && length($result)>1) { $opt_V = $result; $foundvcs = 1; - } - # see if working copy has modifications, additions, removals, or missing files - my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`; - if(!$?) { - $opt_V = "$opt_V-dirty"; + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`; + if(!$?) { + $opt_V = "$opt_V-dirty"; + } } } -if(-d "$opt_t/.hg") { # Mercurial +if(!$foundvcs && -d "$opt_t/.hg") { # Mercurial # v1-4-abcdef-dirty # is 4 commits after tag 'v1' with short hash abcdef # with uncommited modifications $result = `cd "$opt_t" && hg tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`; chomp($result); - if(length($result)>1) { + if(!$? && length($result)>1) { $opt_V = $result; $foundvcs = 1; - } - # see if working copy has modifications, additions, removals, or missing files - my $hasmod = `cd "$opt_t" && hg status -m -a -r -d`; - chomp($hasmod); - if(length($hasmod)>0) { - $opt_V = "$opt_V-dirty"; + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `cd "$opt_t" && hg status -m -a -r -d`; + chomp($hasmod); + if(length($hasmod)>0) { + $opt_V = "$opt_V-dirty"; + } } } if(!$foundvcs && -d "$opt_t/.git") { # same format as Mercurial $result = `git --git-dir="$opt_t/.git" describe --tags --dirty`; chomp($result); - if(length($result)>1) { + if(!$? && length($result)>1) { $opt_V = $result; $foundvcs = 1; } From bc9255ba1c9015270538a8e43b40853d2ba9cf39 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 28 Aug 2014 10:38:24 -0700 Subject: [PATCH 016/204] add version header to example template --- src/template/base/Makefile | 2 + src/template/base/top/exampleApp/Db/Makefile | 1 + .../top/exampleApp/Db/dbVersionExample.db | 5 ++ src/template/base/top/exampleApp/src/Makefile | 12 +++++ .../top/exampleApp/src/_APPNAME_Hello.dbd | 1 + .../base/top/exampleApp/src/devStringIn.c | 52 +++++++++++++++++++ .../base/top/exampleBoot/ioc/st.cmd@Common | 1 + .../base/top/exampleBoot/ioc/st.cmd@RTEMS | 1 + .../base/top/exampleBoot/ioc/st.cmd@vxWorks | 1 + 9 files changed, 76 insertions(+) create mode 100644 src/template/base/top/exampleApp/Db/dbVersionExample.db create mode 100644 src/template/base/top/exampleApp/src/devStringIn.c diff --git a/src/template/base/Makefile b/src/template/base/Makefile index 3cb048b0f..8e3aae45e 100644 --- a/src/template/base/Makefile +++ b/src/template/base/Makefile @@ -28,9 +28,11 @@ TEMPLATES += top/exampleApp/Makefile TEMPLATES += top/exampleApp/Db/Makefile TEMPLATES += top/exampleApp/Db/dbExample1.db TEMPLATES += top/exampleApp/Db/dbExample2.db +TEMPLATES += top/exampleApp/Db/dbVersionExample.db TEMPLATES += top/exampleApp/Db/dbSubExample.db TEMPLATES += top/exampleApp/Db/user.substitutions TEMPLATES += top/exampleApp/src/Makefile +TEMPLATES += top/exampleApp/src/devStringIn.c TEMPLATES += top/exampleApp/src/xxxRecord.dbd TEMPLATES += top/exampleApp/src/xxxRecord.c TEMPLATES += top/exampleApp/src/devXxxSoft.c diff --git a/src/template/base/top/exampleApp/Db/Makefile b/src/template/base/top/exampleApp/Db/Makefile index 3adbf09a5..86bc0d620 100644 --- a/src/template/base/top/exampleApp/Db/Makefile +++ b/src/template/base/top/exampleApp/Db/Makefile @@ -12,6 +12,7 @@ include $(TOP)/configure/CONFIG # databases, templates, substitutions like this DB += dbExample1.db DB += dbExample2.db +DB += dbVersionExample.db DB += dbSubExample.db DB += user.substitutions diff --git a/src/template/base/top/exampleApp/Db/dbVersionExample.db b/src/template/base/top/exampleApp/Db/dbVersionExample.db new file mode 100644 index 000000000..bd1c175df --- /dev/null +++ b/src/template/base/top/exampleApp/Db/dbVersionExample.db @@ -0,0 +1,5 @@ +record(stringin, "$(user):version") { + field(DTYP, "My Version") + field(DESC, "Module version") + field(PINI, "YES") +} diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile index 243cd926f..4d6b21485 100644 --- a/src/template/base/top/exampleApp/src/Makefile +++ b/src/template/base/top/exampleApp/src/Makefile @@ -17,6 +17,7 @@ DBD += xxxSupport.dbd # Compile and add the code to the support library _APPNAME_Support_SRCS += xxxRecord.c _APPNAME_Support_SRCS += devXxxSoft.c +_APPNAME_Support_SRCS += devStringIn.c # Link locally-provided code into the support library, # rather than directly into the IOC application. @@ -26,6 +27,14 @@ _APPNAME_Support_SRCS += initTrace.c _APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS) +# Generate a header which defines a macro with a version string +# from VCS system, or the default +VERHEADER = mymodversion.h +# The macro name to use (default is MODULEVERSION) +MODMACRO = MYMODVERSION +# Default version string if no VCS system in use +MODVERSION = defaultversionstring + #============================= # Build the IOC application @@ -81,3 +90,6 @@ include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE +# Dependency for the generated header +# must be explicit +devStringIn$(DEP): $(VERHEADER) diff --git a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd index 64eb0389a..5be6cdc04 100644 --- a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd +++ b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd @@ -1 +1,2 @@ registrar(helloRegister) +device(stringin,INST_IO,devSiMyModVersion,"My Version") diff --git a/src/template/base/top/exampleApp/src/devStringIn.c b/src/template/base/top/exampleApp/src/devStringIn.c new file mode 100644 index 000000000..3aa3fc94a --- /dev/null +++ b/src/template/base/top/exampleApp/src/devStringIn.c @@ -0,0 +1,52 @@ +/* devStringIn.c */ +/* Example device support module for stringinRecord */ + +#include +#include +#include +#include + +#include "recGbl.h" +#include "alarm.h" +#include "recSup.h" +#include "devSup.h" +#include "stringinRecord.h" + +#include "mymodversion.h" + + +static long init_record(stringinRecord *prec) +{ + return 0; /* no initialization needed */ +} + +static long read_si(stringinRecord *prec) +{ + size_t N = strlen(MYMODVERSION); + if(Nval, MYMODVERSION); + } else { + /* Not enough space, set an alarm */ + (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + } + return 0; +} + +#include "epicsExport.h" + +static struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_si; +}devSiMyModVersion={ + 5, + NULL, + NULL, + init_record, + NULL, + read_si, +}; +epicsExportAddress(dset,devSiMyModVersion); diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@Common b/src/template/base/top/exampleBoot/ioc/st.cmd@Common index 76360dd08..c4eb081ba 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@Common +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@Common @@ -13,6 +13,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" +dbLoadRecords "db/dbVersionExample.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" ## Set this to see messages from mySub diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS index 4addc5c29..7b6319608 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS @@ -11,6 +11,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase) ## Load record instances dbLoadTemplate("db/user.substitutions") +dbLoadRecords("db/dbVersionExample.db", "user=_USER_") dbLoadRecords("db/dbSubExample.db", "user=_USER_") ## Set this to see messages from mySub diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks index 27ad0572b..d8a2077ce 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks @@ -20,6 +20,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" +dbLoadRecords "db/dbVersionExample.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" ## Set this to see messages from mySub From e5b354708e5060674eec89c2aabb7cb906335ea9 Mon Sep 17 00:00:00 2001 From: Kukhee Kim Date: Thu, 28 Aug 2014 12:11:51 -0700 Subject: [PATCH 017/204] Implement LIFO behavior in compress record --- src/std/rec/compressRecord.c | 52 ++++++++++++++++++++++++++---- src/std/rec/compressRecord.dbd.pod | 11 +++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/std/rec/compressRecord.c b/src/std/rec/compressRecord.c index 0ecfbd4e5..84a93f82f 100644 --- a/src/std/rec/compressRecord.c +++ b/src/std/rec/compressRecord.c @@ -106,7 +106,7 @@ static void monitor(compressRecord *prec) db_post_events(prec, prec->bptr, monitor_mask); } -static void put_value(compressRecord *prec, double *psource, int n) +static void put_value_FIFO(compressRecord *prec, double *psource, int n) { epicsInt32 offset = prec->off; epicsInt32 nuse = prec->nuse; @@ -130,6 +130,42 @@ static void put_value(compressRecord *prec, double *psource, int n) prec->nuse = nuse; return; } + +static void put_value_LIFO(compressRecord *prec, double *psource, int n) +{ + epicsInt32 offset = prec->off; + epicsInt32 nuse = prec->nuse; + epicsInt32 nsam = prec->nsam; + epicsInt32 start = offset - nuse; + double *pdest; + int i; + if(start<0) start += nsam; + + for(i = 0; i < n; i++) { + if(--start < 0) start += nsam; + pdest = prec->bptr + start; + *pdest = *psource++; + } + nuse += n; + if(nuse > nsam) nuse = nsam; + offset = start + nuse; + if(offset>=nsam) offset -= nsam; + + prec->off = offset; + prec->nuse = nuse; + + return; +} + +static void put_value(compressRecord *prec, double *psource, int n) +{ + switch(prec->balg) { + case bufferingALG_FIFO: put_value_FIFO(prec, psource, n); break; + case bufferingALG_LIFO: put_value_LIFO(prec, psource, n); break; + } + return; +} + /* qsort comparison function (for median calculation) */ static int compare(const void *arg1, const void *arg2) @@ -305,6 +341,7 @@ static long init_record(compressRecord *prec, int pass) prec->bptr = calloc(prec->nsam, sizeof(double)); reset(prec); } + return 0; } @@ -388,18 +425,21 @@ static long cvt_dbaddr(DBADDR *paddr) paddr->field_type = DBF_DOUBLE; paddr->field_size = sizeof(double); paddr->dbr_field_type = DBF_DOUBLE; + + if(prec->balg == bufferingALG_LIFO) paddr->special = SPC_NOMOD; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { compressRecord *prec = (compressRecord *) paddr->precord; + epicsInt32 start = prec->off - prec->nuse; + if(start<0) start += prec->nsam; + if(prec->balg == bufferingALG_FIFO) + *no_elements = prec->nuse; + else *no_elements = prec->nsam; + *offset = start; - *no_elements = prec->nuse; - if (prec->nuse == prec->nsam) - *offset = prec->off; - else - *offset = 0; return 0; } diff --git a/src/std/rec/compressRecord.dbd.pod b/src/std/rec/compressRecord.dbd.pod index 97b84a9a0..14f1bd407 100644 --- a/src/std/rec/compressRecord.dbd.pod +++ b/src/std/rec/compressRecord.dbd.pod @@ -41,6 +41,10 @@ menu(compressALG) { choice(compressALG_Circular_Buffer,"Circular Buffer") choice(compressALG_N_to_1_Median,"N to 1 Median") } +menu(bufferingALG) { + choice(bufferingALG_FIFO, "FIFO Buffer") + choice(bufferingALG_LIFO, "LIFO Buffer") +} recordtype(compress) { =fields VAL @@ -76,6 +80,13 @@ recordtype(compress) { interest(1) menu(compressALG) } + field(BALG,DBF_MENU) { + prompt("Buffering Algorithm") + promptgroup(GUI_ALARMS) + special(SPC_RESET) + interest(1) + menu(bufferingALG) + } field(NSAM,DBF_ULONG) { prompt("Number of Values") promptgroup(GUI_COMPRESS) From e24d4637c9271f8d4c37c927e999658da11f08e7 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 29 Aug 2014 09:56:32 -0700 Subject: [PATCH 018/204] genVersionHeader: use date+time as the default version number --- configure/RULES_BUILD | 4 ++-- src/tools/genVersionHeader.pl | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 0af9b6e55..516c1eaaf 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -364,11 +364,11 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES) ifneq ($(VERHEADER),) GENVERSIONHEADER ?= $(PERL) $(TOOLS)/genVersionHeader.pl -MODVERSION ?= undefined +#MODVERSION = defaultversionnumber MODMACRO ?= MODULEVERSION $(VERHEADER):: - $(GENVERSIONHEADER) -t $(TOP) -V $(MODVERSION) -N $(MODMACRO) $@ + $(GENVERSIONHEADER) -t "$(TOP)" -V "$(MODVERSION)" -N "$(MODMACRO)" $@ endif diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index 42c0ee531..19c28edd7 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -6,13 +6,13 @@ # use EPICS::Getopts; +use POSIX qw(strftime); use strict; our($opt_v,$opt_t,$opt_N,$opt_V); $opt_N = "MODVERSION"; -$opt_V = "undefined"; $opt_t = "."; my $foundvcs = 0; my $result; @@ -22,6 +22,12 @@ getopts("vt:N:V:") or my ($outfile) = @ARGV; +chomp($opt_V); +if(!$opt_V) { + # RFC 8601 date+time w/ zone (eg "2014-08-29T09:42:47-0700") + $opt_V = strftime "%Y-%m-%dT%H:%M:%S%z", localtime; +} + if(!$foundvcs && -d "$opt_t/_darcs") { # Darcs # v1-4-dirty # is tag 'v1' plus 4 patches From 95d0d97f6af5d347323c59163a90d95c78b9ad51 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 29 Aug 2014 10:58:18 -0700 Subject: [PATCH 019/204] more consistent macro names --- configure/RULES_BUILD | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 516c1eaaf..2b836ce2c 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -362,13 +362,15 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES) #--------------------------------------------------------------- # Generate header with version number from VCS -ifneq ($(VERHEADER),) -GENVERSIONHEADER ?= $(PERL) $(TOOLS)/genVersionHeader.pl -#MODVERSION = defaultversionnumber -MODMACRO ?= MODULEVERSION +ifneq ($(GENVERSION),) +GENERATEVERSIONHEADERCMD ?= $(PERL) $(TOOLS)/genVersionHeader.pl +# C macro name +GENVERSIONMACRO ?= MODULEVERSION +# C macro default (empty uses date+time) +#GENVERSIONDEFAULT = defaultversionnumber -$(VERHEADER):: - $(GENVERSIONHEADER) -t "$(TOP)" -V "$(MODVERSION)" -N "$(MODMACRO)" $@ +$(GENVERSION):: + $(GENERATEVERSIONHEADERCMD) -t "$(TOP)" -V "$(GENVERSIONDEFAULT)" -N "$(GENVERSIONMACRO)" $@ endif From 75321140ef05d1510b135120df7819cc1ba9fb17 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 29 Aug 2014 10:58:18 -0700 Subject: [PATCH 020/204] update example template --- src/template/base/Makefile | 2 +- src/template/base/top/exampleApp/src/Makefile | 12 ++++-------- ...{devStringIn.c => devStringInGenVersion.c} | 19 +++++++------------ 3 files changed, 12 insertions(+), 21 deletions(-) rename src/template/base/top/exampleApp/src/{devStringIn.c => devStringInGenVersion.c} (72%) diff --git a/src/template/base/Makefile b/src/template/base/Makefile index 8e3aae45e..f2292c4e5 100644 --- a/src/template/base/Makefile +++ b/src/template/base/Makefile @@ -32,7 +32,7 @@ TEMPLATES += top/exampleApp/Db/dbVersionExample.db TEMPLATES += top/exampleApp/Db/dbSubExample.db TEMPLATES += top/exampleApp/Db/user.substitutions TEMPLATES += top/exampleApp/src/Makefile -TEMPLATES += top/exampleApp/src/devStringIn.c +TEMPLATES += top/exampleApp/src/devStringInGenVersion.c TEMPLATES += top/exampleApp/src/xxxRecord.dbd TEMPLATES += top/exampleApp/src/xxxRecord.c TEMPLATES += top/exampleApp/src/devXxxSoft.c diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile index 4d6b21485..a419167b8 100644 --- a/src/template/base/top/exampleApp/src/Makefile +++ b/src/template/base/top/exampleApp/src/Makefile @@ -17,7 +17,7 @@ DBD += xxxSupport.dbd # Compile and add the code to the support library _APPNAME_Support_SRCS += xxxRecord.c _APPNAME_Support_SRCS += devXxxSoft.c -_APPNAME_Support_SRCS += devStringIn.c +_APPNAME_Support_SRCS += devStringInGenVersion.c # Link locally-provided code into the support library, # rather than directly into the IOC application. @@ -28,12 +28,8 @@ _APPNAME_Support_SRCS += initTrace.c _APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS) # Generate a header which defines a macro with a version string -# from VCS system, or the default -VERHEADER = mymodversion.h -# The macro name to use (default is MODULEVERSION) -MODMACRO = MYMODVERSION -# Default version string if no VCS system in use -MODVERSION = defaultversionstring +# with the date and time, or a revision id from VCS system if available +GENVERSION = mymodversion.h #============================= # Build the IOC application @@ -92,4 +88,4 @@ include $(TOP)/configure/RULES # Dependency for the generated header # must be explicit -devStringIn$(DEP): $(VERHEADER) +devStringInGenVersion$(DEP): $(GENVERSION) diff --git a/src/template/base/top/exampleApp/src/devStringIn.c b/src/template/base/top/exampleApp/src/devStringInGenVersion.c similarity index 72% rename from src/template/base/top/exampleApp/src/devStringIn.c rename to src/template/base/top/exampleApp/src/devStringInGenVersion.c index 3aa3fc94a..21fd09bfc 100644 --- a/src/template/base/top/exampleApp/src/devStringIn.c +++ b/src/template/base/top/exampleApp/src/devStringInGenVersion.c @@ -14,26 +14,21 @@ #include "mymodversion.h" - -static long init_record(stringinRecord *prec) -{ - return 0; /* no initialization needed */ -} +/* must be last include */ +#include "epicsExport.h" static long read_si(stringinRecord *prec) { - size_t N = strlen(MYMODVERSION); - if(Nval, MYMODVERSION); + size_t N = strlen(MODULEVERSION); + if(Nval)) { + strcpy(prec->val, MODULEVERSION); } else { - /* Not enough space, set an alarm */ + /* Not enough space, so signal an alarm */ (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); } return 0; } -#include "epicsExport.h" - static struct { long number; DEVSUPFUN report; @@ -45,7 +40,7 @@ static struct { 5, NULL, NULL, - init_record, + NULL, NULL, read_si, }; From 79497c359c133c6ce48e6637a3145e8e5fdf8e7e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 29 Aug 2014 11:05:56 -0700 Subject: [PATCH 021/204] add release note --- documentation/RELEASE_NOTES.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 3db8233c0..99a32458e 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -15,6 +15,16 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.

Changes between 3.15.0.1 and 3.15.0.2

+

Generate Version Header

+ +

A Perl script and Makefile rules are added to allow modules +to generate a C header file with a macro defined with an automatically updated identifier. +This is a VCS revision ID (Darcs, Git, and Mercurial supported) or +the time of the build if not VCS system is in use.

+ +

The example template is updated with a demonstration which makes this +identifier visible with a stringin record.

+

Implement EPICS_CAS_INTF_ADDR_LIST in rsrv

The IOC server can now bind to a single IP address (and optional port number) From 42a3cf8e110a5e261ab7b6198a27491412f1f4bd Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 1 Dec 2014 15:31:18 -0600 Subject: [PATCH 022/204] Set version numbers for 3.16.0.0-DEV --- configure/CONFIG_BASE_VERSION | 25 +- documentation/RELEASE_NOTES.html | 1005 +----------------------------- 2 files changed, 26 insertions(+), 1004 deletions(-) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index f15f47449..a87b2ce72 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -17,26 +17,37 @@ # EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local # version number to be included in the reported version string. -# In 3.15 we still define BASE_3_14 so "ifdef BASE_3_14" means -# 3.14 or later, but "ifeq ($(BASE_3_14),YES)" means 3.14 only. +# We define BASE_3_14 and BASE_3_15 as NO and BASE_3_16 as YES, so +# ifdef BASE_3_14 +# true for 3.14 or later +# ifdef BASE_3_15 +# true for 3.15 or later +# ifeq ($(BASE_3_14),YES) +# true for 3.14.x only +# ifeq ($(BASE_3_15),YES) +# true for 3.15 only +# ifeq ($(BASE_3_16),YES) +# true for 3.16 only. + BASE_3_14 = NO -BASE_3_15 = YES +BASE_3_15 = NO +BASE_3_16 = YES # EPICS_VERSION must be a number >0 and <256 EPICS_VERSION = 3 # EPICS_REVISION must be a number >=0 and <256 -EPICS_REVISION = 15 +EPICS_REVISION = 16 # EPICS_MODIFICATION must be a number >=0 and <256 -EPICS_MODIFICATION = 1 +EPICS_MODIFICATION = 0 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included if zero EPICS_PATCH_LEVEL = 0 # This will end in -DEV between official releases -#EPICS_DEV_SNAPSHOT=-DEV +EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-pre1 #EPICS_DEV_SNAPSHOT=-pre1-DEV #EPICS_DEV_SNAPSHOT=-pre2 @@ -45,7 +56,7 @@ EPICS_PATCH_LEVEL = 0 #EPICS_DEV_SNAPSHOT=-rc1-DEV #EPICS_DEV_SNAPSHOT=-rc2 #EPICS_DEV_SNAPSHOT=-rc2-DEV -EPICS_DEV_SNAPSHOT= +#EPICS_DEV_SNAPSHOT= # No changes should be needed below here diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 269b6210c..8361621c8 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -3,1012 +3,23 @@ - EPICS Base R3.15.1 Release Notes + EPICS Base R3.16.0 Release Notes -

EPICS Base Release 3.15.1

+

EPICS Base Release 3.16.0

-

Changes between 3.15.0.2 and 3.15.1

+

This version of EPICS Base has not been released yet.

-

epicsStrnEscapedFromRaw() and epicsStrnRawFromEscaped()

+

Changes between 3.15.x and 3.16.0

+ -

Hooking into dbLoadRecords

- -

A function pointer hook has been added to the dbLoadRecords() routine, to -allow external modules such as autosave to be notified when new records have -been loaded during IOC initialization. The hook is called dbLoadRecordsHook and -follows the model of the recGblAlarmHook pointer in that modules that wish to -use it must save the current value of the pointer before installing their own -function pointer, and must call the original function from their own -routine.

- -

The hook is activiated from the dbLoadRecords() routine and gets called only -after a database instance file has been read in without error. Note that the -dbLoadTemplates() routine directly calls dbLoadRecords() so this hook also -provides information about instantiated database templates. It is still possible -to load record instances using dbLoadDatabase() though, and doing this will not -result in the hook routines being called.

- -

Code to use this hook should look something like this:

- -
-#include "dbAccessDefs.h"
-
-static DB_LOAD_RECORDS_HOOK_ROUTINE previousHook;
-
-static void myRoutine(const char* file, const char* subs) {
-    if (previousHook)
-        previousHook(file, subs);
-
-    /* Do whatever ... */
-}
-
-void myInit(void) {
-    static int done = 0;
-
-    if (!done) {
-        previousHook = dbLoadRecordsHook;
-        dbLoadRecordsHook = myRoutine;
-        done = 1;
-    }
-}
-
- -

As with many other parts of the static database access library there is no -mutex to protect the function pointer. Initialization is expected to take place -in the context of the IOC's main thread, from either a static C++ constructor or -an EPICS registrar routine.

- - -

Changes between 3.15.0.1 and 3.15.0.2

- -

New iocshLoad command

- -

A new command iocshLoad has been added to iocsh which executes a -named iocsh script and can also set one or more shell macro variables at the -same time, the values of which will be forgotten immediately after the named -script finishes executing. The following example shows the syntax:

- -
-iocshLoad "serial.cmd", "DEV=/dev/ttyS0,PORT=com1,TYPE=RS485"
-iocshLoad "radmon.cmd", "PORT=com1,ADDR=0"
-
- -

Support routines for 64-bit integers

- -

The libCom library now provides support for 64-bit integer types on all -supported architectures. The epicsTypes.h header file defines epicsInt64 and -epicsUInt64 type definitions for both C and C++ code. The epicsStdlib.h header -also declares the following for parsing strings into the relevent sized integer -variables: Functions epicsParseLLong(), epicsParseULLong() with related macros -epicsScanLLong() and epicsScanULLong(), and the functions epicsParseInt64() -and epicsParseUInt64(). Use the first two functions and the macros for long long -and unsigned long long integer types, and the last two functions for the -epicsInt64 and epicsUInt64 types. Note that the latter can map to the types long -and unsigned long on some 64-bit architectures such as linux-x86_64, not to the -two long long types.

- -

This version does not provide the ability to define 64-bit record fields, the -use of the 64-bit types in the IOC database will come in a later release of -EPICS Base.

- -

Full support for loadable support modules

- -

Apparently later versions of Base 3.14 permitted support modules to be loaded -from a shared library at runtime without the IOC having been linked against that -shared library; the registerRecordDeviceDriver.pl program would accept a partial -DBD file containing just the entries needed for the library and generate the -appropriate registration code. In 3.15 however the registerRecordDeviceDriver.pl -program was replaced by one using the new DBD file parser, and in this a device -support entry would only be accepted after first loading the record type that it -depended on.

- -

The parser has been modified to accept device entries without having seen the -record type first, although a warning is given when that happens. To remove the -warning the DBD file can provide a record type declaration instead (no fields -can be defined, so the braces must be empty), before the device() entry. The -result will generate the correct registration code for the device entry without -including anything for any merely declared record types. The generated code can -be linked into a shared library and loaded by an IOC at runtime using dlload. -

- -

Parallel callback threads

- -

The general purpose callback facility can run multiple parallel callback -threads per priority level. This makes better use of SMP architectures (e.g. -processors with multiple cores), as callback work - which includes second -stage processing of records with asynchronuous device support and I/O -scanned processing - can be distributed over the available CPUs.

- -

Note that by using parallel callback threads the order of scan callback -requests in the queue is not retained. If a device support needs to be -informed when scanIoRequest processing has finished, it should use the new -scanIoSetComplete() feature to add a user function that will be called after -the scanIoRequest record processing has finished.

- -

Parallel callback threads have to be explicitly configured, by default -the IOC keeps the old behavior of running one callback thread per priority.

- -

Merge MMIO API from devLib2

- -

Added calls to handle 8, 16, and 32 bit Memory Mapped I/O reads and writes. -The calls added include X_iowriteY() and -X_ioreadY() -where X is nat (native), be or le, -and Y is 16 or 32. -Also added are ioread8() and iowrite8().

- -

Added optional dbServer API to database

- -

A server layer that sits on top of the IOC database may now register itself -as such by calling dbRegisterServer() and providing optional routines -that other components can use. The initial purpose of this API allows the Trace -Processing implementation in dbProcess() to identify a client that -causes a record to process when TPRO is set.

- -

To support the client idenfication, the server provides a routine that -returns that identity string when called by one of its own processing -threads.

- -

Concatenated database definition files

- -

A series of database definition (dbd) files can now be concatenated during -the build process into a newly-created dbd file with result being installed into -$(INSTALL_LOCATION)/dbd without expanding it.

- -

The following lines in an EPICS Makefile will create a file name.dbd in the -O.Common build directory containing the contents of file1.dbd followed by -file2.dbd then file3.dbd. The new file will then be installed into -$(INSTALL_LOCATION)/dbd without expanding any of its include statements.

- -
-DBDCAT += name.dbd
-name_DBD += file1.dbd file2.dbd file3.dbd
-
- -

The source files file1.dbd, file2.dbd and file3.dbd may be created by the -current Makefile, be located in the parent directory or any other directory in -the SRC_DIRS list, be specified by their full pathname, exist in the install dbd -directory, or be found in any dbd directory linked from the application's -RELEASE files.

- -

Posix: Drop SCHED_FIFO before exec() in child process

- -

If Base is compiled with USE_POSIX_THREAD_PRIORITY_SCHEDULING = YES -in configure/CONFIG_SITE or related files, the Posix implementation of the -libCom osiSpawnDetachedProcess() routine will switch the child process -to use the normal SCHED_OTHER (non real-time) scheduler before executing the -named executable program. If it needs to use the real-time scheduler the new -program can request that for itself.

- -

Posix: Lock all memory when running with FIFO scheduler

- -

On Posix systems, an IOC application's ability to meet timing deadlines is -often dependent on its ability to lock part or all of the process's virtual -address space into RAM, preventing that memory from being paged to the swap -area. This change will attempt to lock the process's virtual address space into -RAM if the process has the ability to run threads with different priorities. If -unsuccessful, it prints an message to stderr and continues.

- -

On Linux, one can grant a process the ability to run threads with different -priorities by using the command ulimit -r unlimited. To use the -FIFO scheduler for an IOC, use a command like this:

- -
chrt -f 1 softIoc -d test.db
- -

On Linux, one can grant a process the ability to lock itself into memory -using the command ulimit -l unlimited. These limits can also be -configured on a per user/per group basis by changing /etc/security/limits.conf -or its equivalent.

- -

A child process created via fork() normally inherits its parent's resource -limits, so a child of a real-time soft-IOC will get its parent's real-time -priority and memlock limits. The memory locks themselves however are not -inherited by child processes.

- -

Implement EPICS_CAS_INTF_ADDR_LIST in rsrv

- -

The IOC server can now bind to a single IP address (and optional port number) -read from the standard environment parameter EPICS_CAS_INTF_ADDR_LIST. -Additional addresses included in that parameter after the first will be ignored -and a warning message displayed at iocInit time.

- -

alarmString.h deprecated again

- -

The string arrays that provide string versions of the alarm status and -severity values have been moved into libCom and the header file that used to -instanciate them is no longer required, although a copy is still provided for -backwards compatibility reasons. Only the alarm.h header needs to be included -now to declare the epicsAlarmSeverityStrings and epicsAlarmConditionStrings -arrays.

- -

General purpose thread pool

- -

-A general purpose threaded work queue API epicsThreadPool is added. -Multiple pools can be created with controlable priority and number -of worker threads. Lazy worker startup is supported.

- -

Database field setting updates

- -

A database (.db) file loaded by an IOC does not have to repeat the record -type of a record that has already been loaded. It may replace the first -parameter of the record(type, name) statement with an asterisk -character inside double-quotes, "*" instead. Thus the following is a -legal database file:

- -
record(ao, "ao1") {}
-record("*", "ao1") {
-    field(VAL, 10)
-}
- -

Note that database configuration tools will not be expected to have to -understand this syntax, which is provided for scripted and hand-coded database -and template instantiation only. Setting the IOC's dbRecordsOnceOnly -flag also makes this syntax illegal, since its purpose is to prevent -multiply-defined records from being collapsed into a single instance.

- -

Added echo command to iocsh

- -

The single argument string may contain escaped characters, which will be -translated to their raw form before being printed (enclose the string in quotes -to avoid double-translation). A newline is always appended to the output, and -output stream redirection is supported.

- -

Added macro EPICS_UNUSED to compilerDependencies.h

- -

To prevent the compiler from warning about a known-unused variable, mark it -with the macro EPICS_UNUSED. On gcc and clang this will expand to -__attribute__((unused)) to prevent the warning.

- -

User specified db substitution file suffix

- -

Per Dirk Zimoch's suggestion, a user specified db substitution file suffix is -now allowed by setting the variable SUBST_SUFFIX in a configuration directory -CONFIG_SITE file or in a Makefile before the include $(TOP)/configure/RULES -line. The default for SUBST_SUFFIX is .substitutions

- -

NTP Time Provider adjusts to OS tick rate changes

- -

Dirk Zimoch provided code that allows the NTP Time provider (used on VxWorks -and RTEMS only) to adapt to changes in the OS clock tick rate after the provider -has been initialized. Note that changing the tick rate after iocInit() is not -advisable, and that other software might still misbehave if initialized before -an OS tick rate change.

- -

Added newEpicsMutex macro

- -

Internal C++ uses of new epicsMutex() have been replaced with a new -macro which calls a new constructor, passing it the file name and line number of -the mutex creation code. C code that creates mutexes has been using a similar -macro for a long time, but there was no equivalent constructor for the C++ -wrapper class, so identifying a specific mutex was much harder to do.

- -

Post DBE_PROPERTY events automatically

- -

A new record field attribute "prop(YES)" has been added to identify fields -holding meta-data. External changes to these fields will cause a CA monitor -event to be sent to all record subscribers who have asked for DBE_PROPERTY -updates. Meta-data fields have been marked for all Base record types.

- -

errlogRemoveListener() routine changed

- -

Code that calls errlogRemoveListener(myfunc) must be modified to use -the new, safer routine errlogRemoveListeners(myfunc, &pvt) instead. -The replacement routine takes a second argument which must be the same private -pointer that was passed to errlogAddListener() when adding that -listener. It also deletes all matching listeners (hence the new plural name) and -returns how many were actually deleted, whereas the previous routine only -removed the first listener that matched.

- -

Simplified generation of .dbd files

- -

The Perl script makeIncludeDbd.pl has been removed and the rules -that created an intermediate xxxInclude.dbd file from the -Makefile variable xxx_DBD have been modified to generate the target -xxx.dbd file directly. This should simplify applications that -might have had to provide dependency rules for the intermediate files in 3.15. -Applications which provide their own xxxInclude.dbd source file -will continue to have it expanded as before.

- -

New Undefined Severity field UDFS

- -

A new field has been added to dbCommon which configures the alarm severity -associated with the record being undefined (when UDF=TRUE). The default value is -INVALID so old databases will not be affected, but now individual records can be -configured to have a lower severity or even no alarm when undefined. Be careful -when changing this on applications where the IVOA field of output records is -used, IVOA still requires an INVALID severity to trigger value replacement.

- -

New build target tapfiles

- -

This new make target runs the same tests as the runtests target, but -instead of summarizing or displaying the output for each test script it creates -a .tap file inside the architecture build directory which contains the -detailed test output. The output file can be parsed by continuous integration -packages such as Jenkins to show the -test results.

- -

Array field double-buffering

- -

Array data can now be moved, without copying, into and out of the VAL field -of the waveform, aai, and aao record types by replacing the pointer in BPTR. -The basic rules which device support must follow are:

- -
    -
  1. BPTR, and the memory it is currently pointing to, can only be accessed - while the record is locked.
  2. -
  3. NELM may not be changed; NORD should be updated whenever the number of - valid data elements changes.
  4. -
  5. When BPTR is replaced it must always point to a block of memory large - enough to hold the maximum number of elements, as given by the NELM and - FTVL fields.
  6. -
- -

Spin-locks API added

- -

The new header file epicsSpin.h adds a portable spin-locks API which is -intended for locking very short sections of code (typically one or two lines of -C or C++) to provide a critical section that protects against race conditions. -On Posix platforms this uses the pthread_spinlock_t type if it's available and -the build is not configured to use Posix thread priorities, but otherwise it -falls back to a pthread_mutex_t. On the UP VxWorks and RTEMS platforms the -implementations lock out CPU interrupts and disable task preemption while a -spin-lock is held. The default implementation (used when no other implementation -is provided) uses an epicsMutex. Spin-locks may not be taken recursively, and -the code inside the critical section should be short and deterministic.

- -

Improvements to aToIPAddr()

- -

The libCom routine aToIPAddr() and the vxWorks implementation of the -associated hostToIPAddr() function have been modified to be able to look up -hostnames that begin with one or more digits. The epicsSockResolveTest program -was added to check this functionality.

- -

mbboDirect and mbbiDirect records

- -

These record types have undergone some significant rework, and will behave -slightly differently than they did in their 3.14 versions. The externally -visible changes are as follows:

- -
mbbiDirect
- -
    -
  • If the MASK field is set in a database file, it will not be over-written - when the record is initialized. This allows non-contiguous masks to be set, - although only the device support actually uses the MASK field.
  • -
  • If process() finds the UDF field to be set, the record will raise a - UDF/INVALID alarm.
  • -
- -
mbboDirect
- -
    -
  • If the MASK field is set in a database file, it will not be over-written - when the record is initialized. This allows non-contiguous masks to be set, - although only the device support actually uses the MASK field.
  • -
  • After the device support's init_record() routine returns during record - initialization, if OMSL is supervisory and UDF is clear the fields - B0-BF will be set from the current VAL field.
  • -
  • When a put to the OMSL field sets it to supervisory, the fields - B0-BF will be set from the current VAL field. This did not used to happen, - the individual bit fields were previously never modified by the record. - Note that this change may require some databases to be modified, if they - were designed to take advantage of the previous behavior.
  • -
- -

Redirection of the errlog console stream

- -

A new routine has been added to the errlog facility which allows the console -error message stream to be redirected from stderr to some other already open -file stream:

- -
int errlogSetConsole(FILE *stream);
-
- -

The stream argument must be a FILE* pointer as returned by fopen() that is -open for output. If NULL is passed in, the errlog thread's stderr output stream -will be used instead. Note that messages to the console can be disabled and -re-enabled using the eltc routine which is also an iocsh command, but there is -no iocsh command currently provided for calling errlogSetConsole.

- -

Add cleanup subroutine to aSub record

- -

An aSub routine may set the CADR field with a function pointer which will be -run before a new routine in the event that a change to the SNAM field changes -the record's process subroutine.

- -

This can be used to free any resources the routine needs to allocate. It can -also be used to determine if this is the first time this routine has been called -by this record instance. The CADR field is set to NULL immediately after the -routine it points to is called.

- -

Example:

- -
void cleanup(aSubRecord* prec) {
-    free(prec->dpvt);
-    prec->dpvt = NULL;
-}
-
-long myAsubRoutine(aSubRecord* prec) {
-    if (!prec->cadr) {
-        /* check types of inputs and outputs */
-        if (prec->ftva != menuFtypeDOUBLE)
-            return 1; /* oops */
-
-        dpvt = malloc(42);
-        prec->cadr = &cleanup;
-    }
-
-    /* normal processing */
-}
-epicsRegisterFunction(myAsubRoutine);
-
- -

Sequence record enhancements

- -

The sequence record type now has 16 link groups numbered 0 through 9 and A -through F, instead of the previous 10 groups numbered 1 through 9 and A. The -changes to this record are directly equivalent to those described below for the -fanout record. The fields OFFS and SHFT have been added and operate on the SELN -value exactly the same way. The result is backwards compatible with the 3.14 -version of the sequence record as long as none of the new fields are modified -and the application does not rely on the SOFT/INVALID alarm that was generated -when the selection number exceeded 10. The record also now posts monitors on the -SELN field at the end of the sequence if its value changed when read through the -SELL link. - -

Fanout record enhancements

- -

The fanout record type now has 16 output links LNK0-LNK9 and LNKA-LNKF, plus -two additional fields which make the result backwards compatible with 3.14 -databases, but also allow the link selection to be shifted without having to -process the SELN value through a calc or calcout record first.

- -

Previously there was no LNK0 field, so when SELM is Mask bit 0 of SELN -controls whether the LNK1 link field was activated; bit 1 controls LNK2 and so -on. When SELM is Specified and SELN is zero no output link would be -activated at all; LNK1 gets activated when SELN is 1 and so on. Only 6 links -were provided, LNK1 through LNK6. The updated record type maintains the original -behavior when the new fields are not configured, except that the SOFT/INVALID -alarm is not generated when SELN is 7 through 15.

- -

The update involved adding a LNK0 field, as well as fields LNK7 through LNK9 -and LNKA through LNKF. To add flexibility and maintain backwards compatibility, -two additional fields have been added:

- -
-
OFFS
- -
This field holds a signed offset which is added to SELN to select which link -to activate when SELM is Specified. If the resulting value is outside the -range 0 .. 15 the record will go into a SOFT/INVALID alarm state. The default -value of OFFS is zero, so if it is not explicitly set and SELN is 1 the LNK1 -link will be activated.
- -
SHFT
- -
When SELM is Mask the signed field SHFT is used to shift the SELN -value by SHFT bits (positive means right-wards, values outside the range -15 .. -15 will result in a SOFT/INVALID alarm), before using the resulting bit-pattern -to control which links to activate. The default value is -1, so if SHFT is not -explicitly set bit 0 of SELN will be used to control whether LNK1 gets -activated.
- -
- -

The record also now posts monitors on the SELN field if it changes as a -result of record processing (i.e. when read through the SELL link).

- -

Deleted Java build rules

- -

Java has its own build systems now, so we've deleted the rules and associated -variables from Base, although they might get added to the Extensions build rules -for a while in case anyone still needs them.

- -

Changes between 3.14.x and 3.15.0.1

- -

Application clean rules

- -

The clean Makefile target has changed between a single-colon rule -and a double-colon rule more than once in the life of the EPICS build rules, and -it just changed back to a single-colon rule, but now we recommend that -applications that wish to provide a Makefile that is backwards compatible with -the 3.14 build rules use the construct shown below. The 3.15 rules now support -a variable called CLEANS to which a Makefile can add a list of files to -be deleted when the user does a make clean like this:

- -
CLEANS += <list of files to be cleaned>
-
-ifndef BASE_3_15
-clean::
-	$(RM) $(CLEANS)
-endif
- -

The conditional rule provides compatibility for use with the 3.14 build -system.

- -

MSI included with Base

- -

An enhanced version of the Macro Substitution and Include program msi -has been included with Base. Both this new version of msi and the IOC's -dbLoadTemplates command now support setting global macros in -substitution files, and dbLoadTemplates can now take a list of global -macro settings as the second argument on its command line. The substitution file -syntax is documented in the Application Developers Guide.

- -

Cross-builds targeting win32-x86-mingw

- -

Some Linux distributions now package the MinGW cross-compiler which makes it -possible to cross-build the win32-x86-mingw target from a linux-x86 host. Build -configuration files for this combination are now included; adjust the settings -in configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw and add win32-x86-mingw to -the CROSS_COMPILER_TARGET_ARCHS variable in configure/CONFIG_SITE or in -configure/os/CONFIG_SITE.linux-x86.Common.

- -

Architecture win32-x86-cygwin Removed

- -

The ability to compile non-cygwin binaries using the Cygwin build tools is no -longer supported by current versions of Cygwin, so this architecture has been -removed. Use the MinWG tools and the win32-x86-mingw architecture instead.

- -

RTEMS and VxWorks Test Harnesses

- -

The original libCom test harness has been renamed libComTestHarness, -and two additional test harnesses have been created dbTestHarness and -filterTestHarness which are all built for RTEMS and vxWorks targets. -The new ones include tests in src/ioc/db/test and src/std/filters/test.

- -

Running the new tests requires additional .db and .dbd files to be loaded at -runtime, which can be found in the relevant source directory or its O.Common -subdirectory. If the target can access the Base source tree directly it may be -simplest to cd to the relevant source directory before running the test. If not, -the files needed are listed in the generated 'testspec' file found in the -associated build (O.arch) directory.

- -

For RTEMS users the current directory is determined in a BSP specific way. -See rtems_init.c and setBootConfigFromNVRAM.c in src/libCom/RTEMS.

- -

New API to hook into thread creation

- -

A hook API has been added allowing user-supplied functions to be called -whenever a thread starts. The calls are made from the thread's context, -and can be used to control additional thread properties not handled inside -EPICS base, e.g. setting the scheduling policy or CPU affinity (on SMP -systems).

- -

The API also supports a mapping operation, calling a user-supplied function -for every thread that is currently running.

- -

New scan rate units

- -

Scan rates defined in the menuScan.dbd file may now be specified in seconds, -minutes, hours or Hertz, and plural time units will also be accepted (seconds -are used if no unit is mentioned in the choice string). At iocInit each -scan rate is compared with the OS's clock tick and a warning printed if the -rate is too fast or likely to be more than 10% different to the requested rate. -For example the rates given below are all valid, although non-standard (the -default menuScan choices that come with Base have not been changed):

- -
-
menu(menuScan) {
-    choice(menuScanPassive,     "Passive")
-    choice(menuScanEvent,       "Event")
-    choice(menuScanI_O_Intr,    "I/O Intr")
-    choice(menuScan1_hour,      "1 hour")
-    choice(menuScan0_5_hours, "0.5 hours")
-    choice(menuScan15_minutes, "15 minutes")
-    choice(menuScan5_minutes,   "5 minutes")
-    choice(menuScan1_minute,    "1 minute")
-    choice(menuScan10_seconds, "10 seconds")
-    choice(menuScan5_seconds,   "5 seconds")
-    choice(menuScan2_seconds,   "2 seconds")
-    choice(menuScan1_second,    "1 second")
-    choice(menuScan2_Hertz,     "2 Hertz")
-    choice(menuScan5_Hertz,     "5 Hertz")
-    choice(menuScan10_Hertz,   "10 Hz")
-}
- -

Alarm filtering added to input record types

- -

The record types ai, calc, longin and mbbi have a new alarm filter added to -them. This provides a low-pass filter that can be used to delay the reporting of -alarms caused by the input level passing the HIGH, HIHI, LOW or LOLO values. The -filter is controlled with a new AFTC field that sets the filter's time constant. -The default value for this field is zero, which keeps the record's original -alarm behaviour.

- -

The record must be scanned often enough for the filtering action to work -effectively and the alarm severity can only change when the record is processed, -but that processing does not have to be regular; the filter uses the time since -the record last processed in its calculation. Setting AFTC to a positive number -of seconds will delay the record going into or out of a minor alarm severity or -from minor to major severity until the input signal has been in that range for -that number of seconds.

- -

Post events on Waveform record's NORD field

- -

When the record type or device support modify the NORD field of a waveform -record, the record support code now posts DBE_VALUE and DBE_LOG events for that -field, signalling the array length change to any client monitoring the NORD -field.

- -

Attributes of Non-VAL Fields

- -

Non-VAL fields now report meaningful information for precision, units, -graphic limits, control limits, and alarm limits instead of simply using -PREC, EGU, HOPR, LOPR, DRVL, DRVH, HIHI, HIGH, LOW, and LOLO. All delay -fields have a default precision of 2 digits, units "s" and control limits -of 0 to 100,000 seconds (these precision and limit values can be changed -for each record type as a whole at runtime by updating a registered global -variable). Input fields like A-L of the calc record read their metadata -from the corresponding INPn link if possible.

-

epicsStdioRedirect.h merged into epicsStdio.h

- -

The definitions from the header file epicsStdioRedirect.h have been moved -into epicsStdio.h so all calls to printf(), puts() and putchar() in files that -include that OSI header will now be subject to stdout redirection. In past -releases (3.14.7 and later) it was necessary to request the redirection support -by including the epicsStdioRedirect.h header file. The header file is still -provided, but now it just includes epicsStdio.h.

- -

Named Soft Events

- -

Soft events can now be given meaningful names instead of just using the -numbers 1-255. The EVNT field is now a DBF_STRING. The post_event() API -is now deprecated but still works. It should be replaced by code that in advance -looks up the EVNTPVT event handle associated with the named event by -calling eventNameToHandle(char *), and when that event occurs passes -that handle to the new postEvent(EVNTPVT) routine (which may be called -from interrupt level). A new iocsh command postEvent name will -trigger a named event from the command-line or a startup script (on vxWorks the -expression postEvent(eventNameToHandle("name")) must be used -instead though).

- -

Parallel Builds

- -

-As EPICS sites get computers with more CPUs they report additional bugs in our -parallel build rules. Various issues have been fixed by separating out the build -rules that generate dependency (.d) files, ensuring that they are constructed at -the appropriate time in the build.

- -

-These rule changes can cause additional warning messages to appear when building -support modules. Where an application provides its own Makefile rules it may now -have to add rules to construct an associated dependency file. In many cases -though the change needed is just to replace a dependency for a -target$(OBJ) with the target$(DEP) so this

- -
-    myLib$(OBJ): myLib_lex.c
- -

-becomes

- -
-    myLib$(DEP): myLib_lex.c
- -

-To debug build issues assocated with dependency files, use the command make ---debug=m which tells GNUmake to display information about what it is doing -during the first pass when it updates its makefiles.

- -

-Removed tsDefs.h

- -

-The deprecated tsDefs API was provided for 3.13 compatibility only, and has now -been removed. Convert any remaining code that used it to call the epicsTime API -instead.

- -

-Changes to epicsVersion.h

- -

-The two macros EPICS_UPDATE_LEVEL and EPICS_CVS_SNAPSHOT have -been deleted from the epicsVersion.h file; they were deprecated in R3.14 and can -be replaced with EPICS_PATCH_LEVEL and EPICS_DEV_SNAPSHOT -respectively.

- -

-A new pair of macros has been added to make version number comparisons easier. -Code that will not work with a version of Base before 3.15.0 can now be -written like this to prevent it from compiling:

- -
-#if defined(VERSION_INT) && EPICS_VERSION_INT < VERSION_INT(3,15,0,0)
-#  error EPICS Base R3.15.0 or later is required
-#endif
-
- -

-Added support for iocLogPrefix

- -

-Added a iocLogPrefix command to iocsh. This adds a -prefix to all messages from this IOC (or other log client) as they get sent to the -iocLogServer. This lets sites use the "fac=<facility>" syntax for -displaying the facility, process name etc. in log viewers like the -cmlogviewer.

- -

-Reworked the epicsEvent C & C++ APIs

- -
    -
  • Renamed the enum epicsEventWaitStatus to epicsEventStatus
  • -
  • Defined epicsEventWaitStatus as a macro for epicsEventStatus
  • -
  • Renamed epicsEventWaitOk to epicsEventOk
  • -
  • Renamed epicsEventWaitError to epicsEventError
  • -
  • Defined epicsEventWaitOK and epicsEventWaitError as macros
  • -
  • Added epicsEventTrigger(id) which triggers an event and returns OK or an - error status if the underlying OS primitives report an error
  • -
  • Added epicsEventMustTrigger(id) which halts on error
  • -
  • Defined epicsEventSignal(id) as a macro for epicsEventMustTrigger(id)
  • -
  • Added a new C++ method epicsEvent::trigger() which throws an - epicsEvent::invalidSemaphore in the event of an error
  • -
  • epicsEvent::signal() makes an inline call to epicsEvent::trigger()
  • -
  • epicsEventWait() and epicsEventWaitWithTimeout() now return an error - status if the underlying OS primitives report an error
  • -
  • All the epicsEventMust...() routines are now implemented in the common - libCom/osi/epicsEvent.cpp source file, and call cantProceed() instead of - mis-using assert()
  • -
  • Implemented epicsEventShow() on Posix
  • -
  • Win32: Removed all epicsShareAPI decorations
  • -
- -

-Enabled histogram record type

- -

-The histogram record was not included in the base.dbd file in any 3.14 release, -but has now been added along with its associated soft device support. The build -system now generates the list of all the record.dbd files in base automatically -in src/std/rec/Makefile.

- -

-Reorganization of src/

- -

Reorganization of subdirectories of src/ to better represent the relation -between different parts as described in the following table.

- -

This change also allows the number of libraries built to be reduced to: -libCap5.so, libca.so, libdbCore.so, libdbStaticHost.so, -libCom.so, libcas.so, libdbRecStd.so, and libgdd.so

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ComponentDependencyLibrary nameDescription
src/toolsBuild system scripts
src/libComsrc/toolsComUtility routines and OS-independant API
src/templatesrc/toolsUser application templates (e.g. makeBaseApp)
src/ca/clientsrc/libComcaChannel Access client
src/ca/legacy/gddsrc/ca/clientgddGeneric data layer for PCAS
src/ca/legacy/pcassrc/ca/legacy/gddcasPortable Channel Access Server
src/iocsrc/cadbCoreCore database processing functions
src/stdsrc/iocdbRecStdStandard records, soft device support and the softIoc
- -

-In order to better reflect these relations the following -directories and files were moved as described:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Relocations
PreviousNew
libCom
src/RTEMSsrc/libCom/RTEMS
src/toolsComm/flexsrc/libCom/flex
src/toolsComm/antelopesrc/libCom/yacc
src/dbStatic/alarm.h
.../alarmString.h
src/libCom/misc/
IOC Core Components
src/bptsrc/ioc/bpt
src/dbsrc/ioc/db
src/dbStaticsrc/ioc/dbStatic
src/dbtoolssrc/ioc/dbtemplate
src/miscsrc/ioc/misc
src/registrysrc/ioc/registry
src/rsrvsrc/ioc/rsrv 1
Standard Record Definitions
src/dev/softDevsrc/std/dev
src/recsrc/std/rec
src/softIocsrc/std/softIoc
Channel Access
src/casrc/ca/client
src/catoolssrc/ca/client/tools
src/cap5src/ca/client/perl
src/gddsrc/ca/legacy/gdd
src/cassrc/ca/legacy/pcas
src/excassrc/ca/legacy/pcas/ex
User Templates
src/makeBaseAppsrc/template/base
src/makeBaseExtsrc/template/ext
Dispersed
src/util 2src/ca/client
src/ca/client/test
src/libCom/log
src/as 3src/libCom/as
src/ioc/as
- -

1 -RSRV is built as part of dbCore due to its tight (bidirectional) coupling -with the other database code.

- -

2 -The contents for src/util/ moved to three locations. The caRepeater init script -was moved to src/ca/client/. ca_test is now in src/ca/client/test/. -The iocLogServer was moved into the same directory (src/libCom/log) as -the log client code.

- -

3 -The Access Security code has been divided, with the parts not related to the -database (lexer/parser and trap registration) becoming part of libCom. -The remaining components are included in the dbCore library

- -

-Moved src/RTEMS/base directory

- -

-These files are now found under src/RTEMS.

- -

-Removed 3.13 compatibility

- -

-Removed the 3.13 <top>/config directory and build compatibility rules and -variables, and various conversion documents.

From 0f23784b32f641943d785bbe8288a0cead3e660e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 2 Dec 2014 11:30:37 -0600 Subject: [PATCH 023/204] Tweak to callback API. --- documentation/RELEASE_NOTES.html | 5 +++++ src/ioc/db/callback.h | 18 ++++++++++-------- src/ioc/db/dbScan.c | 6 ++++-- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 8361621c8..b2e5fdc65 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,11 @@ --> +

Callback subsystem API

+ +

Added a new macro callbackGetPriority(prio, callback) to the +callback.h header and removed the need for dbScan.c to reach into the internals +of its CALLBACK objects.

diff --git a/src/ioc/db/callback.h b/src/ioc/db/callback.h index 2732f9d5e..66a949036 100644 --- a/src/ioc/db/callback.h +++ b/src/ioc/db/callback.h @@ -47,14 +47,16 @@ typedef struct callbackPvt { typedef void (*CALLBACKFUNC)(struct callbackPvt*); -#define callbackSetCallback(PFUN,PCALLBACK)\ -( (PCALLBACK)->callback = (PFUN) ) -#define callbackSetPriority(PRIORITY,PCALLBACK)\ -( (PCALLBACK)->priority = (PRIORITY) ) -#define callbackSetUser(USER,PCALLBACK)\ -( (PCALLBACK)->user = (void *)(USER) ) -#define callbackGetUser(USER,PCALLBACK)\ -( (USER) = (void *)((CALLBACK *)(PCALLBACK))->user ) +#define callbackSetCallback(PFUN, PCALLBACK) \ + ( (PCALLBACK)->callback = (PFUN) ) +#define callbackSetPriority(PRIORITY, PCALLBACK) \ + ( (PCALLBACK)->priority = (PRIORITY) ) +#define callbackGetPriority(PRIORITY, PCALLBACK) \ + ( (PRIORITY) = (PCALLBACK)->priority ) +#define callbackSetUser(USER, PCALLBACK) \ + ( (PCALLBACK)->user = (void *) (USER) ) +#define callbackGetUser(USER, PCALLBACK) \ + ( (USER) = (PCALLBACK)->user ) epicsShareFunc void callbackInit(void); epicsShareFunc void callbackShutdown(void); diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index ba927a04b..08c5c4af3 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -813,9 +813,11 @@ static void spawnPeriodic(int ind) static void ioscanCallback(CALLBACK *pcallback) { - ioscan_head *piosh = (ioscan_head *) pcallback->user; - int prio = pcallback->priority; + ioscan_head *piosh; + int prio; + callbackGetUser(piosh, pcallback); + callbackGetPriority(prio, pcallback); scanList(&piosh->iosl[prio].scan_list); if (piosh->cb) piosh->cb(piosh->arg, piosh, prio); From c16c4590ec1e71bc790e54a495b78add7c115b05 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 2 Dec 2014 15:25:09 -0600 Subject: [PATCH 024/204] Replace epicsTimeERROR with specific error status values Introduced error status values for epicsTime routines. The epicsTimeERROR identifier no longer exists, so code that uses it will no longer compile (deliberately). Replace all uses of it with a specific error status value. --- documentation/RELEASE_NOTES.html | 9 +++++++ src/libCom/error/Makefile | 1 + src/libCom/error/errMdef.h | 1 + src/libCom/osi/epicsGeneralTime.c | 40 +++++++++++++++------------- src/libCom/osi/epicsTime.cpp | 22 +++++++-------- src/libCom/osi/epicsTime.h | 11 ++++++-- src/libCom/osi/os/Darwin/osdTime.cpp | 5 ++-- src/libCom/osi/os/RTEMS/osdTime.cpp | 4 +-- src/libCom/osi/os/WIN32/osdTime.cpp | 14 +++++----- src/libCom/osi/os/posix/osdTime.cpp | 7 ++--- src/libCom/osi/osiNTPTime.c | 4 +-- src/std/dev/devGeneralTime.c | 2 +- 12 files changed, 72 insertions(+), 48 deletions(-) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index b2e5fdc65..48dacb281 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,15 @@ --> +

epicsTime API return status

+ +

The epicsTime routines that used to return epicsTimeERROR now return a specific +S_time_ status value, allowing the caller to discover the reason for any failure. +The identifier epicsTimeERROR is no longer defined, so any references to +it in source code will no longer compile. The identifier epicsTimeOK still exists +and has the value 0 as before, so most code that uses these APIs can be changed in +a way that is backwards-compatible with the previous return status.

+

Callback subsystem API

Added a new macro callbackGetPriority(prio, callback) to the diff --git a/src/libCom/error/Makefile b/src/libCom/error/Makefile index bf81074e8..0a99b0def 100644 --- a/src/libCom/error/Makefile +++ b/src/libCom/error/Makefile @@ -22,6 +22,7 @@ Com_SRCS += errSymTbl.c # For bldErrSymTbl # ERR_S_FILES += $(LIBCOM)/osi/devLib.h +ERR_S_FILES += $(LIBCOM)/osi/epicsTime.h ERR_S_FILES += $(LIBCOM)/as/asLib.h ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h ERR_S_FILES += $(LIBCOM)/pool/epicsThreadPool.h diff --git a/src/libCom/error/errMdef.h b/src/libCom/error/errMdef.h index 27b693d8c..e511d912b 100644 --- a/src/libCom/error/errMdef.h +++ b/src/libCom/error/errMdef.h @@ -50,6 +50,7 @@ extern "C" { #define M_gddFuncTbl (526 <<16) /*gdd jump table*/ #define M_stdlib (527 <<16) /*EPICS Standard library*/ #define M_pool (528 <<16) /*Thread pool*/ +#define M_time (529 <<16) /*epicsTime*/ epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength); epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum); diff --git a/src/libCom/osi/epicsGeneralTime.c b/src/libCom/osi/epicsGeneralTime.c index 8ef8a73cc..7f8e1b66f 100644 --- a/src/libCom/osi/epicsGeneralTime.c +++ b/src/libCom/osi/epicsGeneralTime.c @@ -86,7 +86,7 @@ void generalTime_Init(void) int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore) { gtProvider *ptp; - int status = epicsTimeERROR; + int status = S_time_noProvider; generalTime_Init(); @@ -107,6 +107,7 @@ int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore) *pPrio = ptp->priority; } else { int key; + *pDest = gtPvt.lastProvidedTime; if (pPrio) *pPrio = gtPvt.lastTimeProvider->priority; @@ -117,8 +118,7 @@ int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore) break; } } - if (status == epicsTimeERROR && - ignore == 0) + if (status && ignore == 0) gtPvt.lastTimeProvider = NULL; epicsMutexUnlock(gtPvt.timeListLock); @@ -135,7 +135,8 @@ int epicsTimeGetCurrentInt(epicsTimeStamp *pDest) gtProvider *ptp = gtPvt.lastTimeProvider; if (ptp == NULL || - ptp->getInt.Time == NULL) return epicsTimeERROR; + ptp->getInt.Time == NULL) + return S_time_noProvider; return ptp->getInt.Time(pDest); } @@ -145,20 +146,20 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, int *pPrio) { gtProvider *ptp; - int status = epicsTimeERROR; + int status = S_time_noProvider; generalTime_Init(); if ((eventNumber < 0 || eventNumber >= NUM_TIME_EVENTS) && (eventNumber != epicsTimeEventBestTime)) - return status; + return S_time_badEvent; epicsMutexMustLock(gtPvt.eventListLock); for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders); ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { status = ptp->get.Event(pDest, eventNumber); - if (status != epicsTimeERROR) { + if (status == epicsTimeOK) { gtPvt.lastEventProvider = ptp; if (pPrio) *pPrio = ptp->priority; @@ -169,6 +170,7 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, gtPvt.lastProvidedBestTime = *pDest; } else { int key; + *pDest = gtPvt.lastProvidedBestTime; key = epicsInterruptLock(); gtPvt.ErrorCounts++; @@ -180,6 +182,7 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, gtPvt.eventTime[eventNumber] = *pDest; } else { int key; + *pDest = gtPvt.eventTime[eventNumber]; key = epicsInterruptLock(); gtPvt.ErrorCounts++; @@ -189,7 +192,7 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, break; } } - if (status == epicsTimeERROR) + if (status) gtPvt.lastEventProvider = NULL; epicsMutexUnlock(gtPvt.eventListLock); @@ -213,7 +216,8 @@ int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber) gtProvider *ptp = gtPvt.lastEventProvider; if (ptp == NULL || - ptp->getInt.Event == NULL) return epicsTimeERROR; + ptp->getInt.Event == NULL) + return S_time_noProvider; return ptp->getInt.Event(pDest, eventNumber); } @@ -271,11 +275,11 @@ int generalTimeRegisterEventProvider(const char *name, int priority, generalTime_Init(); if (name == NULL || getEvent == NULL) - return epicsTimeERROR; + return S_time_badArgs; ptp = (gtProvider *)malloc(sizeof(gtProvider)); if (ptp == NULL) - return epicsTimeERROR; + return S_time_noMemory; ptp->name = epicsStrDup(name); ptp->priority = priority; @@ -293,7 +297,7 @@ int generalTimeAddIntEventProvider(const char *name, int priority, gtProvider *ptp = findProvider(>Pvt.eventProviders, gtPvt.eventListLock, name, priority); if (ptp == NULL) - return epicsTimeERROR; + return S_time_noProvider; ptp->getInt.Event = getEvent; @@ -308,11 +312,11 @@ int generalTimeRegisterCurrentProvider(const char *name, int priority, generalTime_Init(); if (name == NULL || getTime == NULL) - return epicsTimeERROR; + return S_time_badArgs; ptp = (gtProvider *)malloc(sizeof(gtProvider)); if (ptp == NULL) - return epicsTimeERROR; + return S_time_noMemory; ptp->name = epicsStrDup(name); ptp->priority = priority; @@ -330,7 +334,7 @@ int generalTimeAddIntCurrentProvider(const char *name, int priority, gtProvider *ptp = findProvider(>Pvt.timeProviders, gtPvt.timeListLock, name, priority); if (ptp == NULL) - return epicsTimeERROR; + return S_time_noProvider; ptp->getInt.Time = getTime; @@ -388,7 +392,7 @@ long generalTimeReport(int level) if (!message) { epicsMutexUnlock(gtPvt.timeListLock); printf("Out of memory\n"); - return epicsTimeERROR; + return S_time_noMemory; } pout = message; @@ -399,7 +403,7 @@ long generalTimeReport(int level) ptp->name, ptp->priority); if (level) { epicsTimeStamp tempTS; - if (ptp->get.Time(&tempTS) != epicsTimeERROR) { + if (ptp->get.Time(&tempTS) == epicsTimeOK) { char tempTSText[40]; epicsTimeToStrftime(tempTSText, sizeof(tempTSText), "%Y-%m-%d %H:%M:%S.%06f", &tempTS); @@ -430,7 +434,7 @@ long generalTimeReport(int level) if (!message) { epicsMutexUnlock(gtPvt.eventListLock); printf("Out of memory\n"); - return epicsTimeERROR; + return S_time_noMemory; } pout = message; diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp index b85b67b8e..59514c2f7 100644 --- a/src/libCom/osi/epicsTime.cpp +++ b/src/libCom/osi/epicsTime.cpp @@ -284,7 +284,7 @@ epicsTime::operator local_tm_nano_sec () const local_tm_nano_sec tm; int status = epicsTime_localtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); - if ( status != epicsTimeOK ) { + if ( status ) { throw std::logic_error ( "epicsTime_localtime failed" ); } @@ -303,7 +303,7 @@ epicsTime::operator gm_tm_nano_sec () const gm_tm_nano_sec tm; int status = epicsTime_gmtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); - if ( status != epicsTimeOK ) { + if ( status ) { throw std::logic_error ( "epicsTime_gmtime failed" ); } @@ -891,7 +891,7 @@ extern "C" { *pDest = dst.ts; } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -903,7 +903,7 @@ extern "C" { *pDest = epicsTime ( dst ); } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -915,7 +915,7 @@ extern "C" { *pNSecDest = tmns.nSec; } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -927,7 +927,7 @@ extern "C" { *pNSecDest = gmtmns.nSec; } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -940,7 +940,7 @@ extern "C" { *pDest = epicsTime (tmns); } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -950,7 +950,7 @@ extern "C" { *pDest = epicsTime (*pSrc); } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -960,7 +960,7 @@ extern "C" { *pDest = epicsTime (*pSrc); } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -970,7 +970,7 @@ extern "C" { *pDest = epicsTime (*pSrc); } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } @@ -980,7 +980,7 @@ extern "C" { *pDest = epicsTime (*pSrc); } catch (...) { - return epicsTimeERROR; + return S_time_conversion; } return epicsTimeOK; } diff --git a/src/libCom/osi/epicsTime.h b/src/libCom/osi/epicsTime.h index a36d6afe3..bf8f77c87 100644 --- a/src/libCom/osi/epicsTime.h +++ b/src/libCom/osi/epicsTime.h @@ -17,6 +17,7 @@ #include "shareLib.h" #include "epicsTypes.h" #include "osdTime.h" +#include "errMdef.h" /* The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC */ #define POSIX_TIME_AT_EPICS_EPOCH 631152000u @@ -170,9 +171,15 @@ private: extern "C" { #endif /* __cplusplus */ -/* All epicsTime routines return (-1,0) for (failure,success) */ +/* epicsTime routines return S_time_ error status values */ #define epicsTimeOK 0 -#define epicsTimeERROR (-1) +#define S_time_noProvider (M_time| 1) /*No time provider*/ +#define S_time_badEvent (M_time| 2) /*Bad event number*/ +#define S_time_badArgs (M_time| 3) /*Invalid arguments*/ +#define S_time_noMemory (M_time| 4) /*Out of memory*/ +#define S_time_unsynchronized (M_time| 5) /*Provider not synchronized*/ +#define S_time_timezone (M_time| 6) /*Invalid timeone*/ +#define S_time_conversion (M_time| 7) /*Time conversion error*/ /*Some special values for eventNumber*/ #define epicsTimeEventCurrentTime 0 diff --git a/src/libCom/osi/os/Darwin/osdTime.cpp b/src/libCom/osi/os/Darwin/osdTime.cpp index f3e6db5dd..c4a7e838c 100644 --- a/src/libCom/osi/os/Darwin/osdTime.cpp +++ b/src/libCom/osi/os/Darwin/osdTime.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -52,13 +53,13 @@ static int done = timeRegister(); int epicsTime_gmtime(const time_t *pAnsiTime, struct tm *pTM) { return gmtime_r(pAnsiTime, pTM) ? - epicsTimeOK : epicsTimeERROR; + epicsTimeOK : errno; } int epicsTime_localtime(const time_t *clock, struct tm *result) { return localtime_r(clock, result) ? - epicsTimeOK : epicsTimeERROR; + epicsTimeOK : errno; } extern "C" epicsShareFunc void diff --git a/src/libCom/osi/os/RTEMS/osdTime.cpp b/src/libCom/osi/os/RTEMS/osdTime.cpp index acefa1b09..b233ec1e5 100644 --- a/src/libCom/osi/os/RTEMS/osdTime.cpp +++ b/src/libCom/osi/os/RTEMS/osdTime.cpp @@ -91,7 +91,7 @@ int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) return epicsTimeOK; } else { - return epicsTimeERROR; + return errno; } } @@ -102,7 +102,7 @@ int epicsTime_localtime ( const time_t *clock, struct tm *result ) return epicsTimeOK; } else { - return epicsTimeERROR; + return errno; } } diff --git a/src/libCom/osi/os/WIN32/osdTime.cpp b/src/libCom/osi/os/WIN32/osdTime.cpp index 877311ffa..9d584879c 100644 --- a/src/libCom/osi/os/WIN32/osdTime.cpp +++ b/src/libCom/osi/os/WIN32/osdTime.cpp @@ -164,7 +164,7 @@ int epicsShareAPI epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) SYSTEMTIME st; BOOL status = FileTimeToSystemTime ( &ft, &st ); if ( ! status ) { - return epicsTimeERROR; + return S_time_conversion; } pTM->tm_sec = st.wSecond; // seconds after the minute - [0,59] @@ -193,7 +193,7 @@ int epicsShareAPI epicsTime_localtime ( TIME_ZONE_INFORMATION tzInfo; DWORD tzStatus = GetTimeZoneInformation ( & tzInfo ); if ( tzStatus == TIME_ZONE_ID_INVALID ) { - return epicsTimeERROR; + return S_time_timezone; } // @@ -204,13 +204,13 @@ int epicsShareAPI epicsTime_localtime ( SYSTEMTIME st; BOOL success = FileTimeToSystemTime ( & ft, & st ); if ( ! success ) { - return epicsTimeERROR; + return S_time_conversion; } SYSTEMTIME lst; success = SystemTimeToTzSpecificLocalTime ( & tzInfo, & st, & lst ); if ( ! success ) { - return epicsTimeERROR; + return S_time_conversion; } // @@ -220,7 +220,7 @@ int epicsShareAPI epicsTime_localtime ( FILETIME lft; success = SystemTimeToFileTime ( & lst, & lft ); if ( ! success ) { - return epicsTimeERROR; + return S_time_conversion; } int is_dst = -1; // unknown state of dst @@ -234,14 +234,14 @@ int epicsShareAPI epicsTime_localtime ( success = SystemTimeToFileTime ( & tzInfo.StandardDate, & StandardDateFT ); if ( ! success ) { - return epicsTimeERROR; + return S_time_conversion; } tzInfo.DaylightDate.wYear = st.wYear; FILETIME DaylightDateFT; success = SystemTimeToFileTime ( & tzInfo.DaylightDate, & DaylightDateFT ); if ( ! success ) { - return epicsTimeERROR; + return S_time_conversion; } if ( CompareFileTime ( & lft, & DaylightDateFT ) >= 0 && CompareFileTime ( & lft, & StandardDateFT ) < 0 ) { diff --git a/src/libCom/osi/os/posix/osdTime.cpp b/src/libCom/osi/os/posix/osdTime.cpp index 679c6e376..7713a2c25 100644 --- a/src/libCom/osi/os/posix/osdTime.cpp +++ b/src/libCom/osi/os/posix/osdTime.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "osiSock.h" @@ -36,7 +37,7 @@ struct timezone tz; if (gettimeofday (&tv, &tz)) - return epicsTimeERROR; + return errno; *pDest = epicsTime(tv); return epicsTimeOK; @@ -68,7 +69,7 @@ int epicsTime_gmtime ( const time_t *pAnsiTime, return epicsTimeOK; } else { - return epicsTimeERROR; + return errno; } } @@ -80,7 +81,7 @@ int epicsTime_localtime ( const time_t *clock, return epicsTimeOK; } else { - return epicsTimeERROR; + return errno; } } diff --git a/src/libCom/osi/osiNTPTime.c b/src/libCom/osi/osiNTPTime.c index 64362b2e8..d22df861b 100644 --- a/src/libCom/osi/osiNTPTime.c +++ b/src/libCom/osi/osiNTPTime.c @@ -164,7 +164,7 @@ static void NTPTimeSync(void *dummy) } if (timespecNow.tv_sec <= POSIX_TIME_AT_EPICS_EPOCH || - epicsTimeFromTimespec(&timeNow, ×pecNow) == epicsTimeERROR) { + epicsTimeFromTimespec(&timeNow, ×pecNow) != epicsTimeOK) { errlogPrintf("NTPTimeSync: Bad time received from NTP server\n"); NTPTimePvt.synchronized = 0; continue; @@ -213,7 +213,7 @@ static int NTPTimeGetCurrent(epicsTimeStamp *pDest) epicsUInt32 ticksSince; if (!NTPTimePvt.synchronized) - return epicsTimeERROR; + return S_time_unsynchronized; epicsMutexMustLock(NTPTimePvt.lock); diff --git a/src/std/dev/devGeneralTime.c b/src/std/dev/devGeneralTime.c index c656c85db..0cce00d4b 100644 --- a/src/std/dev/devGeneralTime.c +++ b/src/std/dev/devGeneralTime.c @@ -37,7 +37,7 @@ static int getCurrentTime(double * pseconds) { epicsTimeStamp ts; - if (epicsTimeERROR != epicsTimeGetCurrent(&ts)) { + if (epicsTimeOK == epicsTimeGetCurrent(&ts)) { *pseconds = ts.secPastEpoch + ((double)(ts.nsec)) * 1e-9; return 0; } From 4518e8fa7fb6b347d05524119b70c01ed17c5163 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 29 Dec 2014 18:16:26 -0500 Subject: [PATCH 025/204] dbLink: re-intorduce struct lset and dbDb_lset" without initLink, addLink, and loadLink --- src/ioc/db/dbLink.c | 6 ++++++ src/ioc/db/dbLink.h | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index bc4de8ec6..b42504139 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -359,6 +359,12 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } +lset dbDb_lset = { dbDbRemoveLink, + dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, + dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, + dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, + dbDbPutValue, dbDbScanFwdLink }; + /***************************** Generic Link API *****************************/ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index 68a803dbb..8a5eb2573 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -26,6 +26,27 @@ extern "C" { #endif +typedef struct lset { + void (*removeLink)(struct link *plink); + int (*isLinkConnected)(const struct link *plink); + int (*getDBFtype)(const struct link *plink); + long (*getElements)(const struct link *plink, long *nelements); + long (*getValue)(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); + long (*getControlLimits)(const struct link *plink, double *lo, double *hi); + long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); + long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, + double *hi, double *hihi); + long (*getPrecision)(const struct link *plink, short *precision); + long (*getUnits)(const struct link *plink, char *units, int unitsSize); + long (*getAlarm)(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity); + long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); + long (*putValue)(struct link *plink, short dbrType, + const void *pbuffer, long nRequest); + void (*scanFwdLink)(struct link *plink); +} lset; + #define dbGetSevr(PLINK, PSEVERITY) \ dbGetAlarm((PLINK), NULL, (PSEVERITY)); From 7d50f62aed5a60caf58d143dd0da63f5c7d4071d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 026/204] dbScan: allow direct scan of I/O Intr list add scanIoImmediate() to allow drivers to implement I/O Intr scanning without the overhead of maintaining their own scan lists. --- src/ioc/db/dbScan.c | 22 ++++++++++++++++++++++ src/ioc/db/dbScan.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 08c5c4af3..272b185b0 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -580,6 +580,28 @@ unsigned int scanIoRequest(IOSCANPVT piosh) return queued; } +unsigned int scanIoImmediate(IOSCANPVT piosh, int prio) +{ + io_scan_list *piosl; + + if (prio<0 || prio>=NUM_CALLBACK_PRIORITIES) + return S_db_errArg; + else if (scanCtl != ctlRun) + return 0; + + piosl = &piosh->iosl[prio]; + + if (ellCount(&piosl->scan_list.list) == 0) + return 0; + + scanList(&piosl->scan_list); + + if (piosh->cb) + piosh->cb(piosh->arg, piosh, prio); + + return 1 << prio; +} + /* May not be called while a scan request is queued or running */ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg) { diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index cd9666348..8bd2bba22 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -68,6 +68,7 @@ epicsShareFunc int scanpiol(void); epicsShareFunc void scanIoInit(IOSCANPVT *ppios); epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios); +epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio); epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr); #ifdef __cplusplus From 2ba2b90b06b21ed0dbcf0fa29278104b7efc9d3c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 027/204] dbScan: add scanOnce3() w/ completion callback --- src/ioc/db/dbScan.c | 55 ++++++++++++++++++++++++++++++++------------- src/ioc/db/dbScan.h | 8 ++++--- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 272b185b0..3cd93da1a 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -26,10 +26,9 @@ #include "ellLib.h" #include "epicsEvent.h" #include "epicsExit.h" -#include "epicsInterrupt.h" #include "epicsMutex.h" #include "epicsPrint.h" -#include "epicsRingPointer.h" +#include "epicsRingBytes.h" #include "epicsStdio.h" #include "epicsStdlib.h" #include "epicsString.h" @@ -64,7 +63,7 @@ static volatile enum ctl scanCtl; static int onceQueueSize = 1000; static epicsEventId onceSem; -static epicsRingPointerId onceQ; +static epicsRingBytesId onceQ; static epicsThreadId onceTaskId; static void *exitOnce; @@ -172,7 +171,7 @@ void scanShutdown(void) deletePeriodic(); ioscanDestroy(); - epicsRingPointerDelete(onceQ); + epicsRingBytesDelete(onceQ); epicsEventDestroy(startStopEvent); epicsEventDestroy(onceSem); @@ -609,15 +608,27 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg) piosh->arg = arg; } -void scanOnce(struct dbCommon *precord) +int scanOnce(struct dbCommon *precord) { + return scanOnce3(precord, NULL, NULL); +} + +typedef struct { + struct dbCommon *prec; + once_complete cb; + void *usr; +} onceEntry; + +int scanOnce3(struct dbCommon *precord, once_complete cb, void *usr) { static int newOverflow = TRUE; - int lockKey; + onceEntry ent; int pushOK; - lockKey = epicsInterruptLock(); - pushOK = epicsRingPointerPush(onceQ, precord); - epicsInterruptUnlock(lockKey); + ent.prec = precord; + ent.cb = cb; + ent.usr = usr; + + pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent)); if (!pushOK) { if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); @@ -626,6 +637,8 @@ void scanOnce(struct dbCommon *precord) newOverflow = TRUE; } epicsEventSignal(onceSem); + + return !pushOK; } static void onceTask(void *arg) @@ -634,14 +647,24 @@ static void onceTask(void *arg) epicsEventSignal(startStopEvent); while (TRUE) { - void *precord; epicsEventMustWait(onceSem); - while ((precord = epicsRingPointerPop(onceQ))) { - if (precord == &exitOnce) goto shutdown; - dbScanLock(precord); - dbProcess(precord); - dbScanUnlock(precord); + while(1) { + onceEntry ent; + int bytes = epicsRingBytesGet(onceQ, (void*)&ent, sizeof(ent)); + if(bytes==0) + break; + if(bytes!=sizeof(ent)) { + errlogPrintf("onceTask: received incomplete %d of %u\n", + bytes, (unsigned)sizeof(ent)); + continue; /* what to do? */ + } else if (ent.prec == (void*)&exitOnce) goto shutdown; + + dbScanLock(ent.prec); + dbProcess(ent.prec); + dbScanUnlock(ent.prec); + if(ent.cb) + ent.cb(ent.usr, ent.prec); } } @@ -658,7 +681,7 @@ int scanOnceSetQueueSize(int size) static void initOnce(void) { - if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) { + if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { cantProceed("initOnce: Ring buffer create failed\n"); } onceSem = epicsEventMustCreate(epicsEventEmpty); diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index 8bd2bba22..fffae23d1 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -39,10 +39,11 @@ struct ioscan_head; typedef struct ioscan_head *IOSCANPVT; typedef struct event_list *EVENTPVT; -typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); - struct dbCommon; +typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); +typedef void (*once_complete)(void *usr, struct dbCommon*); + epicsShareFunc long scanInit(void); epicsShareFunc void scanRun(void); epicsShareFunc void scanPause(void); @@ -54,7 +55,8 @@ epicsShareFunc void post_event(int event) EPICS_DEPRECATED; epicsShareFunc void scanAdd(struct dbCommon *); epicsShareFunc void scanDelete(struct dbCommon *); epicsShareFunc double scanPeriod(int scan); -epicsShareFunc void scanOnce(struct dbCommon *); +epicsShareFunc int scanOnce(struct dbCommon *); +epicsShareFunc int scanOnce3(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); /*print periodic lists*/ From ff14d811a723c16f1e6950c7e085cce346d8fbc6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 028/204] dbCaPvt.h: normalize indentation --- src/ioc/db/dbCaPvt.h | 100 +++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index cab596dba..584c53da3 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -38,56 +38,56 @@ typedef struct caLink { - ELLNODE node; - epicsMutexId lock; - struct link *plink; - char *pvname; - chid chid; - short link_action; - /* The following have new values after each data event*/ - epicsEnum16 sevr; - epicsEnum16 stat; - epicsTimeStamp timeStamp; - /* The following have values after connection*/ - short dbrType; - long nelements; - char hasReadAccess; - char hasWriteAccess; - char isConnected; - char gotFirstConnection; - /* The following are for dbCaAddLinkCallback */ - dbCaCallback connect; - dbCaCallback monitor; - void *userPvt; - /* The following are for write request */ - short putType; - dbCaCallback putCallback; - void *putUserPvt; - struct link *plinkPutCallback; - /* The following are for access to additional attributes*/ - char gotAttributes; - dbCaCallback getAttributes; - void *getAttributesPvt; - /* The following have values after getAttribEventCallback*/ - double controlLimits[2]; - double displayLimits[2]; - double alarmLimits[4]; - short precision; - char units[MAX_UNITS_SIZE]; /* units of value */ - /* The following are for handling data*/ - void *pgetNative; - char *pgetString; - void *pputNative; - char *pputString; - char gotInNative; - char gotInString; - char gotOutNative; - char gotOutString; - char newOutNative; - char newOutString; - /* The following are for dbcar*/ - unsigned long nDisconnect; - unsigned long nNoWrite; /*only modified by dbCaPutLink*/ + ELLNODE node; + epicsMutexId lock; + struct link *plink; + char *pvname; + chid chid; + short link_action; + /* The following have new values after each data event*/ + epicsEnum16 sevr; + epicsEnum16 stat; + epicsTimeStamp timeStamp; + /* The following have values after connection*/ + short dbrType; + long nelements; + char hasReadAccess; + char hasWriteAccess; + char isConnected; + char gotFirstConnection; + /* The following are for dbCaAddLinkCallback */ + dbCaCallback connect; + dbCaCallback monitor; + void *userPvt; + /* The following are for write request */ + short putType; + dbCaCallback putCallback; + void *putUserPvt; + struct link *plinkPutCallback; + /* The following are for access to additional attributes*/ + char gotAttributes; + dbCaCallback getAttributes; + void *getAttributesPvt; + /* The following have values after getAttribEventCallback*/ + double controlLimits[2]; + double displayLimits[2]; + double alarmLimits[4]; + short precision; + char units[MAX_UNITS_SIZE]; /* units of value */ + /* The following are for handling data*/ + void *pgetNative; + char *pgetString; + void *pputNative; + char *pputString; + char gotInNative; + char gotInString; + char gotOutNative; + char gotOutString; + char newOutNative; + char newOutString; + /* The following are for dbcar*/ + unsigned long nDisconnect; + unsigned long nNoWrite; /*only modified by dbCaPutLink*/ }caLink; #endif /* INC_dbCaPvt_H */ From d897c9b68616c11e3f431df1b9589059d61bb86c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 029/204] dbCa: use scanOnce3() to prevent once queue overflow Prevent CP links to high rate records from overflowing the once queue. --- src/ioc/db/dbCa.c | 31 ++++++++++++++++++++++++++++--- src/ioc/db/dbCaPvt.h | 1 + 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index e3ce5efac..2e1bf2ebf 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -629,6 +629,31 @@ long dbCaGetUnits(const struct link *plink, return gotAttributes ? 0 : -1; } +static void scanComplete(void *raw, dbCommon *prec) +{ + caLink *pca = raw; + epicsMutexMustLock(pca->lock); + if(pca->scanningOnce==0) + errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); + else if(--pca->scanningOnce){ + /* another scan is queued */ + if(scanOnce3(prec, scanComplete, raw)) { + errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); + } + } + epicsMutexUnlock(pca->lock); +} + +/* must be called with pca->lock held */ +static void scanLinkOnce(dbCommon *prec, caLink *pca) { + if(pca->scanningOnce==0 && scanOnce3(prec, scanComplete, pca)) { + errlogPrintf("dbCa.c failed to queue scanOnce\n"); + } + if(pca->scanningOnce<5) + pca->scanningOnce++; + /* else too many scans queued */ +} + static void connectionCallback(struct connection_handler_args arg) { caLink *pca; @@ -649,7 +674,7 @@ static void connectionCallback(struct connection_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanOnce(precord); + scanLinkOnce(precord, pca); goto done; } pca->hasReadAccess = ca_read_access(arg.chid); @@ -762,7 +787,7 @@ static void eventCallback(struct event_handler_args arg) if ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)) - scanOnce(precord); + scanLinkOnce(precord, pca); } done: epicsMutexUnlock(pca->lock); @@ -835,7 +860,7 @@ static void accessRightsCallback(struct access_rights_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanOnce(precord); + scanLinkOnce(precord, pca); done: epicsMutexUnlock(pca->lock); } diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index 584c53da3..219da5a51 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -85,6 +85,7 @@ typedef struct caLink char gotOutString; char newOutNative; char newOutString; + unsigned char scanningOnce; /* The following are for dbcar*/ unsigned long nDisconnect; unsigned long nNoWrite; /*only modified by dbCaPutLink*/ From 6d7a0327e03289978b4708a8b7e2b557b5352959 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 030/204] dbScanTest: start with test of scanOnce3() --- src/ioc/db/test/Makefile | 6 +++ src/ioc/db/test/dbScanTest.c | 73 +++++++++++++++++++++++++++++++ src/ioc/db/test/epicsRunDbTests.c | 2 + 3 files changed, 81 insertions(+) create mode 100644 src/ioc/db/test/dbScanTest.c diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index ef0927c26..f1bdbf33e 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -28,6 +28,12 @@ testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp PROD_LIBS = dbTestIoc dbCore ca Com +TESTPROD_HOST += dbScanTest +dbScanTest_SRCS += dbScanTest.c +dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbScanTest.c +TESTS += dbScanTest + TESTPROD_HOST += dbShutdownTest dbShutdownTest_SRCS += dbShutdownTest.c dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp diff --git a/src/ioc/db/test/dbScanTest.c b/src/ioc/db/test/dbScanTest.c new file mode 100644 index 000000000..f9f1f8e44 --- /dev/null +++ b/src/ioc/db/test/dbScanTest.c @@ -0,0 +1,73 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + */ + +#include + +#include "dbScan.h" +#include "epicsEvent.h" + +#include "dbUnitTest.h" +#include "testMain.h" + +#include "dbAccess.h" +#include "errlog.h" + +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static epicsEventId waiter; +static int called; +static dbCommon *prec; + +static void onceComp(void *junk, dbCommon *prec) +{ + testOk1(junk==(void*)&waiter); + testOk1(strcmp(prec->name, "reca")==0); + called = 1; + epicsEventMustTrigger(waiter); +} + +static void testOnce(void) +{ + testDiag("check scanOnce3() callback"); + waiter = epicsEventMustCreate(epicsEventError); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + prec = testdbRecordPtr("reca"); + + testDiag("scanOnce %s", prec->name); + scanOnce3(prec, onceComp, &waiter); + testDiag("Waiting"); + epicsEventMustWait(waiter); + testOk1(called==1); + if(!called) + testSkip(2, "callback failed to run"); + + testIocShutdownOk(); + + testdbCleanup(); + epicsEventDestroy(waiter); +} + +MAIN(dbScanTest) +{ + testPlan(3); + testOnce(); + return testDone(); +} diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index 05fedeb51..0ee631686 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -22,6 +22,7 @@ int callbackParallelTest(void); int dbStateTest(void); int dbCaStatsTest(void); int dbShutdownTest(void); +int dbScanTest(void); int scanIoTest(void); int dbLockTest(void); int dbPutLinkTest(void); @@ -40,6 +41,7 @@ void epicsRunDbTests(void) runTest(dbStateTest); runTest(dbCaStatsTest); runTest(dbShutdownTest); + runTest(dbScanTest); runTest(scanIoTest); runTest(dbLockTest); runTest(dbPutLinkTest); From b9b8cde5f6aba8e47c068ca579bed7df78142ec1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:48 -0500 Subject: [PATCH 031/204] dbScan: avoid mustAlloc in eventNameToHandle() Can already return NULL for other error conditions --- src/ioc/db/dbScan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 3cd93da1a..9dfeec0bc 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -455,7 +455,9 @@ event_list *eventNameToHandle(const char *eventname) if (strcmp(pel->event_name, eventname) == 0) break; } if (pel == NULL) { - pel = dbCalloc(1, sizeof(event_list)); + pel = calloc(1, sizeof(event_list)); + if (!pel) + goto done; strcpy(pel->event_name, eventname); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]); @@ -473,6 +475,7 @@ event_list *eventNameToHandle(const char *eventname) pevent_list[e] = pel; } } +done: epicsMutexUnlock(event_lock); return pel; } From 85e21863dc062db16639c6888197e9313442065c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 17:43:17 -0500 Subject: [PATCH 032/204] dbStaticLib: set pdbbase=NULL after freeing --- src/ioc/dbStatic/dbStaticLib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 4cf6d4603..e1af4c720 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -588,6 +588,7 @@ void dbFreeBase(dbBase *pdbbase) dbPvdFreeMem(pdbbase); dbFreePath(pdbbase); free((void *)pdbbase); + pdbbase = NULL; return; } From 368223f783a00f5894f41b76344a8e35ba34f2d2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 17:43:45 -0500 Subject: [PATCH 033/204] db/test: devExtend() only during pass 0 --- src/ioc/db/test/dbLinkdset.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/test/dbLinkdset.c b/src/ioc/db/test/dbLinkdset.c index dce258a3f..5e7ea285f 100644 --- a/src/ioc/db/test/dbLinkdset.c +++ b/src/ioc/db/test/dbLinkdset.c @@ -12,9 +12,10 @@ long link_test_extend(struct dbCommon *junk) static dsxt xrecextend = {&link_test_extend, &link_test_extend}; static -long link_test_init(int junk) +long link_test_init(int pass) { - devExtend(&xrecextend); + if (pass == 0) + devExtend(&xrecextend); return 0; } From 92641e91603991147ad6964130bd4ece37a16e02 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 17:45:00 -0500 Subject: [PATCH 034/204] dbCaStatsTest: set # of tests --- src/ioc/db/test/dbCaStatsTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbCaStatsTest.c b/src/ioc/db/test/dbCaStatsTest.c index e9de6ae6f..020c65f10 100644 --- a/src/ioc/db/test/dbCaStatsTest.c +++ b/src/ioc/db/test/dbCaStatsTest.c @@ -67,7 +67,7 @@ void testCaStats(void) { MAIN(dbCaStatsTest) { - testPlan(0); + testPlan(5); testCaStats(); return testDone(); } From bbbf05414da5bcc6ec9a3a897b329588003676db Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 17 Feb 2015 11:31:05 -0500 Subject: [PATCH 035/204] dbScan: rename scanOnce3 to scanOnceCallback --- src/ioc/db/dbCa.c | 4 ++-- src/ioc/db/dbScan.c | 4 ++-- src/ioc/db/dbScan.h | 2 +- src/ioc/db/test/dbScanTest.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 2e1bf2ebf..f6422873b 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -637,7 +637,7 @@ static void scanComplete(void *raw, dbCommon *prec) errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); else if(--pca->scanningOnce){ /* another scan is queued */ - if(scanOnce3(prec, scanComplete, raw)) { + if(scanOnceCallback(prec, scanComplete, raw)) { errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); } } @@ -646,7 +646,7 @@ static void scanComplete(void *raw, dbCommon *prec) /* must be called with pca->lock held */ static void scanLinkOnce(dbCommon *prec, caLink *pca) { - if(pca->scanningOnce==0 && scanOnce3(prec, scanComplete, pca)) { + if(pca->scanningOnce==0 && scanOnceCallback(prec, scanComplete, pca)) { errlogPrintf("dbCa.c failed to queue scanOnce\n"); } if(pca->scanningOnce<5) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 9dfeec0bc..e0f2b3603 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -612,7 +612,7 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg) } int scanOnce(struct dbCommon *precord) { - return scanOnce3(precord, NULL, NULL); + return scanOnceCallback(precord, NULL, NULL); } typedef struct { @@ -621,7 +621,7 @@ typedef struct { void *usr; } onceEntry; -int scanOnce3(struct dbCommon *precord, once_complete cb, void *usr) +int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr) { static int newOverflow = TRUE; onceEntry ent; diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index fffae23d1..422864a0a 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -56,7 +56,7 @@ epicsShareFunc void scanAdd(struct dbCommon *); epicsShareFunc void scanDelete(struct dbCommon *); epicsShareFunc double scanPeriod(int scan); epicsShareFunc int scanOnce(struct dbCommon *); -epicsShareFunc int scanOnce3(struct dbCommon *, once_complete cb, void *usr); +epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); /*print periodic lists*/ diff --git a/src/ioc/db/test/dbScanTest.c b/src/ioc/db/test/dbScanTest.c index f9f1f8e44..0815827e3 100644 --- a/src/ioc/db/test/dbScanTest.c +++ b/src/ioc/db/test/dbScanTest.c @@ -36,7 +36,7 @@ static void onceComp(void *junk, dbCommon *prec) static void testOnce(void) { - testDiag("check scanOnce3() callback"); + testDiag("check scanOnceCallback() callback"); waiter = epicsEventMustCreate(epicsEventError); testdbPrepare(); @@ -52,7 +52,7 @@ static void testOnce(void) prec = testdbRecordPtr("reca"); testDiag("scanOnce %s", prec->name); - scanOnce3(prec, onceComp, &waiter); + scanOnceCallback(prec, onceComp, &waiter); testDiag("Waiting"); epicsEventMustWait(waiter); testOk1(called==1); From a526d0eb04823d90fdeb7ec7de661e3cd0a9232c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 17 Feb 2015 16:57:20 -0500 Subject: [PATCH 036/204] dbPutLinkTest: attempt MSVC workaround Apparently MSVC doesn't play well with variadic macros. --- src/ioc/db/test/dbPutLinkTest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 6e7f56ce6..d333d8c14 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -42,7 +42,8 @@ void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); #define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}} #define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}} -#define TEST_HW(SET, TYPE, ID, PARM, ...) {SET, {TYPE, PARM, 0, ID, {__VA_ARGS__}}} +#define TEST_HW_(...) __VA_ARGS__ +#define TEST_HW(SET, TYPE, ID, PARM, ...) {SET, {TYPE, PARM, 0, ID, {TEST_HW_(__VA_ARGS__)}}} static const struct testParseDataT { const char * const str; From be06b37b4238697e9a4157f38f3b7dfe97333d29 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 18 Feb 2015 10:47:42 -0500 Subject: [PATCH 037/204] dbPutLinkTest: don't use variadic macros MSVC doesn't seem to support them correctly --- src/ioc/db/test/dbPutLinkTest.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index d333d8c14..5fcab16b8 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -42,8 +42,6 @@ void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); #define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}} #define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}} -#define TEST_HW_(...) __VA_ARGS__ -#define TEST_HW(SET, TYPE, ID, PARM, ...) {SET, {TYPE, PARM, 0, ID, {TEST_HW_(__VA_ARGS__)}}} static const struct testParseDataT { const char * const str; @@ -58,10 +56,10 @@ static const struct testParseDataT { TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI), TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP), - TEST_HW("#C14 S145 @testing", VME_IO, "CS", "testing", 14, 145), - TEST_HW("#B11 C12 N13 A14 F15 @cparam", CAMAC_IO, "BCNAF", "cparam", 11, 12, 13, 14, 15), - TEST_HW(" #B111 C112 N113 @cparam", CAMAC_IO, "BCN", "cparam", 111, 112, 113), - TEST_HW(" @hello world ", INST_IO, "", "hello world"), + {"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}}, + {"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}}, + {" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}}, + {" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}}, {NULL} }; From 06beb2cafb280ba4c8cf707cab12d510fbab6a1c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 20 Feb 2015 12:14:23 -0500 Subject: [PATCH 038/204] dbCaTest: use dbCaIsLinkConnected() instead of inspecting caLink* --- src/ioc/db/dbCaTest.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ioc/db/dbCaTest.c b/src/ioc/db/dbCaTest.c index 40cf451ba..409fd526b 100644 --- a/src/ioc/db/dbCaTest.c +++ b/src/ioc/db/dbCaTest.c @@ -189,10 +189,8 @@ void dbcaStats(int *pchans, int *pdiscon) dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i]; plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if (plink->type == CA_LINK) { - caLink *pca = (caLink *)plink->value.pv_link.pvt; - ncalinks++; - if (pca && pca->chid && ca_state(pca->chid) == cs_conn) { + if (dbCaIsLinkConnected(plink)) { nconnected++; } } From c120027768c94c29a6e007ead3dc66214d1b57fa Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:15:03 -0400 Subject: [PATCH 039/204] dbCa: fix race in scanComplete Use a reference counter in caLink to ensure it isn't free'd while callbacks remain in the scanOnce queue. --- src/ioc/db/dbCa.c | 35 ++++++++++++++++++++++++++++------- src/ioc/db/dbCaPvt.h | 1 + 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 1ae5a8026..644ae12b9 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -30,6 +30,7 @@ #include "epicsPrint.h" #include "epicsString.h" #include "epicsThread.h" +#include "epicsAtomic.h" #include "epicsTime.h" #include "errlog.h" #include "errMdef.h" @@ -165,11 +166,23 @@ static void addAction(caLink *pca, short link_action) epicsEventSignal(workListEvent); } -static void dbCaLinkFree(caLink *pca) +static void caLinkInc(caLink *pca) { + assert(epicsAtomicGetIntT(&pca->refcount)>0); + epicsAtomicIncrIntT(&pca->refcount); +} + +static void caLinkDec(caLink *pca) +{ + int cnt; dbCaCallback callback; struct link *plinkPutCallback = 0; + cnt = epicsAtomicDecrIntT(&pca->refcount); + assert(cnt>=0); + if(cnt>0) + return; + if (pca->chid) { ca_clear_channel(pca->chid); --dbca_chan_count; @@ -216,7 +229,7 @@ void dbCaShutdown(void) epicsMutexMustLock(workListLock); while((pca=(caLink*)ellGet(&workList))!=NULL) { if(pca->link_action&CA_CLEAR_CHANNEL) { - dbCaLinkFree(pca); + caLinkDec(pca); } } epicsMutexUnlock(workListLock); @@ -275,6 +288,7 @@ void dbCaAddLinkCallback(struct link *plink, assert(!plink->value.pv_link.pvt); pca = (caLink *)dbCalloc(1, sizeof(caLink)); + pca->refcount = 1; pca->lock = epicsMutexMustCreate(); pca->plink = plink; pca->pvname = epicsStrDup(plink->value.pv_link.pvname); @@ -639,21 +653,28 @@ static void scanComplete(void *raw, dbCommon *prec) { caLink *pca = raw; epicsMutexMustLock(pca->lock); - if(pca->scanningOnce==0) + if(!pca->plink) { + /* IOC shutdown or link re-targeted. Do nothing. */ + } else if(pca->scanningOnce==0) { errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); - else if(--pca->scanningOnce){ + } else if(--pca->scanningOnce){ /* another scan is queued */ if(scanOnceCallback(prec, scanComplete, raw)) { errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); } } epicsMutexUnlock(pca->lock); + caLinkDec(pca); } /* must be called with pca->lock held */ static void scanLinkOnce(dbCommon *prec, caLink *pca) { - if(pca->scanningOnce==0 && scanOnceCallback(prec, scanComplete, pca)) { - errlogPrintf("dbCa.c failed to queue scanOnce\n"); + if(pca->scanningOnce==0) { + caLinkInc(pca); + if(scanOnceCallback(prec, scanComplete, pca)) { + caLinkDec(pca); + errlogPrintf("dbCa.c failed to queue scanOnce\n"); + } } if(pca->scanningOnce<5) pca->scanningOnce++; @@ -953,7 +974,7 @@ static void dbCaTask(void *arg) if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding; epicsMutexUnlock(workListLock); /* Give back immediately */ if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */ - dbCaLinkFree(pca); + caLinkDec(pca); /* No alarm is raised. Since link is changing so what? */ continue; /* No other link_action makes sense */ } diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index 219da5a51..dc5a20557 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -39,6 +39,7 @@ typedef struct caLink { ELLNODE node; + int refcount; epicsMutexId lock; struct link *plink; char *pvname; From 4bd4b936499cf4f69796dfab10ae41fa5dbcc1a8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:15:03 -0400 Subject: [PATCH 040/204] dbCa: update comments on locking --- src/ioc/db/dbCa.c | 58 +++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 644ae12b9..984b59f13 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -79,49 +79,37 @@ static int dbca_chan_count; /* caLink locking * - * workListLock - * This is only used to put request into and take them out of workList. - * While this is locked no other locks are taken + * Lock ordering: + * dbScanLock -> caLink.lock -> workListLock * - * dbScanLock - * dbCaAddLink and dbCaRemoveLink are only called by dbAccess or iocInit - * They are only called by dbAccess when it has a global lock on lock set. - * It is assumed that ALL other dbCaxxx calls are made only if dbScanLock - * is already active. These routines are intended for use by record/device - * support. + * workListLock: + * Guards access to workList. * - * caLink.lock - * Any code that use a caLink takes this lock and releases it when done + * dbScanLock: + * All dbCa* functions operating on a single link may only be called when + * the record containing the DBLINK is locked. Including: + * dbCaGet*() + * dbCaIsLinkConnected() + * dbCaPutLink() + * dbCaScanFwdLink() + * dbCaAddLinkCallback() + * dbCaRemoveLink() * - * dbCaTask and the channel access callbacks NEVER access anything in the - * records except after locking caLink.lock and checking that caLink.plink - * is not null. They NEVER call dbScanLock. + * Guard the pointer plink.value.pv_link.pvt, but not the struct caLink + * which is pointed to. * - * The above is necessary to prevent deadlocks and attempts to use a caLink - * that has been deleted. + * caLink.lock: + * Guards the caLink structure (but not the struct DBLINK) * - * Just a few words about handling dbCaRemoveLink because this is when - * it is essential that nothing trys to use a caLink that has been freed. + * The dbCaTask only locks caLink, and must not lock the record (a violation of lock order). * - * dbCaRemoveLink is called when links are being modified. This is only - * done with the dbScan mechanism guranteeing that nothing from - * database access trys to access the record containing the caLink. + * During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock) + * is used as a flag to indicate that a link is no longer active. * - * Thus the problem is to make sure that nothing from channel access - * accesses a caLink that is deleted. This is done as follows. + * References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback() + * which is in progress. * - * dbCaRemoveLink does the following: - * epicsMutexMustLock(pca->lock); - * pca->plink = 0; - * plink->value.pv_link.pvt = 0; - * epicsMutexUnlock(pca->lock); - * addAction(pca,CA_CLEAR_CHANNEL); - * - * dbCaTask issues a ca_clear_channel and then frees the caLink. - * - * If any channel access callback gets called before the ca_clear_channel - * it finds pca->plink=0 and does nothing. Once ca_clear_channel - * is called no other callback for this caLink will be called. + * The libca and scanOnceCallback callbacks take no action if pca->plink==NULL. * * dbCaPutLinkCallback causes an additional complication because * when dbCaRemoveLink is called the callback may not have occured. From b9cbf7a3ac9b926d04de1b71f542690f6e070847 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:06 -0400 Subject: [PATCH 041/204] Add xRecord device support --- src/ioc/db/test/Makefile | 3 + src/ioc/db/test/dbLinkdset.c | 1 - src/ioc/db/test/dbLinkdset.dbd | 2 - src/ioc/db/test/devx.c | 163 +++++++++++++++++++++++++++++++++ src/ioc/db/test/devx.dbd | 2 + src/ioc/db/test/devx.h | 52 +++++++++++ src/ioc/db/test/xRecord.c | 27 +++++- 7 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 src/ioc/db/test/devx.c create mode 100644 src/ioc/db/test/devx.dbd create mode 100644 src/ioc/db/test/devx.h diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index c51f1a119..81c4ca04a 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -14,6 +14,7 @@ TESTLIBRARY = dbTestIoc dbTestIoc_SRCS += xRecord.c dbTestIoc_SRCS += dbLinkdset.c +dbTestIoc_SRCS += devx.c dbTestIoc_LIBS = dbCore ca Com TARGETS += $(COMMON_DIR)/dbTestIoc.dbd @@ -21,6 +22,7 @@ dbTestIoc_DBD += menuGlobal.dbd dbTestIoc_DBD += menuConvert.dbd dbTestIoc_DBD += menuScan.dbd dbTestIoc_DBD += xRecord.dbd +dbTestIoc_DBD += devx.dbd dbTestIoc_DBD += dbLinkdset.dbd TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db @@ -141,4 +143,5 @@ include $(TOP)/configure/RULES xRecord$(DEP): $(COMMON_DIR)/xRecord.h dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h +devx$(DEP): $(COMMON_DIR)/xRecord.h diff --git a/src/ioc/db/test/dbLinkdset.c b/src/ioc/db/test/dbLinkdset.c index 5e7ea285f..6e775df7b 100644 --- a/src/ioc/db/test/dbLinkdset.c +++ b/src/ioc/db/test/dbLinkdset.c @@ -29,7 +29,6 @@ long link_test_noop(void *junk) static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \ epicsExportAddress(dset, devxLTest ## LTYPE); -DEFDSET(Soft) DEFDSET(VME_IO) DEFDSET(CAMAC_IO) DEFDSET(AB_IO) diff --git a/src/ioc/db/test/dbLinkdset.dbd b/src/ioc/db/test/dbLinkdset.dbd index b1e070c66..84d5eefe9 100644 --- a/src/ioc/db/test/dbLinkdset.dbd +++ b/src/ioc/db/test/dbLinkdset.dbd @@ -1,5 +1,3 @@ -device(x, CONSTANT,devxLTestSoft,"Soft Channel") - device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO") device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO") device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO") diff --git a/src/ioc/db/test/devx.c b/src/ioc/db/test/devx.c new file mode 100644 index 000000000..104c64001 --- /dev/null +++ b/src/ioc/db/test/devx.c @@ -0,0 +1,163 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven +* National Lab +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "devx.h" + +/* xRecord DTYP="Scan I/O" + * + * dset to test I/O Intr scanning. + * INP="@drvname" names a "driver" which + * provides a IOSCANPVT. + * The driver also defines a callback function which + * is invoked when the record is processed. + */ + +struct ELLLIST xdrivers; + +/* Add a new "driver" with the given group id + * and processing callback + */ +xdrv* xdrv_add(int group, xdrvcb cb, void *arg) +{ + xdrv *drv=callocMustSucceed(1, sizeof(*drv), "xdrv_add"); + drv->cb = cb; + drv->arg = arg; + drv->group = group; + scanIoInit(&drv->scan); + ellAdd(&xdrivers, &drv->drvnode); + return drv; +} + +/* Trigger the named "driver" group to scan */ +xdrv *xdrv_get(int group) +{ + ELLNODE *cur; + for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) { + xdrv *curd = CONTAINER(cur, xdrv, drvnode); + if(curd->group==group) { + return curd; + } + } + cantProceed("xdrv_get() for non-existant group"); + return NULL; +} + +/* Free all "driver" groups and record private structures. + * Call after testdbCleanup() + */ +void xdrv_reset() +{ + ELLNODE *cur; + while((cur=ellGet(&xdrivers))!=NULL) { + ELLNODE *cur2; + xdrv *curd = CONTAINER(cur, xdrv, drvnode); + while((cur2=ellGet(&curd->privlist))!=NULL) { + xpriv *priv = CONTAINER(cur2, xpriv, privnode); + free(priv); + } + free(curd); + } +} + +static long xscanio_init_record(xRecord *prec) +{ + ELLNODE *cur; + xpriv *priv; + xdrv *drv = NULL; + int group, member; + assert(prec->inp.type==INST_IO); + + if(sscanf(prec->inp.value.instio.string, "%d %d", + &group, &member)!=2) + cantProceed("xscanio_init_record invalid INP string"); + + for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) { + xdrv *curd = CONTAINER(cur, xdrv, drvnode); + if(curd->group==group) { + drv = curd; + break; + } + } + + assert(drv!=NULL); + priv = mallocMustSucceed(sizeof(*priv), "xscanio_init_record"); + priv->prec = prec; + priv->drv = drv; + priv->member = member; + ellAdd(&drv->privlist, &priv->privnode); + prec->dpvt = priv; + + return 0; +} + +static long xscanio_get_ioint_info(int cmd, xRecord *prec, IOSCANPVT *ppvt) +{ + xpriv *priv = prec->dpvt; + if(!priv || !priv->drv) + return 0; + *ppvt = priv->drv->scan; + return 0; +} + +static long xscanio_read(xRecord *prec) +{ + xpriv *priv = prec->dpvt; + if(!priv || !priv->drv) + return 0; + if(priv->drv->cb) + (*priv->drv->cb)(priv, priv->drv->arg); + return 0; +} + +static xdset devxScanIO = { + 5, NULL, NULL, + &xscanio_init_record, + &xscanio_get_ioint_info, + &xscanio_read +}; +epicsExportAddress(dset, devxScanIO); + +/* basic DTYP="Soft Channel" */ +static long xsoft_init_record(xRecord *prec) +{ + if(prec->inp.type==CONSTANT) + recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val); + return 0; +} + +static long xsoft_read(xRecord *prec) +{ + if(prec->inp.type==CONSTANT) + return 0; + dbGetLink(&prec->inp, DBR_DOUBLE, &prec->val, NULL, NULL); + return 0; +} + +static struct xdset devxSoft = { + 5, NULL, NULL, + &xsoft_init_record, + NULL, + &xsoft_read +}; +epicsExportAddress(dset, devxSoft); diff --git a/src/ioc/db/test/devx.dbd b/src/ioc/db/test/devx.dbd new file mode 100644 index 000000000..ee6421bc0 --- /dev/null +++ b/src/ioc/db/test/devx.dbd @@ -0,0 +1,2 @@ +device(x, CONSTANT, devxSoft, "Soft Channel") +device(x, INST_IO , devxScanIO, "Scan I/O") diff --git a/src/ioc/db/test/devx.h b/src/ioc/db/test/devx.h new file mode 100644 index 000000000..f8f417a14 --- /dev/null +++ b/src/ioc/db/test/devx.h @@ -0,0 +1,52 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven +* National Lab +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef DEVXSCANIO_H +#define DEVXSCANIO_H + +#include +#include + +#include + +struct xRecord; +struct xpriv; + +epicsShareExtern struct ELLLIST xdrivers; + +typedef void (*xdrvcb)(struct xpriv *, void *); + +typedef struct { + ELLNODE drvnode; + IOSCANPVT scan; + xdrvcb cb; + void *arg; + ELLLIST privlist; + int group; +} xdrv; + +typedef struct xpriv { + ELLNODE privnode; + xdrv *drv; + struct xRecord *prec; + int member; +} xpriv; + +epicsShareFunc xdrv *xdrv_add(int group, xdrvcb cb, void *arg); +epicsShareFunc xdrv *xdrv_get(int group); +epicsShareFunc void xdrv_reset(); + +typedef struct xdset { + long number; + long (*report)(int); + long (*init)(int); + long (*init_record)(struct xRecord *); + long (*get_ioint_info)(int, struct xRecord*, IOSCANPVT*); + long (*process)(struct xRecord *); +} xdset; + +#endif /* DEVXSCANIO_H */ diff --git a/src/ioc/db/test/xRecord.c b/src/ioc/db/test/xRecord.c index dcc1f776c..b83a8368c 100644 --- a/src/ioc/db/test/xRecord.c +++ b/src/ioc/db/test/xRecord.c @@ -16,22 +16,45 @@ #include "dbAccessDefs.h" #include "recSup.h" #include "recGbl.h" +#include "devSup.h" +#include "dbScan.h" #define GEN_SIZE_OFFSET #include "xRecord.h" #include +#include "devx.h" + +static long init_record(xRecord *prec, int pass) +{ + long ret = 0; + xdset *xset = (xdset*)prec->dset; + if(!pass) return 0; + + if(!xset) { + recGblRecordError(S_dev_noDSET, prec, "x: init_record"); + return S_dev_noDSET; + } + if(xset->init_record) + ret = (*xset->init_record)(prec); + return ret; +} + static long process(xRecord *prec) { + long ret = 0; + xdset *xset = (xdset*)prec->dset; prec->pact = TRUE; + if(xset && xset->process) + ret = (*xset->process)(prec); recGblGetTimeStamp(prec); recGblFwdLink(prec); prec->pact = FALSE; - return 0; + return ret; } static rset xRSET = { - RSETNUMBER, NULL, NULL, NULL, process + RSETNUMBER, NULL, NULL, init_record, process }; epicsExportAddress(rset,xRSET); From 8ef1e0d17778d164ed9ed0265ccda0f1b4b72df8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 042/204] re-write scanIoTest --- src/ioc/db/test/Makefile | 10 +- src/ioc/db/test/scanIoTest.c | 677 ++++++++++++---------------------- src/ioc/db/test/scanIoTest.db | 4 +- src/ioc/db/test/yRecord.dbd | 10 - 4 files changed, 243 insertions(+), 458 deletions(-) delete mode 100644 src/ioc/db/test/yRecord.dbd diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 81c4ca04a..0f4765d2d 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -83,18 +83,13 @@ testHarness_SRCS += dbCaStatsTest.c TESTS += dbCaStatsTest TESTFILES += ../dbCaStatsTest.db -TARGETS += $(COMMON_DIR)/scanIoTest.dbd -scanIoTest_DBD += menuGlobal.dbd -scanIoTest_DBD += menuConvert.dbd scanIoTest_DBD += menuScan.dbd -scanIoTest_DBD += yRecord.dbd TESTPROD_HOST += scanIoTest scanIoTest_SRCS += scanIoTest.c scanIoTest_REGRDDFLAGS = -l -scanIoTest_SRCS += scanIoTest_registerRecordDeviceDriver.cpp +scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += scanIoTest.c -testHarness_SRCS += scanIoTest_registerRecordDeviceDriver.cpp -TESTFILES += $(COMMON_DIR)/scanIoTest.dbd ../scanIoTest.db +TESTFILES += ../scanIoTest.db TESTS += scanIoTest TESTPROD_HOST += dbChannelTest @@ -142,6 +137,5 @@ include $(TOP)/configure/RULES xRecord$(DEP): $(COMMON_DIR)/xRecord.h dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h -scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h devx$(DEP): $(COMMON_DIR)/xRecord.h diff --git a/src/ioc/db/test/scanIoTest.c b/src/ioc/db/test/scanIoTest.c index 9457414b7..904f24d6e 100644 --- a/src/ioc/db/test/scanIoTest.c +++ b/src/ioc/db/test/scanIoTest.c @@ -12,6 +12,8 @@ */ #include +#include +#include #include "epicsEvent.h" #include "epicsMessageQueue.h" @@ -26,9 +28,6 @@ #include "dbLock.h" #include "dbUnitTest.h" #include "dbCommon.h" -#include "registry.h" -#include "registryRecordType.h" -#include "registryDeviceSupport.h" #include "recSup.h" #include "devSup.h" #include "iocInit.h" @@ -38,11 +37,11 @@ #include "testMain.h" #include "osiFileName.h" -#define GEN_SIZE_OFFSET -#include "yRecord.h" - #include "epicsExport.h" +#include "devx.h" +#include "xRecord.h" + #define ONE_THREAD_LOOPS 101 #define PAR_THREAD_LOOPS 53 #define CB_THREAD_LOOPS 13 @@ -53,467 +52,267 @@ #define NO_OF_MID_THREADS 3 -static int noOfGroups = NO_OF_GROUPS; -static int noOfIoscans = NO_OF_GROUPS; +STATIC_ASSERT(NUM_CALLBACK_PRIORITIES==3); -static IOSCANPVT *ioscanpvt; /* Soft interrupt sources */ -static ELLLIST *pvtList; /* Per group private part lists */ +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); -static int executionOrder; -static int orderFail; -static int testNo; -static epicsMessageQueueId *mq; /* Per group message queue */ -static epicsEventId *barrier; /* Per group barrier event */ -static int *cbCounter; - -struct pvtY { - ELLNODE node; - yRecord *prec; - int group; - int member; - int count; - int processed; - int callback; -}; - -/* test2: priority and ioscan index for each group - * used priorities are expressed in the bit pattern of (ioscan index + 1) */ -struct groupItem { - int prio; - int ioscan; -} groupTable[12] = { - { 0, 0 }, - { 1, 1 }, - { 0, 2 }, { 1, 2 }, - { 2, 3 }, - { 0, 4 }, { 2, 4 }, - { 1, 5 }, { 2, 5 }, - { 0, 6 }, { 1, 6 }, { 2, 6 } -}; -static int recsProcessed = 1; -static int noDoubleCallback = 1; - -void scanIoTest_registerRecordDeviceDriver(struct dbBase *); - -long count_bits(long n) { - unsigned int c; /* c accumulates the total bits set in v */ - for (c = 0; n; c++) - n &= n - 1; /* clear the least significant bit set */ - return c; -} - -/*************************************************************************\ -* yRecord: minimal record needed to test I/O Intr scanning -\*************************************************************************/ - -static long get_ioint_info(int cmd, yRecord *prec, IOSCANPVT *ppvt) +static void loadRecord(int group, int member, const char *prio) { - struct pvtY *pvt = (struct pvtY *)(prec->dpvt); - - if (testNo == 2) - *ppvt = ioscanpvt[groupTable[pvt->group].ioscan]; - else - *ppvt = ioscanpvt[pvt->group]; - return 0; + char buf[40]; + sprintf(buf, "GROUP=%d,MEMBER=%d,PRIO=%s", + group, member, prio); + testdbReadDatabase("scanIoTest.db", NULL, buf); } -struct ydset { - long number; - DEVSUPFUN report; - DEVSUPFUN init; - DEVSUPFUN init_record; - DEVSUPFUN get_ioint_info; - DEVSUPFUN process; -} devY = { - 5, - NULL, - NULL, - NULL, - get_ioint_info, - NULL -}; -epicsExportAddress(dset, devY); +typedef struct { + int hasprocd[NUM_CALLBACK_PRIORITIES]; + int getcomplete[NUM_CALLBACK_PRIORITIES]; + epicsEventId wait[NUM_CALLBACK_PRIORITIES]; + epicsEventId wake[NUM_CALLBACK_PRIORITIES]; +} testsingle; -static long init_record(yRecord *prec, int pass) +static void testcb(xpriv *priv, void *raw) { - struct pvtY *pvt; + testsingle *td = raw; + int prio = priv->prec->prio; - if (pass == 0) return 0; - - pvt = (struct pvtY *) calloc(1, sizeof(struct pvtY)); - prec->dpvt = pvt; - - pvt->prec = prec; - sscanf(prec->name, "g%dm%d", &pvt->group, &pvt->member); - ellAdd(&pvtList[pvt->group], &pvt->node); - - return 0; + testOk1(td->hasprocd[prio]==0); + td->hasprocd[prio] = 1; } -static long process(yRecord *prec) +static void testcomp(void *raw, IOSCANPVT scan, int prio) { - struct pvtY *pvt = (struct pvtY *)(prec->dpvt); + testsingle *td = raw; - if (testNo == 0) { - /* Single callback thread */ - if (executionOrder != pvt->member) { - orderFail = 1; - } - pvt->count++; - if (++executionOrder == NO_OF_MEMBERS) executionOrder = 0; - } else { - pvt->count++; - if (pvt->member == 0) { - epicsMessageQueueSend(mq[pvt->group], NULL, 0); - epicsEventMustWait(barrier[pvt->group]); - } - } - pvt->processed = 1; - return 0; + testOk1(td->hasprocd[prio]==1); + testOk1(td->getcomplete[prio]==0); + td->getcomplete[prio] = 1; + epicsEventMustTrigger(td->wait[prio]); + epicsEventMustWait(td->wake[prio]); } -rset yRSET={ - 4, - NULL, /* report */ - NULL, /* initialize */ - init_record, - process -}; -epicsExportAddress(rset, yRSET); - -static void startMockIoc(void) { - char substitutions[256]; - int i, j; - char *prio[] = { "LOW", "MEDIUM", "HIGH" }; - - if (testNo == 2) { - noOfGroups = 12; - noOfIoscans = 7; - } - ioscanpvt = calloc(noOfIoscans, sizeof(IOSCANPVT)); - mq = calloc(noOfGroups, sizeof(epicsMessageQueueId)); - barrier = calloc(noOfGroups, sizeof(epicsEventId)); - pvtList = calloc(noOfGroups, sizeof(ELLLIST)); - cbCounter = calloc(noOfGroups, sizeof(int)); - - if (dbReadDatabase(&pdbbase, "scanIoTest.dbd", - "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR - "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Error reading database description 'scanIoTest.dbd'"); - - callbackParallelThreads(1, "Low"); - callbackParallelThreads(NO_OF_MID_THREADS, "Medium"); - callbackParallelThreads(NO_OF_THREADS, "High"); - - for (i = 0; i < noOfIoscans; i++) { - scanIoInit(&ioscanpvt[i]); - } - - for (i = 0; i < noOfGroups; i++) { - mq[i] = epicsMessageQueueCreate(NO_OF_MEMBERS, 1); - barrier[i] = epicsEventMustCreate(epicsEventEmpty); - ellInit(&pvtList[i]); - } - - scanIoTest_registerRecordDeviceDriver(pdbbase); - for (i = 0; i < noOfGroups; i++) { - for (j = 0; j < NO_OF_MEMBERS; j++) { - sprintf(substitutions, "GROUP=%d,MEMBER=%d,PRIO=%s", i, j, - testNo==0?"LOW":(testNo==1?"HIGH":prio[groupTable[i].prio])); - if (dbReadDatabase(&pdbbase, "scanIoTest.db", - "." OSI_PATH_LIST_SEPARATOR "..", substitutions)) - testAbort("Error reading test database 'scanIoTest.db'"); - } - } - - testIocInitOk(); -} - -static void stopMockIoc(void) { +static void testSingleThreading(void) +{ int i; + testsingle data[2]; + xdrv *drvs[2]; + + memset(data, 0, sizeof(data)); + + for(i=0; i<2; i++) { + int p; + for(p=0; pscan, &testcomp, &data[0]); + scanIoSetComplete(drvs[1]->scan, &testcomp, &data[1]); + + eltc(0); + testIocInitOk(); + eltc(1); + + testDiag("Scan first list"); + scanIoRequest(drvs[0]->scan); + testDiag("Scan second list"); + scanIoRequest(drvs[1]->scan); + + testDiag("Wait for first list to complete"); + for(i=0; imember; + + testOk1(td->hasprocd==0); + td->hasprocd = 1; + epicsEventMustTrigger(td->wait); + epicsEventMustWait(td->wake); + td->getcomplete = 1; +} + +static void testcompmulti(void *raw, IOSCANPVT scan, int prio) +{ + int *mask = raw; + testOk(((*mask)&(1<node)) { - if (pvt->callback == 1) { - testDiag("callback for rec %s arrived twice\n", pvt->prec->name); - noDoubleCallback = 0; - } - if (pvt->processed == 0) { - testDiag("rec %s was not processed\n", pvt->prec->name); - recsProcessed = 0; - } - pvt->callback = 1; + testDiag("Test multi-threaded I/O Intr scanning"); + + testdbPrepare(); + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + /* create two scan lists with one record on each of three priorities */ + + /* group#, member#, priority */ + loadRecord(0, 0, "LOW"); + loadRecord(1, 1, "LOW"); + loadRecord(0, 2, "MEDIUM"); + loadRecord(1, 3, "MEDIUM"); + loadRecord(0, 4, "HIGH"); + loadRecord(1, 5, "HIGH"); + + drvs[0] = xdrv_add(0, &testcbmulti, data); + drvs[1] = xdrv_add(1, &testcbmulti, data); + scanIoSetComplete(drvs[0]->scan, &testcompmulti, &masks[0]); + scanIoSetComplete(drvs[1]->scan, &testcompmulti, &masks[1]); + + /* just enough workers to process all records concurrently */ + callbackParallelThreads(2, "LOW"); + callbackParallelThreads(2, "MEDIUM"); + callbackParallelThreads(2, "HIGH"); + + eltc(0); + testIocInitOk(); + eltc(1); + + testDiag("Scan first list"); + scanIoRequest(drvs[0]->scan); + testDiag("Scan second list"); + scanIoRequest(drvs[1]->scan); + + testDiag("Wait for everything to start"); + for(i=0; i= number of parallel threads"); - - /**************************************\ - * Single callback thread - \**************************************/ - - testNo = 0; - startMockIoc(); - - testDiag("Testing single callback thread"); - testDiag(" using %d ioscan sources, %d records for each, and %d loops", - noOfGroups, NO_OF_MEMBERS, ONE_THREAD_LOOPS); - - for (j = 0; j < ONE_THREAD_LOOPS; j++) { - for (i = 0; i < noOfIoscans; i++) { - scanIoRequest(ioscanpvt[i]); - } - } - - epicsThreadSleep(1.0); - - testOk((orderFail==0), "No out-of-order processing"); - - result = 1; - for (i = 0; i < noOfGroups; i++) { - for (pvt = (struct pvtY *)ellFirst(&pvtList[i]); - pvt; - pvt = (struct pvtY *)ellNext(&pvt->node)) { - if (pvt->count != ONE_THREAD_LOOPS) result = 0; - } - } - - testOk(result, "All per-record process counters match number of loops"); - - stopMockIoc(); - - /**************************************\ - * Multiple parallel callback threads - \**************************************/ - - testNo = 1; - startMockIoc(); - - testDiag("Testing multiple parallel callback threads"); - testDiag(" using %d ioscan sources, %d records for each, %d loops, and %d parallel threads", - noOfIoscans, NO_OF_MEMBERS, PAR_THREAD_LOOPS, NO_OF_THREADS); - - for (j = 0; j < PAR_THREAD_LOOPS; j++) { - for (i = 0; i < noOfIoscans; i++) { - scanIoRequest(ioscanpvt[i]); - } - } - - /* With parallel cb threads, order and distribution to threads are not guaranteed. - * We have stop barrier events for each request (in the first record). - * Test schedule: - * - After the requests have been put in the queue, NO_OF_THREADS threads should have taken - * one request each. - * - Each barrier event is given PAR_THREAD_LOOPS times. - * - Whenever things stop, there should be four threads waiting, one request each. - * - After all loops, each record should have processed PAR_THREAD_LOOPS times. - */ - - max_one_all = 1; - parallel_all = 1; - - for (loop = 0; loop < (PAR_THREAD_LOOPS * noOfGroups) / NO_OF_THREADS + 1; loop++) { - max_one = 1; - parallel = 0; - waiting = 0; - j = 0; - do { - epicsThreadSleep(0.001); - j++; - for (i = 0; i < noOfGroups; i++) { - int l = epicsMessageQueuePending(mq[i]); - while (epicsMessageQueueTryReceive(mq[i], NULL, 0) != -1); - if (l == 1) { - waiting |= 1 << i; - } else if (l > 1) { - max_one = 0; - } - } - parallel = count_bits(waiting); - } while (j < 5 && parallel < NO_OF_THREADS); - - if (!max_one) max_one_all = 0; - if (loop < (PAR_THREAD_LOOPS * noOfGroups) / NO_OF_THREADS) { - if (!(parallel == NO_OF_THREADS)) parallel_all = 0; - } else { - /* In the last run of the loop only the remaining requests are processed */ - if (!(parallel == PAR_THREAD_LOOPS * noOfGroups % NO_OF_THREADS)) parallel_all = 0; - } - - for (i = 0; i < noOfGroups; i++) { - if (waiting & (1 << i)) { - epicsEventTrigger(barrier[i]); - } - } - } - - testOk(max_one_all, "No thread took more than one request per loop"); - testOk(parallel_all, "Correct number of requests were being processed in parallel in each loop"); - - epicsThreadSleep(0.1); - - result = 1; - for (i = 0; i < noOfGroups; i++) { - for (pvt = (struct pvtY *)ellFirst(&pvtList[i]); - pvt; - pvt = (struct pvtY *)ellNext(&pvt->node)) { - if (pvt->count != PAR_THREAD_LOOPS) { - testDiag("Process counter for record %s (%d) does not match loop count (%d)", - pvt->prec->name, pvt->count, PAR_THREAD_LOOPS); - result = 0; - } - } - } - - testOk(result, "All per-record process counters match number of loops"); - - stopMockIoc(); - - /**************************************\ - * Scanio callback mechanism - \**************************************/ - - testNo = 2; - startMockIoc(); - - for (i = 0; i < noOfIoscans; i++) { - scanIoSetComplete(ioscanpvt[i], checkProcessed, NULL); - } - - testDiag("Testing scanio callback mechanism"); - testDiag(" using %d ioscan sources, %d records for each, %d loops, and 1 LOW / %d MEDIUM / %d HIGH parallel threads", - noOfIoscans, NO_OF_MEMBERS, CB_THREAD_LOOPS, NO_OF_MID_THREADS, NO_OF_THREADS); - - result = 1; - for (j = 0; j < CB_THREAD_LOOPS; j++) { - for (i = 0; i < noOfIoscans; i++) { - int prio_used; - prio_used = scanIoRequest(ioscanpvt[i]); - if (i+1 != prio_used) - result = 0; - } - } - testOk(result, "All requests return the correct priority callback mask (all 7 permutations covered)"); - - /* Test schedule: - * After the requests have been put in the queue, it is checked - * - that each callback arrives exactly once, - * - after all records in the group have been processed. - */ - - /* loop count times 4 since (worst case) one loop triggers 4 groups for the single LOW thread */ - for (loop = 0; loop < CB_THREAD_LOOPS * 4; loop++) { - max_one = 1; - parallel = 0; - waiting = 0; - j = 0; - do { - epicsThreadSleep(0.001); - j++; - for (i = 0; i < noOfGroups; i++) { - int l = epicsMessageQueuePending(mq[i]); - while (epicsMessageQueueTryReceive(mq[i], NULL, 0) != -1); - if (l == 1) { - waiting |= 1 << i; - } else if (l > 1) { - max_one = 0; - } - } - parallel = count_bits(waiting); - } while (j < 5); -\ - for (i = 0; i < noOfGroups; i++) { - if (waiting & (1 << i)) { - for (pvt = (struct pvtY *)ellFirst(&pvtList[i]); - pvt; - pvt = (struct pvtY *)ellNext(&pvt->node)) { - pvt->processed = 0; - pvt->callback = 0; - /* record processing will set this at the end of process() */ - } - epicsEventTrigger(barrier[i]); - } - } - } - - epicsThreadSleep(0.1); - - testOk(recsProcessed, "Each callback occured after all records in the group were processed"); - testOk(noDoubleCallback, "No double callbacks occured in any loop"); - - result = 1; - cbCountOk = 1; - for (i = 0; i < noOfGroups; i++) { - if (cbCounter[i] != CB_THREAD_LOOPS) { - testDiag("Callback counter for group %d (%d) does not match loop count (%d)", - i, cbCounter[i], CB_THREAD_LOOPS); - cbCountOk = 0; - } - for (pvt = (struct pvtY *)ellFirst(&pvtList[i]); - pvt; - pvt = (struct pvtY *)ellNext(&pvt->node)) { - if (pvt->count != CB_THREAD_LOOPS) { - testDiag("Process counter for record %s (%d) does not match loop count (%d)", - pvt->prec->name, pvt->count, CB_THREAD_LOOPS); - result = 0; - } - } - } - - testOk(result, "All per-record process counters match number of loops"); - testOk(cbCountOk, "All per-group callback counters match number of loops"); - - stopMockIoc(); - + testPlan(0); + testSingleThreading(); + testDiag("run a second time to verify shutdown and restart works"); + testSingleThreading(); + testMultiThreading(); + testDiag("run a second time to verify shutdown and restart works"); + testMultiThreading(); return testDone(); } diff --git a/src/ioc/db/test/scanIoTest.db b/src/ioc/db/test/scanIoTest.db index 810a84edf..9452ffd0a 100644 --- a/src/ioc/db/test/scanIoTest.db +++ b/src/ioc/db/test/scanIoTest.db @@ -1,4 +1,6 @@ -record(y, g$(GROUP)m$(MEMBER)) { +record(x, "g$(GROUP)m$(MEMBER)") { + field(DTYP, "Scan I/O") + field(INP , "@$(GROUP) $(MEMBER)") field(SCAN, "I/O Intr") field(PRIO, "$(PRIO)") } diff --git a/src/ioc/db/test/yRecord.dbd b/src/ioc/db/test/yRecord.dbd deleted file mode 100644 index 0aa970da5..000000000 --- a/src/ioc/db/test/yRecord.dbd +++ /dev/null @@ -1,10 +0,0 @@ -# This is a minimal I/O scanned record - -recordtype(y) { - include "dbCommon.dbd" - field(VAL, DBF_LONG) { - prompt("Value") - } -} - -device(y,CONSTANT,devY,"ScanIO Test") From a0143143844190653f8403fbfde2ca889ad8befe Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 043/204] db/test: add simple processing callback to xRecord --- src/ioc/db/test/xRecord.c | 2 ++ src/ioc/db/test/xRecord.dbd | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/ioc/db/test/xRecord.c b/src/ioc/db/test/xRecord.c index b83a8368c..356af0869 100644 --- a/src/ioc/db/test/xRecord.c +++ b/src/ioc/db/test/xRecord.c @@ -45,6 +45,8 @@ static long process(xRecord *prec) { long ret = 0; xdset *xset = (xdset*)prec->dset; + if(prec->clbk) + (*prec->clbk)(prec); prec->pact = TRUE; if(xset && xset->process) ret = (*xset->process)(prec); diff --git a/src/ioc/db/test/xRecord.dbd b/src/ioc/db/test/xRecord.dbd index fb230f563..915746a25 100644 --- a/src/ioc/db/test/xRecord.dbd +++ b/src/ioc/db/test/xRecord.dbd @@ -11,4 +11,9 @@ recordtype(x) { field(INP, DBF_INLINK) { prompt("Input Link") } + field(CLBK, DBF_NOACCESS) { + prompt("Processing callback") + special(SPC_NOMOD) + extra("void (*clbk)(struct xRecord*)") + } } From a7a87372aab2c086f7ac60db4a5d9e39f08b9f05 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 044/204] dbNotify: cleanup freelist --- src/ioc/db/dbNotify.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index ede8dd30c..5e902ab79 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -109,6 +109,15 @@ static void notifyCallback(CALLBACK *pcallback); (listnode)->isOnList=0; \ } +static void notifyFree(void *raw) +{ + notifyPvt *pnotifyPvt = raw; + assert(pnotifyPvt->magic==MAGIC); + epicsEventDestroy(pnotifyPvt->cancelEvent); + epicsEventDestroy(pnotifyPvt->userCallbackEvent); + free(pnotifyPvt); +} + static void notifyInit(processNotify *ppn) { notifyPvt *pnotifyPvt; @@ -301,7 +310,7 @@ static void notifyCallback(CALLBACK *pcallback) void dbProcessNotifyExit(void) { - assert(ellCount(&pnotifyGlobal->freeList)==0); + ellFree2(&pnotifyGlobal->freeList, ¬ifyFree); epicsMutexDestroy(pnotifyGlobal->lock); free(pnotifyGlobal); pnotifyGlobal = NULL; From 3e8ba7d7cfd6b607b154fb18a96ed052a71c1c03 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 045/204] dbCa: enable unittests --- src/ioc/db/dbCAC.h | 1 + src/ioc/db/dbCa.c | 30 ++++++---- src/ioc/db/dbChannelNOOP.h | 118 +++++++++++++++++++++++++++++++++++++ src/ioc/db/dbContext.cpp | 18 +++++- src/ioc/misc/iocInit.c | 1 + 5 files changed, 152 insertions(+), 16 deletions(-) create mode 100644 src/ioc/db/dbChannelNOOP.h diff --git a/src/ioc/db/dbCAC.h b/src/ioc/db/dbCAC.h index ad3d0287b..82b20b619 100644 --- a/src/ioc/db/dbCAC.h +++ b/src/ioc/db/dbCAC.h @@ -201,6 +201,7 @@ private: cacContextNotify & notify; epics_auto_ptr < cacContext > pNetContext; char * pStateNotifyCache; + bool isolated; cacChannel & createChannel ( epicsGuard < epicsMutex > &, diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 984b59f13..029037f0e 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -52,8 +52,11 @@ #include "link.h" #include "recSup.h" +/* defined in dbContext.cpp + * Setup local CA access + */ extern void dbServiceIOInit(); - +extern int dbServiceIsolate; static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */ static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/ @@ -224,25 +227,16 @@ void dbCaShutdown(void) } } -static void dbCaExit(void *arg) +static void dbCaLinkInitImpl(int isolate) { - dbCaShutdown(); -} + dbServiceIsolate = isolate; + dbServiceIOInit(); -void dbCaLinkInitIsolated(void) -{ if (!workListLock) workListLock = epicsMutexMustCreate(); if (!workListEvent) workListEvent = epicsEventMustCreate(epicsEventEmpty); - dbCaCtl = ctlExit; - epicsAtExit(dbCaExit, NULL); -} -void dbCaLinkInit(void) -{ - dbServiceIOInit(); - dbCaLinkInitIsolated(); startStopEvent = epicsEventMustCreate(epicsEventEmpty); dbCaCtl = ctlPause; @@ -252,6 +246,16 @@ void dbCaLinkInit(void) epicsEventMustWait(startStopEvent); } +void dbCaLinkInitIsolated(void) +{ + dbCaLinkInitImpl(1); +} + +void dbCaLinkInit(void) +{ + dbCaLinkInitImpl(0); +} + void dbCaRun(void) { if (dbCaCtl == ctlPause) { diff --git a/src/ioc/db/dbChannelNOOP.h b/src/ioc/db/dbChannelNOOP.h new file mode 100644 index 000000000..d48540d88 --- /dev/null +++ b/src/ioc/db/dbChannelNOOP.h @@ -0,0 +1,118 @@ +#ifndef DBCHANNELNOOP_H +#define DBCHANNELNOOP_H + +#include +#include + +#include "cacIO.h" +#include "caerr.h" + +/** @brief A channel which never connects + * + * Used when dbCa is placed in isolated mode for unittests + */ +class dbChannelNOOP : public cacChannel +{ + std::string myname; +public: + dbChannelNOOP(const char *name, cacChannelNotify ¬ify) + :cacChannel(notify) + ,myname(name) + {} + + virtual void destroy ( + CallbackGuard & /*callbackGuard*/, + epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ ) + { + delete this; // goodbye cruel world + } + + virtual unsigned getName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw () + { + const char* name = myname.c_str(); + if(bufLen>myname.size()+1) { + bufLen=myname.size()+1; + } + memcpy(pBuf, name, bufLen); + pBuf[--bufLen] = '\0'; + return bufLen; + } + + // !! deprecated, avoid use !! + virtual const char * pName ( + epicsGuard < epicsMutex > & guard ) const throw () + {return myname.c_str();} + + virtual void show ( + epicsGuard < epicsMutex > &, + unsigned level ) const + {} + + virtual void initiateConnect ( + epicsGuard < epicsMutex > & ) + {} + + virtual unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ ) + {return 0;} + + virtual void flush ( + epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ ) + {} + + virtual ioStatus read ( + epicsGuard < epicsMutex > &mut, + unsigned type, arrayElementCount count, + cacReadNotify ¬ify, ioid * = 0 ) + { + notify.exception(mut, ECA_NORDACCESS, "dbChannelNOOP", type, count); + return iosSynch; + } + + virtual void write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + const void *pValue ) + {} + + virtual ioStatus write ( + epicsGuard < epicsMutex > &mut, + unsigned type, arrayElementCount count, + const void */*pValue*/, cacWriteNotify & notify, ioid * = 0 ) + { + notify.exception(mut, ECA_NOWTACCESS, "dbChannelNOOP", type, count); + return iosSynch; + } + + virtual void subscribe ( + epicsGuard < epicsMutex > &mut, unsigned type, + arrayElementCount count, unsigned /*mask*/, cacStateNotify & notify, + ioid * = 0 ) + { + // should never subscribe + notify.exception(mut, ECA_BADMASK, "dbChannelNOOP", type, count); + } + + virtual void ioCancel ( + CallbackGuard & callbackGuard, + epicsGuard < epicsMutex > & mutualExclusionGuard, + const ioid & ) + {} + + virtual void ioShow ( + epicsGuard < epicsMutex > &, + const ioid &, unsigned level ) const + {} + + virtual short nativeType ( + epicsGuard < epicsMutex > & ) const + {return 0;} // DBR_STRING + + virtual arrayElementCount nativeElementCount ( + epicsGuard < epicsMutex > & ) const + {return 1;} +}; + +#endif // DBCHANNELNOOP_H diff --git a/src/ioc/db/dbContext.cpp b/src/ioc/db/dbContext.cpp index d005f084a..124a836cf 100644 --- a/src/ioc/db/dbContext.cpp +++ b/src/ioc/db/dbContext.cpp @@ -39,6 +39,7 @@ #include "dbCAC.h" #include "dbChannel.h" #include "dbChannelIO.h" +#include "dbChannelNOOP.h" #include "dbPutNotifyBlocker.h" class dbService : public cacService { @@ -61,9 +62,16 @@ cacContext & dbService::contextCreate ( mutualExclusion, notify ); } +extern "C" int dbServiceIsolate; +int dbServiceIsolate = 0; + extern "C" void dbServiceIOInit () { - caInstallDefaultService ( dbs ); + static int init=0; + if(!init) { + caInstallDefaultService ( dbs ); + init=1; + } } dbBaseIO::dbBaseIO () {} @@ -72,7 +80,8 @@ dbContext::dbContext ( epicsMutex & cbMutexIn, epicsMutex & mutexIn, cacContextNotify & notifyIn ) : readNotifyCache ( mutexIn ), ctx ( 0 ), stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ), - notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ) + notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ), + isolated(dbServiceIsolate) { } @@ -92,7 +101,10 @@ cacChannel & dbContext::createChannel ( dbChannel *dbch = dbChannel_create ( pName ); if ( ! dbch ) { - if ( ! this->pNetContext.get() ) { + if ( isolated ) { + return *new dbChannelNOOP(pName, notifyIn); + + } else if ( ! this->pNetContext.get() ) { this->pNetContext.reset ( & this->notify.createNetworkContext ( this->mutex, this->cbMutex ) ); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index a66de064a..508f0312d 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -693,6 +693,7 @@ int iocShutdown(void) dbProcessNotifyExit(); iocshFree(); } + dbCaShutdown(); iocState = iocStopped; iocBuildMode = buildRSRV; return 0; From 77efc973daaaaf41440b5e600250e018fdeb32f3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 046/204] iocInit: free PPNR --- src/ioc/misc/iocInit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 508f0312d..14dc756ca 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -677,6 +677,9 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, dbFreeLinkContents(plink); } + + // may be allocated in dbNotify.c + free(precord->ppnr); } int iocShutdown(void) From 40d4d608edb790c36afb0e9140b91c2443a2e6fd Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 047/204] std/filters/test: add simple processing callback to arrRecord --- src/std/filters/test/arrRecord.c | 6 ++++++ src/std/filters/test/arrRecord.dbd | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/std/filters/test/arrRecord.c b/src/std/filters/test/arrRecord.c index 5e9b2f02c..7dea7caaf 100644 --- a/src/std/filters/test/arrRecord.c +++ b/src/std/filters/test/arrRecord.c @@ -97,6 +97,12 @@ static long init_record(arrRecord *prec, int pass) static long process(arrRecord *prec) { + if(prec->clbk) + (*prec->clbk)(prec); + prec->pact = TRUE; + recGblGetTimeStamp(prec); + recGblFwdLink(prec); + prec->pact = FALSE; return 0; } diff --git a/src/std/filters/test/arrRecord.dbd b/src/std/filters/test/arrRecord.dbd index 2b115b500..307ef7ead 100644 --- a/src/std/filters/test/arrRecord.dbd +++ b/src/std/filters/test/arrRecord.dbd @@ -31,4 +31,9 @@ recordtype(arr) { special(SPC_NOMOD) extra("void *bptr") } + field(CLBK, DBF_NOACCESS) { + prompt("Processing callback") + special(SPC_NOMOD) + extra("void (*clbk)(struct arrRecord*)") + } } From 40cb21df9f0ac24425991a33664b26cdefa181db Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 048/204] std/filters/test: add input link to arrRecord --- src/std/filters/test/arrRecord.dbd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/std/filters/test/arrRecord.dbd b/src/std/filters/test/arrRecord.dbd index 307ef7ead..b504be1cb 100644 --- a/src/std/filters/test/arrRecord.dbd +++ b/src/std/filters/test/arrRecord.dbd @@ -31,6 +31,9 @@ recordtype(arr) { special(SPC_NOMOD) extra("void *bptr") } + field(INP, DBF_INLINK) { + prompt("Input Link") + } field(CLBK, DBF_NOACCESS) { prompt("Processing callback") special(SPC_NOMOD) From e73ed24631b8bf6e96f5b4cdb931841767a906a5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 049/204] db/test: copy arrRecord from std/filters/test --- src/ioc/db/test/Makefile | 3 + src/ioc/db/test/arrRecord.c | 141 ++++++++++++++++++++++++++++++++++ src/ioc/db/test/arrRecord.dbd | 42 ++++++++++ 3 files changed, 186 insertions(+) create mode 100644 src/ioc/db/test/arrRecord.c create mode 100644 src/ioc/db/test/arrRecord.dbd diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 0f4765d2d..4299a6ba5 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -13,6 +13,7 @@ include $(TOP)/configure/CONFIG TESTLIBRARY = dbTestIoc dbTestIoc_SRCS += xRecord.c +dbTestIoc_SRCS += arrRecord.c dbTestIoc_SRCS += dbLinkdset.c dbTestIoc_SRCS += devx.c dbTestIoc_LIBS = dbCore ca Com @@ -22,6 +23,7 @@ dbTestIoc_DBD += menuGlobal.dbd dbTestIoc_DBD += menuConvert.dbd dbTestIoc_DBD += menuScan.dbd dbTestIoc_DBD += xRecord.dbd +dbTestIoc_DBD += arrRecord.dbd dbTestIoc_DBD += devx.dbd dbTestIoc_DBD += dbLinkdset.dbd TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db @@ -136,6 +138,7 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES xRecord$(DEP): $(COMMON_DIR)/xRecord.h +arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h devx$(DEP): $(COMMON_DIR)/xRecord.h diff --git a/src/ioc/db/test/arrRecord.c b/src/ioc/db/test/arrRecord.c new file mode 100644 index 000000000..7dea7caaf --- /dev/null +++ b/src/ioc/db/test/arrRecord.c @@ -0,0 +1,141 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* arrRecord.c - minimal array record for test purposes: no processing */ + +/* + * Author: Ralph Lange + * + * vaguely implemented like parts of recWaveform.c by Bob Dalesio + * + */ + +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#define GEN_SIZE_OFFSET +#include "arrRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(arrRecord *, int); +static long process(arrRecord *); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset arrRSET = { + RSETNUMBER, + report, + initialize, + init_record, + process, + special, + get_value, + cvt_dbaddr, + get_array_info, + put_array_info, + get_units, + get_precision, + get_enum_str, + get_enum_strs, + put_enum_str, + get_graphic_double, + get_control_double, + get_alarm_double +}; +epicsExportAddress(rset, arrRSET); + +static long init_record(arrRecord *prec, int pass) +{ + if (pass == 0) { + if (prec->nelm <= 0) + prec->nelm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), + "arr calloc failed"); + + if (prec->nelm == 1) { + prec->nord = 1; + } else { + prec->nord = 0; + } + return 0; + } + return 0; +} + +static long process(arrRecord *prec) +{ + if(prec->clbk) + (*prec->clbk)(prec); + prec->pact = TRUE; + recGblGetTimeStamp(prec); + recGblFwdLink(prec); + prec->pact = FALSE; + return 0; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->nelm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + *no_elements = prec->nord; + *offset = prec->off; + + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + prec->nord = nNew; + if (prec->nord > prec->nelm) + prec->nord = prec->nelm; + + return 0; +} diff --git a/src/ioc/db/test/arrRecord.dbd b/src/ioc/db/test/arrRecord.dbd new file mode 100644 index 000000000..b504be1cb --- /dev/null +++ b/src/ioc/db/test/arrRecord.dbd @@ -0,0 +1,42 @@ +include "menuGlobal.dbd" +include "menuConvert.dbd" +include "menuScan.dbd" +recordtype(arr) { + include "dbCommon.dbd" + field(VAL, DBF_NOACCESS) { + prompt("Value") + special(SPC_DBADDR) + pp(TRUE) + extra("void *val") + } + field(NELM, DBF_ULONG) { + prompt("Number of Elements") + special(SPC_NOMOD) + initial("1") + } + field(FTVL, DBF_MENU) { + prompt("Field Type of Value") + special(SPC_NOMOD) + menu(menuFtype) + } + field(NORD, DBF_ULONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(OFF, DBF_ULONG) { + prompt("Offset into array") + } + field(BPTR, DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + extra("void *bptr") + } + field(INP, DBF_INLINK) { + prompt("Input Link") + } + field(CLBK, DBF_NOACCESS) { + prompt("Processing callback") + special(SPC_NOMOD) + extra("void (*clbk)(struct arrRecord*)") + } +} From 52fc47dec54192cbe6f25aa19c339bddbd794093 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:24:07 -0400 Subject: [PATCH 050/204] start dbCaLinkTest --- src/ioc/db/test/Makefile | 10 + src/ioc/db/test/dbCaLinkTest.c | 553 ++++++++++++++++++++++++++++++ src/ioc/db/test/dbCaLinkTest1.db | 5 + src/ioc/db/test/dbCaLinkTest2.db | 10 + src/ioc/db/test/dbCaLinkTest3.db | 14 + src/ioc/db/test/epicsRunDbTests.c | 2 + 6 files changed, 594 insertions(+) create mode 100644 src/ioc/db/test/dbCaLinkTest.c create mode 100644 src/ioc/db/test/dbCaLinkTest1.db create mode 100644 src/ioc/db/test/dbCaLinkTest2.db create mode 100644 src/ioc/db/test/dbCaLinkTest3.db diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 4299a6ba5..2a7c15272 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -10,6 +10,9 @@ TOP=../../../.. include $(TOP)/configure/CONFIG +# Find private headers +USR_CFLAGS += -I../.. + TESTLIBRARY = dbTestIoc dbTestIoc_SRCS += xRecord.c @@ -85,6 +88,13 @@ testHarness_SRCS += dbCaStatsTest.c TESTS += dbCaStatsTest TESTFILES += ../dbCaStatsTest.db +TESTPROD_HOST += dbCaLinkTest +dbCaLinkTest_SRCS += dbCaLinkTest.c +dbCaLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbCaLinkTest.c +TESTS += dbCaLinkTest +TESTFILES += ../dbCaLinkTest1.db + scanIoTest_DBD += menuScan.dbd TESTPROD_HOST += scanIoTest scanIoTest_SRCS += scanIoTest.c diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c new file mode 100644 index 000000000..ff01b3ad4 --- /dev/null +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -0,0 +1,553 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + */ + +#include +#include +#include + +#include "epicsString.h" +#include "dbUnitTest.h" +#include "epicsThread.h" +#include "epicsEvent.h" +#include "iocInit.h" +#include "dbBase.h" +#include "link.h" +#include "dbAccess.h" +#include "epicsStdio.h" +#include "dbEvent.h" +/* hackish duplication since we can't include db_access.h here */ +typedef void* chid; +#define MAX_UNITS_SIZE 8 +#include "dbCaPvt.h" +#include "errlog.h" + +#include "xRecord.h" +#include "arrRecord.h" + +#include "testMain.h" + +#define testOp(FMT,A,OP,B) testOk((A)OP(B), #A " ("FMT") " #OP " " #B " ("FMT")", A,B) + +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static epicsEventId waitEvent; +static unsigned waitCounter; + +static +void waitCB(void *unused) +{ + if(waitEvent) + epicsEventMustTrigger(waitEvent); + waitCounter++; //TODO: atomic +} + +static +void startWait(DBLINK *plink) +{ + caLink *pca = plink->value.pv_link.pvt; + + assert(!waitEvent); + waitEvent = epicsEventMustCreate(epicsEventEmpty); + + assert(pca); + epicsMutexMustLock(pca->lock); + assert(!pca->monitor && !pca->userPvt); + pca->monitor = &waitCB; + epicsMutexUnlock(pca->lock); + testDiag("Preparing to wait on pca=%p", pca); +} + +static +void waitForUpdate(DBLINK *plink) +{ + caLink *pca = plink->value.pv_link.pvt; + + assert(pca); + + testDiag("Waiting on pca=%p", pca); + epicsEventMustWait(waitEvent); + + epicsMutexMustLock(pca->lock); + pca->monitor = NULL; + pca->userPvt = NULL; + epicsMutexUnlock(pca->lock); + + epicsEventDestroy(waitEvent); + waitEvent = NULL; +} + +static +void putLink(DBLINK *plink, short dbr, const void*buf, long nReq) +{ + long ret; + waitEvent = epicsEventMustCreate(epicsEventEmpty); + + ret = dbCaPutLinkCallback(plink, dbr, buf, nReq, + &waitCB, NULL); + if(ret) { + testFail("putLink fails %ld\n", ret); + } else { + epicsEventMustWait(waitEvent); + testPass("putLink ok\n"); + } + epicsEventDestroy(waitEvent); + waitEvent = NULL; +} + +static void testNativeLink(void) +{ + xRecord *psrc, *ptarg; + DBLINK *psrclnk; + epicsInt32 temp; + long nReq; + testDiag("Link to a scalar numeric field"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CA"); + + eltc(0); + testIocInitOk(); + eltc(1); + + psrc = (xRecord*)testdbRecordPtr("source"); + ptarg= (xRecord*)testdbRecordPtr("target"); + psrclnk = &psrc->lnk; + + // ensure this is really a CA link + testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg)); + + testOk1(psrclnk->type==CA_LINK); + + startWait(psrclnk); + + dbScanLock((dbCommon*)ptarg); + ptarg->val = 42; + db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); + dbScanUnlock((dbCommon*)ptarg); + + waitForUpdate(psrclnk); + + dbScanLock((dbCommon*)psrc); + // local CA_LINK connects immediately + testOk1(dbCaIsLinkConnected(psrclnk)); + nReq = 422; + testOk1(dbCaGetNelements(psrclnk, &nReq)==0); + testOp("%ld",nReq,==,1l); + + nReq = 1; + temp = 0x0f0f0f0f; + testOk1(dbGetLink(psrclnk, DBR_LONG, (void*)&temp, NULL, &nReq)==0); + testOp("%d",temp,==,42); + dbScanUnlock((dbCommon*)psrc); + + temp = 1010; + nReq = 1; + putLink(psrclnk, DBR_LONG, (void*)&temp, nReq); + + dbScanLock((dbCommon*)ptarg); + testOk1(ptarg->val==1010); + dbScanUnlock((dbCommon*)ptarg); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void testStringLink(void) +{ + xRecord *psrc, *ptarg; + DBLINK *psrclnk; + char temp[MAX_STRING_SIZE]; + long nReq; + testDiag("Link to a string field"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target.DESC CA"); + + eltc(0); + testIocInitOk(); + eltc(1); + + psrc = (xRecord*)testdbRecordPtr("source"); + ptarg= (xRecord*)testdbRecordPtr("target"); + psrclnk = &psrc->lnk; + + // ensure this is really a CA link + testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg)); + + testOk1(psrclnk->type==CA_LINK); + + startWait(psrclnk); + + dbScanLock((dbCommon*)ptarg); + strcpy(ptarg->desc, "hello"); + db_post_events(ptarg, &ptarg->desc, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); + dbScanUnlock((dbCommon*)ptarg); + + waitForUpdate(psrclnk); + + dbScanLock((dbCommon*)psrc); + // local CA_LINK connects immediately + testOk1(dbCaIsLinkConnected(psrclnk)); + + nReq = 1; + memset(temp, '!', sizeof(temp)); + testOk1(dbGetLink(psrclnk, DBR_STRING, (void*)&temp, NULL, &nReq)==0); + testOk(strcmp(temp, "hello")==0, "%s == hello", temp); + dbScanUnlock((dbCommon*)psrc); + + strcpy(temp, "world"); + putLink(psrclnk, DBR_STRING, (void*)&temp, nReq); + + dbScanLock((dbCommon*)ptarg); + testOk(strcmp(ptarg->desc, "world")==0, "%s == world", ptarg->desc); + dbScanUnlock((dbCommon*)ptarg); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void wasproc(xRecord *prec) +{ + waitCB(NULL); +} + +static void testCP(void) +{ + xRecord *psrc, *ptarg; + testDiag("Link CP modifier"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CP"); + + psrc = (xRecord*)testdbRecordPtr("source"); + ptarg= (xRecord*)testdbRecordPtr("target"); + + /* hook in before IOC init */ + waitCounter=0; + psrc->clbk = &wasproc; + + assert(!waitEvent); + waitEvent = epicsEventMustCreate(epicsEventEmpty); + + eltc(0); + testIocInitOk(); + eltc(1); + + epicsEventMustWait(waitEvent); + + dbScanLock((dbCommon*)psrc); + testOp("%u",waitCounter,==,1); // initial processing + dbScanUnlock((dbCommon*)psrc); + + dbScanLock((dbCommon*)ptarg); + ptarg->val = 42; + db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); + dbScanUnlock((dbCommon*)ptarg); + + epicsEventMustWait(waitEvent); + + dbScanLock((dbCommon*)psrc); + testOp("%u",waitCounter,==,2); // process due to monitor update + dbScanUnlock((dbCommon*)psrc); + + testIocShutdownOk(); + + testdbCleanup(); + + epicsEventDestroy(waitEvent); + waitEvent = NULL; +} + +static void fillArray(epicsInt32 *buf, unsigned count, epicsInt32 first) +{ + for(;count;count--,first++) + *buf++ = first; +} + +static void fillArrayDouble(double *buf, unsigned count, double first) +{ + for(;count;count--,first++) + *buf++ = first; +} + +static void checkArray(const char *msg, + epicsInt32 *buf, epicsInt32 first, + unsigned used, unsigned total) +{ + int match = 1; + unsigned i; + epicsInt32 x, *b; + for(b=buf,x=first,i=0;ivalue.pv_link.pvt; + if(lnk->type!=CA_LINK || !pca->pputNative) + return; + epicsMutexMustLock(pca->lock); + memset(pca->pputNative, '!', + pca->nelements*dbr_value_size[ca_field_type(pca->chid)]); + epicsMutexUnlock(pca->lock); +} + +static void testArrayLink(unsigned nsrc, unsigned ntarg) +{ + char buf[100]; + arrRecord *psrc, *ptarg; + DBLINK *psrclnk; + epicsInt32 *bufsrc, *buftarg; + long nReq; + unsigned num; + + testDiag("Link to a array numeric field"); + + epicsSnprintf(buf, sizeof(buf), "TARGET=target CA,FTVL=LONG,SNELM=%u,TNELM=%u", + nsrc, ntarg); + testDiag("%s", buf); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbCaLinkTest2.db", NULL, buf); + + psrc = (arrRecord*)testdbRecordPtr("source"); + ptarg= (arrRecord*)testdbRecordPtr("target"); + psrclnk = &psrc->inp; + + eltc(0); + testIocInitOk(); + eltc(1); + + bufsrc = psrc->bptr; + buftarg= ptarg->bptr; + + num=psrc->nelm; + if(num>ptarg->nelm) + num=ptarg->nelm; + + startWait(psrclnk); + + dbScanLock((dbCommon*)ptarg); + fillArray(buftarg, ptarg->nelm, 1); + ptarg->nord = ptarg->nelm; + db_post_events(ptarg, ptarg->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); + dbScanUnlock((dbCommon*)ptarg); + + waitForUpdate(psrclnk); + + dbScanLock((dbCommon*)psrc); + nReq = psrc->nelm; + if(dbGetLink(psrclnk, DBR_LONG, bufsrc, NULL, &nReq)==0) { + testPass("dbGetLink"); + testOp("%ld",nReq,==,(long)num); + checkArray("array update", bufsrc, 1, nReq, psrc->nelm); + } else { + testFail("dbGetLink"); + testSkip(2, "dbGetLink fails"); + } + dbScanUnlock((dbCommon*)psrc); + + fillArray(bufsrc, psrc->nelm, 2); + /* write buffer allocated on first put */ + putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); + + dbScanLock((dbCommon*)ptarg); + /* CA links always write the full target array length */ + testOp("%ld",(long)ptarg->nord,==,(long)ptarg->nelm); + /* However, if the source length is less, then the target + * is zero filled + */ + checkArray("array update", buftarg, 2, num, ptarg->nelm); + dbScanUnlock((dbCommon*)ptarg); + + /* write again to ensure that buffer is completely updated */ + spoilputbuf(psrclnk); + fillArray(bufsrc, psrc->nelm, 3); + putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); + + dbScanLock((dbCommon*)ptarg); + testOp("%ld",(long)ptarg->nord,==,(long)ptarg->nelm); + checkArray("array update", buftarg, 3, num, ptarg->nelm); + dbScanUnlock((dbCommon*)ptarg); + + testIocShutdownOk(); + + testdbCleanup(); + + /* records don't cleanup after themselves + * so do here to silence valgrind + */ + free(bufsrc); + free(buftarg); +} + + +static void softarr(arrRecord *prec) +{ + long nReq = prec->nelm; + long status = dbGetLink(&prec->inp, DBR_DOUBLE, prec->bptr, NULL, &nReq); + if(status) { + testFail("dbGetLink() -> %ld", status); + } else { + testPass("dbGetLink() succeeds"); + prec->nord = nReq; + if(nReq>0) + testDiag("%s.VAL[0] - %f", prec->name, *(double*)prec->bptr); + } + waitCB(NULL); +} + +static void testreTargetTypeChange(void) +{ + arrRecord *psrc, *ptarg1, *ptarg2; + double *bufsrc, *buftarg1; + epicsInt32 *buftarg2; + testDiag("Retarget an link to a PV with a different type DOUBLE->LONG"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP"); + + psrc = (arrRecord*)testdbRecordPtr("source"); + ptarg1= (arrRecord*)testdbRecordPtr("target1"); + ptarg2= (arrRecord*)testdbRecordPtr("target2"); + + /* hook in before IOC init */ + waitCounter=0; + psrc->clbk = &softarr; + + assert(!waitEvent); + waitEvent = epicsEventMustCreate(epicsEventEmpty); + + eltc(0); + testIocInitOk(); + eltc(1); + + epicsEventMustWait(waitEvent); // wait for initial processing + + bufsrc = psrc->bptr; + buftarg1= ptarg1->bptr; + buftarg2= ptarg2->bptr; + + testDiag("Update one with original target"); + + dbScanLock((dbCommon*)ptarg2); + fillArray(buftarg2, ptarg2->nelm, 2); + ptarg2->nord = ptarg2->nelm; + dbScanUnlock((dbCommon*)ptarg2); + + /* initialize buffers */ + dbScanLock((dbCommon*)ptarg1); + fillArrayDouble(buftarg1, ptarg1->nelm, 1); + ptarg1->nord = ptarg1->nelm; + db_post_events(ptarg1, ptarg1->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); + dbScanUnlock((dbCommon*)ptarg1); + + epicsEventMustWait(waitEvent); // wait for update + + dbScanLock((dbCommon*)psrc); + testOp("%ld",(long)psrc->nord,==,(long)5); + checkArrayDouble("array update", bufsrc, 1, 5, psrc->nelm); + dbScanUnlock((dbCommon*)psrc); + + testDiag("Retarget"); + testdbPutFieldOk("source.INP", DBR_STRING, "target2 CP"); + + epicsEventMustWait(waitEvent); // wait for update + + dbScanLock((dbCommon*)psrc); + testOp("%ld",(long)psrc->nord,==,(long)5); + checkArrayDouble("array update", bufsrc, 2, 5, psrc->nelm); + dbScanUnlock((dbCommon*)psrc); + + testIocShutdownOk(); + + testdbCleanup(); + + /* records don't cleanup after themselves + * so do here to silence valgrind + */ + free(bufsrc); + free(buftarg1); + free(buftarg2); +} + +MAIN(dbCaLinkTest) +{ + testPlan(62); + testNativeLink(); + testStringLink(); + testCP(); + testArrayLink(1,1); + testArrayLink(10,1); + testArrayLink(1,10); + testArrayLink(10,10); + testreTargetTypeChange(); + return testDone(); +} diff --git a/src/ioc/db/test/dbCaLinkTest1.db b/src/ioc/db/test/dbCaLinkTest1.db new file mode 100644 index 000000000..aab5ceb86 --- /dev/null +++ b/src/ioc/db/test/dbCaLinkTest1.db @@ -0,0 +1,5 @@ +record(x, "target") {} + +record(x, "source") { + field(LNK, "$(TARGET)") +} diff --git a/src/ioc/db/test/dbCaLinkTest2.db b/src/ioc/db/test/dbCaLinkTest2.db new file mode 100644 index 000000000..9cfa4ba00 --- /dev/null +++ b/src/ioc/db/test/dbCaLinkTest2.db @@ -0,0 +1,10 @@ +record(arr, "target") { + field(FTVL, "$(TFTVL=$(FTVL=))") + field(NELM, "$(TNELM=$(NELM=))") +} + +record(arr, "source") { + field(INP, "$(TARGET)") + field(FTVL, "$(SFTVL=$(FTVL=))") + field(NELM, "$(SNELM=$(NELM=))") +} diff --git a/src/ioc/db/test/dbCaLinkTest3.db b/src/ioc/db/test/dbCaLinkTest3.db new file mode 100644 index 000000000..f820bd554 --- /dev/null +++ b/src/ioc/db/test/dbCaLinkTest3.db @@ -0,0 +1,14 @@ +record(arr, "target1") { + field(FTVL, "DOUBLE") + field(NELM, "$(TNELM=$(NELM=))") +} +record(arr, "target2") { + field(FTVL, "LONG") + field(NELM, "$(TNELM=$(NELM=))") +} + +record(arr, "source") { + field(INP, "$(TARGET)") + field(FTVL, "DOUBLE") + field(NELM, "$(SNELM=$(NELM=))") +} diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index 0ee631686..10f10e267 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -26,6 +26,7 @@ int dbScanTest(void); int scanIoTest(void); int dbLockTest(void); int dbPutLinkTest(void); +int dbCaLinkTest(void); int testDbChannel(void); int chfPluginTest(void); int arrShorthandTest(void); @@ -45,6 +46,7 @@ void epicsRunDbTests(void) runTest(scanIoTest); runTest(dbLockTest); runTest(dbPutLinkTest); + runTest(dbCaLinkTest); runTest(testDbChannel); runTest(arrShorthandTest); runTest(recGblCheckDeadbandTest); From 0d41dd0113c4ebce64ca4b4efac2eb6167f39e5c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 16 Mar 2015 18:47:58 -0400 Subject: [PATCH 051/204] std/rec/test: add asTest --- src/std/rec/test/Makefile | 10 ++ src/std/rec/test/asTest.c | 217 ++++++++++++++++++++++++++++++++++++ src/std/rec/test/asTest.db | 11 ++ src/std/rec/test/asTest.dbd | 2 + 4 files changed, 240 insertions(+) create mode 100644 src/std/rec/test/asTest.c create mode 100644 src/std/rec/test/asTest.db create mode 100644 src/std/rec/test/asTest.dbd diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index db2c099ff..9905d7504 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -25,6 +25,16 @@ testHarness_SRCS += arrayOpTest.c TESTFILES += ../arrayOpTest.db TESTS += arrayOpTest +TARGETS += $(COMMON_DIR)/asTestIoc.dbd +asTestIoc_DBD += base.dbd +asTestIoc_DBD += asTest.dbd +TESTPROD_HOST += asTest +asTest_SRCS += asTest.c +asTest_SRCS += asTestIoc_registerRecordDeviceDriver.cpp +#testHarness_SRCS += asTest.c +TESTFILES += ../asTest.db +TESTS += asTest + TARGETS += $(COMMON_DIR)/analogMonitorTest.dbd analogMonitorTest_DBD += base.dbd TESTPROD_HOST += analogMonitorTest diff --git a/src/std/rec/test/asTest.c b/src/std/rec/test/asTest.c new file mode 100644 index 000000000..b229b9d45 --- /dev/null +++ b/src/std/rec/test/asTest.c @@ -0,0 +1,217 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + * + * Test the hooks that autosave uses during initialization + */ + +#include "string.h" + +#include "epicsString.h" +#include "dbUnitTest.h" +#include "epicsThread.h" +#include "iocInit.h" +#include "dbBase.h" +#include "link.h" +#include "recSup.h" +#include "dbAccess.h" +#include "dbConvert.h" +#include "dbStaticLib.h" +#include "registry.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "osiFileName.h" +#include "initHooks.h" +#include "devSup.h" +#include "errlog.h" + +#include "aoRecord.h" +#include "waveformRecord.h" + +#include "testMain.h" + +#include "epicsExport.h" + +void asTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static unsigned iran; + +static void hookPass0(initHookState state) +{ + DBENTRY entry; + if(state!=initHookAfterInitDevSup) + return; + testDiag("initHookAfterInitDevSup"); + + dbInitEntry(pdbbase, &entry); + + /* rec0.VAL is initially 1, set it to 2 */ + if(dbFindRecord(&entry, "rec0.VAL")==0) { + aoRecord *prec = entry.precnode->precord; + testOk(prec->val==1, "VAL %d==1 (initial value from .db)", (int)prec->val); + testOk1(dbPutString(&entry, "2")==0); + testOk(prec->val==2, "VAL %d==2", (int)prec->val); + } else { + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + + /* rec0.OUT is initially "rec0.DISV", set it to "rec0.SEVR" */ + if(dbFindRecord(&entry, "rec0.OUT")==0) { + aoRecord *prec = entry.precnode->precord; + if(prec->out.type==PV_LINK) + testOk(strcmp(prec->out.value.pv_link.pvname,"rec0.DISV")==0, + "%s==rec0.DISV (initial value from .db)", + prec->out.value.pv_link.pvname); + else + testFail("Wrong link type"); + + testOk1(dbPutString(&entry, "rec0.SEVR")==0); + } else{ + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + + /* can't restore array field in pass0 */ + + dbFinishEntry(&entry); +} + +static long initRec0(aoRecord *prec) +{ + DBLINK *plink = &prec->out; + testDiag("init_record(%s)", prec->name); + testOk(prec->val==2, "VAL %d==2 (pass0 value)", (int)prec->val); + prec->val = 3; + testOk(prec->val==3, "VAL %d==3", (int)prec->val); + + testOk1(plink->type==DB_LINK); + if(plink->type==DB_LINK) + testOk(strcmp(plink->value.pv_link.pvname,"rec0.SEVR")==0, + "%s==rec0.SEVR (pass0 value)", plink->value.pv_link.pvname); + else + testFail("Wrong link type"); + + iran |= 1; + return 2; /* we set .VAL, so don't use RVAL */ +} + +static long initRec1(waveformRecord *prec) +{ + testDiag("init_record(%s)", prec->name); + testOk(prec->nord==0, "NORD %d==0", (int)prec->nord); + iran |= 2; + return 0; +} + +static double values[] = {1,2,3,4,5}; + +static void hookPass1(initHookState state) +{ + DBENTRY entry; + DBADDR addr; + if(state!=initHookAfterInitDatabase) + return; + testDiag("initHookAfterInitDatabase"); + + dbInitEntry(pdbbase, &entry); + + if(dbFindRecord(&entry, "rec0.VAL")==0) { + aoRecord *prec = entry.precnode->precord; + testOk(prec->val==3, "VAL %d==3 (init_record value)", (int)prec->val); + testOk1(dbPutString(&entry, "4")==0); + testOk(prec->val==4, "VAL %d==4", (int)prec->val); + } else{ + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + + /* Can't restore links in pass 1 */ + + if(dbNameToAddr("rec1.VAL", &addr)) { + testFail("missing rec1"); + testSkip(3, "missing record"); + } else { + struct rset *prset = dbGetRset(&addr); + dbfType ftype = addr.field_type; + long count=-1, offset=-1, maxcount = addr.no_elements; + testOk1(prset && prset->get_array_info && prset->put_array_info); + testOk1((*prset->get_array_info)(&addr, &count, &offset)==0); + /* count is ignored */ + testOk1((*dbPutConvertRoutine[DBF_DOUBLE][ftype])(&addr, values, NELEMENTS(values), maxcount,offset)==0); + testOk1((*prset->put_array_info)(&addr, NELEMENTS(values))==0); + } + + dbFinishEntry(&entry); +} + +static void testRestore(void) +{ + aoRecord *rec0; + waveformRecord *rec1; + testDiag("test Restore"); + + initHookRegister(hookPass0); + initHookRegister(hookPass1); + + testdbPrepare(); + + testdbReadDatabase("asTestIoc.dbd", NULL, NULL); + + asTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("asTest.db", NULL, NULL); + + rec0 = (aoRecord*)testdbRecordPtr("rec0"); + rec1 = (waveformRecord*)testdbRecordPtr("rec1"); + + //eltc(0); + testIocInitOk(); + //eltc(1); + + testDiag("Post initialization"); + + testOk1(iran==3); + + testOk1(rec0->val==4); + testOk1(rec1->nord==5); + { + double *buf = rec1->bptr; + testOk(buf[0]==1, "buf[0] %f==1", buf[0]); + testOk1(buf[1]==2); + testOk1(buf[2]==3); + testOk1(buf[3]==4); + testOk1(buf[4]==5); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + +MAIN(asTest) +{ + testPlan(25); + testRestore(); + return testDone(); +} + +struct dset6 { + dset common; + DEVSUPFUN proc; + DEVSUPFUN linconv; +}; + +static long noop() {return 0;} + +static struct dset6 devAOasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec0, NULL}, (DEVSUPFUN)noop, NULL}; +static struct dset6 devWFasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec1, NULL}, (DEVSUPFUN)noop, NULL}; + +epicsExportAddress(dset, devAOasTest); +epicsExportAddress(dset, devWFasTest); diff --git a/src/std/rec/test/asTest.db b/src/std/rec/test/asTest.db new file mode 100644 index 000000000..a50d1998b --- /dev/null +++ b/src/std/rec/test/asTest.db @@ -0,0 +1,11 @@ +record(ao, "rec0") { + field(DTYP, "asTest") + field(VAL, "1") + field(OUT, "rec0.DISV") +} + +record(waveform, "rec1") { + field(DTYP, "asTest") + field(FTVL, "DOUBLE") + field(NELM, "5") +} diff --git a/src/std/rec/test/asTest.dbd b/src/std/rec/test/asTest.dbd new file mode 100644 index 000000000..c53b52a8b --- /dev/null +++ b/src/std/rec/test/asTest.dbd @@ -0,0 +1,2 @@ +device(ao, CONSTANT, devAOasTest, "asTest") +device(waveform, CONSTANT, devWFasTest, "asTest") From e7037cc519fdbde6b28e3f67976feaf032414e74 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 17 Mar 2015 11:34:36 -0400 Subject: [PATCH 052/204] dbStatic: fix CONSTANT links constantStr==NULL and constantStr=="" have different meanings for recGblInitConstantLink() which is used with DOL. constantStr should remain NULL unless explicitly set by either initial("") or field(..., "") --- src/ioc/dbStatic/dbStaticLib.c | 3 ++- src/ioc/dbStatic/dbStaticRun.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 45dbda411..b80a295c3 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1915,7 +1915,8 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) plink->type = CONSTANT; switch (plink->type) { - case CONSTANT: plink->value.constantStr = callocMustSucceed(1, 1, "init CONSTANT link"); break; + /* constantStr is allowed to remain NULL if plink->text==NULL */ + case CONSTANT: plink->value.constantStr = NULL; break; case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break; case VME_IO: plink->value.vmeio.parm = pNullString; break; case CAMAC_IO: plink->value.camacio.parm = pNullString; break; diff --git a/src/ioc/dbStatic/dbStaticRun.c b/src/ioc/dbStatic/dbStaticRun.c index b6caa1552..2ab477fd6 100644 --- a/src/ioc/dbStatic/dbStaticRun.c +++ b/src/ioc/dbStatic/dbStaticRun.c @@ -270,9 +270,9 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) plink->type = CONSTANT; if(pflddes->initial) { - plink->value.constantStr = + plink->text = dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); - strcpy(plink->value.constantStr,pflddes->initial); + strcpy(plink->text,pflddes->initial); } } break; From 8bf765365ffd3f1ce9e21e9aee616a65c80bfc7f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 17 Mar 2015 15:26:08 -0400 Subject: [PATCH 053/204] std/rec/test: add asTest to RTEMS test harness --- src/ioc/dbStatic/dbStaticLib.c | 4 +++- src/std/rec/test/Makefile | 3 ++- src/std/rec/test/epicsRunRecordTests.c | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index b80a295c3..5318df48a 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1915,7 +1915,9 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) plink->type = CONSTANT; switch (plink->type) { - /* constantStr is allowed to remain NULL if plink->text==NULL */ + /* constantStr is allowed to remain NULL if plink->text==NULL + * constantStr==NULL has special meaning in recGblInitConstantLink() + */ case CONSTANT: plink->value.constantStr = NULL; break; case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break; case VME_IO: plink->value.vmeio.parm = pNullString; break; diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 9905d7504..6b28b5a27 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -17,6 +17,7 @@ recTestIoc_DBD = base.dbd TESTFILES += $(COMMON_DIR)/recTestIoc.dbd testHarness_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += asTestIoc_registerRecordDeviceDriver.cpp TESTPROD_HOST += arrayOpTest arrayOpTest_SRCS += arrayOpTest.c @@ -31,7 +32,7 @@ asTestIoc_DBD += asTest.dbd TESTPROD_HOST += asTest asTest_SRCS += asTest.c asTest_SRCS += asTestIoc_registerRecordDeviceDriver.cpp -#testHarness_SRCS += asTest.c +testHarness_SRCS += asTest.c TESTFILES += ../asTest.db TESTS += asTest diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c index 092c0a6ce..1a5858d76 100644 --- a/src/std/rec/test/epicsRunRecordTests.c +++ b/src/std/rec/test/epicsRunRecordTests.c @@ -14,6 +14,7 @@ int analogMonitorTest(void); int arrayOpTest(void); +int asTest(void); void epicsRunRecordTests(void) { @@ -23,5 +24,7 @@ void epicsRunRecordTests(void) runTest(arrayOpTest); + runTest(asTest); + epicsExit(0); /* Trigger test harness */ } From 3a647fb38b879c49a3447be37552968e2fe6bbc3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 17 Mar 2015 19:13:33 -0400 Subject: [PATCH 054/204] iocInit: prepareLinks after autosave pass0 Allow autosave to restore link fields again... --- src/ioc/misc/iocInit.c | 7 ++++--- src/std/rec/test/asTest.c | 33 ++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index a66de064a..a61e81dd6 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -140,7 +140,6 @@ static void prepareLinks(dbRecordType *rtyp, dbCommon *prec, void *junk) static int iocBuild_2(void) { - iterateRecords(prepareLinks, NULL); initHookAnnounce(initHookAfterCaLinkInit); initDrvSup(); @@ -150,12 +149,14 @@ static int iocBuild_2(void) initHookAnnounce(initHookAfterInitRecSup); initDevSup(); - initHookAnnounce(initHookAfterInitDevSup); + initHookAnnounce(initHookAfterInitDevSup); /* used by autosave pass 0 */ + + iterateRecords(prepareLinks, NULL); dbLockInitRecords(pdbbase); initDatabase(); dbBkptInit(); - initHookAnnounce(initHookAfterInitDatabase); + initHookAnnounce(initHookAfterInitDatabase); /* used by autosave pass 1 */ finishDevSup(); initHookAnnounce(initHookAfterFinishDevSup); diff --git a/src/std/rec/test/asTest.c b/src/std/rec/test/asTest.c index b229b9d45..e472cbc1f 100644 --- a/src/std/rec/test/asTest.c +++ b/src/std/rec/test/asTest.c @@ -65,12 +65,12 @@ static void hookPass0(initHookState state) /* rec0.OUT is initially "rec0.DISV", set it to "rec0.SEVR" */ if(dbFindRecord(&entry, "rec0.OUT")==0) { aoRecord *prec = entry.precnode->precord; - if(prec->out.type==PV_LINK) - testOk(strcmp(prec->out.value.pv_link.pvname,"rec0.DISV")==0, + if(prec->out.type==CONSTANT) + testOk(strcmp(prec->out.text,"rec0.DISV")==0, "%s==rec0.DISV (initial value from .db)", - prec->out.value.pv_link.pvname); + prec->out.text); else - testFail("Wrong link type"); + testFail("Wrong link type: %d", (int)prec->out.type); testOk1(dbPutString(&entry, "rec0.SEVR")==0); } else{ @@ -78,6 +78,20 @@ static void hookPass0(initHookState state) testSkip(1, "missing record"); } + /* rec0.SDIS is initially NULL, set it to "rec0.STAT" */ + if(dbFindRecord(&entry, "rec0.SDIS")==0) { + aoRecord *prec = entry.precnode->precord; + if(prec->sdis.type==CONSTANT) + testOk1(prec->sdis.value.constantStr==NULL); + else + testFail("Wrong link type: %d", (int)prec->sdis.type); + + testOk1(dbPutString(&entry, "rec0.STAT")==0); + } else{ + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + /* can't restore array field in pass0 */ dbFinishEntry(&entry); @@ -98,6 +112,15 @@ static long initRec0(aoRecord *prec) else testFail("Wrong link type"); + plink = &prec->sdis; + + testOk1(plink->type==DB_LINK); + if(plink->type==DB_LINK) + testOk(strcmp(plink->value.pv_link.pvname,"rec0.STAT")==0, + "%s==rec0.STAT (pass0 value)", plink->value.pv_link.pvname); + else + testFail("Wrong link type"); + iran |= 1; return 2; /* we set .VAL, so don't use RVAL */ } @@ -197,7 +220,7 @@ static void testRestore(void) MAIN(asTest) { - testPlan(25); + testPlan(29); testRestore(); return testDone(); } From c9d889ef3e579985eb499f0f213cec1711899f0e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 19 Mar 2015 11:26:48 -0400 Subject: [PATCH 055/204] asTest: quiet --- src/std/rec/test/asTest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/std/rec/test/asTest.c b/src/std/rec/test/asTest.c index e472cbc1f..842a754d9 100644 --- a/src/std/rec/test/asTest.c +++ b/src/std/rec/test/asTest.c @@ -194,9 +194,9 @@ static void testRestore(void) rec0 = (aoRecord*)testdbRecordPtr("rec0"); rec1 = (waveformRecord*)testdbRecordPtr("rec1"); - //eltc(0); + eltc(0); testIocInitOk(); - //eltc(1); + eltc(1); testDiag("Post initialization"); From adcde46e9ea7f6e0a31f43cc623d5717bf34b87e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:12:00 -0400 Subject: [PATCH 056/204] populate RDES early --- src/ioc/dbStatic/dbStaticRun.c | 10 ++++++---- src/ioc/misc/iocInit.c | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticRun.c b/src/ioc/dbStatic/dbStaticRun.c index 2ab477fd6..bd53bb13e 100644 --- a/src/ioc/dbStatic/dbStaticRun.c +++ b/src/ioc/dbStatic/dbStaticRun.c @@ -25,6 +25,7 @@ #define epicsExportSharedSymbols #include "dbBase.h" +#include "dbCommon.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" @@ -198,7 +199,7 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) dbRecordNode *precnode = pdbentry->precnode; dbFldDes *pflddes; int i; - char *precord; + dbCommon *precord; char *pfield; if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); @@ -210,7 +211,8 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) return(S_dbLib_noRecSup); } precnode->precord = dbCalloc(1,pdbRecordType->rec_size); - precord = (char *)precnode->precord; + precord = precnode->precord; + precord->rdes = pdbRecordType; pflddes = pdbRecordType->papFldDes[0]; if(!pflddes) { epicsPrintf("dbAllocRecord pflddes for NAME not found\n"); @@ -220,13 +222,13 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName); return(S_dbLib_nameLength); } - pfield = precord + pflddes->offset; + pfield = (char*)precord + pflddes->offset; strcpy(pfield,precordName); for(i=1; ino_fields; i++) { pflddes = pdbRecordType->papFldDes[i]; if(!pflddes) continue; - pfield = precord + pflddes->offset; + pfield = (char*)precord + pflddes->offset; pdbentry->pfield = (void *)pfield; pdbentry->pflddes = pflddes; pdbentry->indfield = i; diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index a61e81dd6..1f418223d 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -467,7 +467,6 @@ static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord, if (!prset) return; /* unlikely */ precord->rset = prset; - precord->rdes = pdbRecordType; precord->mlok = epicsMutexMustCreate(); ellInit(&precord->mlis); From af89b716f470c386e78bce95f8011eb7c9104326 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:14:46 -0400 Subject: [PATCH 057/204] dbLock: multi-locking new API to lock 2 or more lockSets simultaneously removes global locks for dbScanLock() only one global lock for debugging/freelist Introduce dbLockPvt.h for internal API --- src/ioc/db/dbLock.c | 1391 ++++++++++++++++++++++++++-------------- src/ioc/db/dbLock.h | 25 +- src/ioc/db/dbLockPvt.h | 109 ++++ 3 files changed, 1032 insertions(+), 493 deletions(-) create mode 100644 src/ioc/db/dbLockPvt.h diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 1c59dc068..a2fb4da28 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -7,41 +7,7 @@ * and higher are distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbLock.c */ -/* Author: Marty Kraimer Date: 12MAR96 */ -/************** DISCUSSION OF DYNAMIC LINK MODIFICATION ********************** - -A routine attempting to modify a link must do the following: - -Call dbLockSetGblLock before modifying any link and dbLockSetGblUnlock after. -Call dbLockSetRecordLock for any record referenced during change. It MUST NOT UNLOCK -Call dbLockSetSplit before changing any link that is originally a DB_LINK -Call dbLockSetMerge if changed link becomes a DB_LINK. - -Since the purpose of lock sets is to prevent multiple thread from simultaneously -accessing records in set, dynamically changing lock sets presents a problem. - -Three problems arise: - -1) Two threads simultaneoulsy trying to change lock sets -2) Another thread has successfully issued a dbScanLock and currently owns it. -3) While lock set is being changed, a thread issues a dbScanLock. - -solution: - -1) globalLock is locked during the entire time a thread is modifying lock sets - -2) lockSetModifyLock is locked whenever any fields in lockSet are being accessed -or lockRecord.plockSet is being accessed. - -NOTE: - -dblsr may crash if executed while lock sets are being modified. -It is NOT a good idea to make it more robust by issuing dbLockSetGblLock -since this will delay all other threads. -*****************************************************************************/ - #include #include #include @@ -51,466 +17,947 @@ since this will delay all other threads. #include "dbDefs.h" #include "ellLib.h" #include "epicsAssert.h" -#include "epicsExit.h" #include "epicsMutex.h" #include "epicsPrint.h" #include "epicsStdio.h" #include "epicsThread.h" +#include "epicsSpin.h" +#include "epicsAtomic.h" #include "errMdef.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" +#include "dbLink.h" #include "dbCommon.h" #include "dbFldTypes.h" -#include "dbLock.h" +#include "dbLockPvt.h" #include "dbStaticLib.h" #include "link.h" +typedef struct dbScanLockNode dbScanLockNode; -static int dbLockIsInitialized = FALSE; +static epicsThreadOnceId dbLockOnceInit = EPICS_THREAD_ONCE_INIT; -typedef enum { - listTypeScanLock = 0, - listTypeRecordLock = 1, - listTypeFree = 2 -} listType; +static ELLLIST lockSetsActive; /* in use */ +#ifndef LOCKSET_FREE +static ELLLIST lockSetsFree; /* free list */ +#endif -#define nlistType listTypeFree + 1 +/* Guard the global list */ +static epicsMutexId lockSetsGuard; -static ELLLIST lockSetList[nlistType]; -static epicsMutexId globalLock; -static epicsMutexId lockSetModifyLock; -static unsigned long id = 0; -static char *msstring[4]={"NMS","MS","MSI","MSS"}; - -typedef enum { - lockSetStateFree=0, lockSetStateScanLock, lockSetStateRecordLock -} lockSetState; - -typedef struct lockSet { - ELLNODE node; - ELLLIST lockRecordList; - epicsMutexId lock; - unsigned long id; - listType type; - lockSetState state; - epicsThreadId thread_id; - dbCommon *precord; - int nRecursion; - int nWaiting; - int trace; /*For field TPRO*/ -} lockSet; - -/* dbCommon.LSET is a plockRecord */ -typedef struct lockRecord { - ELLNODE node; - lockSet *plockSet; - dbCommon *precord; -} lockRecord; +#ifndef LOCKSET_NOCNT +/* Counter which is incremented whenever + * any lockRecord::plockSet is changed. + * An optimization to avoid a re-sort + * when no links have changed. + */ +static size_t recomputeCnt; +#endif /*private routines */ -static void dbLockInitialize(void) +static void dbLockOnce(void* ignore) { - int i; - - if(dbLockIsInitialized) return; - for(i=0; i< nlistType; i++) ellInit(&lockSetList[i]); - globalLock = epicsMutexMustCreate(); - lockSetModifyLock = epicsMutexMustCreate(); - dbLockIsInitialized = TRUE; + lockSetsGuard = epicsMutexMustCreate(); } -static lockSet * allocLockSet( - lockRecord *plockRecord, listType type, - lockSetState state, epicsThreadId thread_id) -{ - lockSet *plockSet; +/* global ID number assigned to each lockSet on creation. + * Will never exceed the number of records +1 + */ +static size_t next_id = 1; - assert(dbLockIsInitialized); - plockSet = (lockSet *)ellFirst(&lockSetList[listTypeFree]); - if(plockSet) { - ellDelete(&lockSetList[listTypeFree],&plockSet->node); - } else { - plockSet = dbCalloc(1,sizeof(lockSet)); - plockSet->lock = epicsMutexMustCreate(); +static lockSet* makeSet(void) +{ + lockSet *ls; + int iref; + epicsMutexMustLock(lockSetsGuard); +#ifndef LOCKSET_FREE + ls = (lockSet*)ellGet(&lockSetsFree); + if(!ls) { + epicsMutexUnlock(lockSetsGuard); +#endif + + ls=dbCalloc(1,sizeof(*ls)); + ellInit(&ls->lockRecordList); + ls->lock = epicsMutexMustCreate(); + ls->id = epicsAtomicIncrSizeT(&next_id); + +#ifndef LOCKSET_FREE + epicsMutexMustLock(lockSetsGuard); } - ellInit(&plockSet->lockRecordList); - plockRecord->plockSet = plockSet; - id++; - plockSet->id = id; - plockSet->type = type; - plockSet->state = state; - plockSet->thread_id = thread_id; - plockSet->precord = 0; - plockSet->nRecursion = 0; - plockSet->nWaiting = 0; - ellAdd(&plockSet->lockRecordList,&plockRecord->node); - ellAdd(&lockSetList[type],&plockSet->node); - return(plockSet); +#endif + /* the initial reference for the first lockRecord */ + iref = epicsAtomicIncrIntT(&ls->refcount); + ellAdd(&lockSetsActive, &ls->node); + epicsMutexUnlock(lockSetsGuard); + + assert(ls->id>0); + assert(iref>0); + assert(ellCount(&ls->lockRecordList)==0); + + return ls; +} + +unsigned long dbLockGetRefs(struct dbCommon* prec) +{ + return (unsigned long)epicsAtomicGetIntT(&prec->lset->plockSet->refcount); +} + +unsigned long dbLockCountSets(void) +{ + unsigned long count; + epicsMutexMustLock(lockSetsGuard); + count = (unsigned long)ellCount(&lockSetsActive); + epicsMutexUnlock(lockSetsGuard); + return count; +} + +/* caller must lock accessLock.*/ +void dbLockIncRef(lockSet* ls) +{ + int cnt = epicsAtomicIncrIntT(&ls->refcount); + if(cnt<=0) { + errlogPrintf("dbLockIncRef(%p) on dead lockSet\n", ls); + cantProceed(NULL); + } +} + +/* caller must lock accessLock. + * lockSet must *not* be locked + */ +void dbLockDecRef(lockSet *ls) +{ + int cnt = epicsAtomicDecrIntT(&ls->refcount); + assert(cnt>=0); + + if(cnt) + return; + + if(ellCount(&ls->lockRecordList)!=0) { + errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n", ls, ellCount(&ls->lockRecordList)); + cantProceed(NULL); + } + + epicsMutexMustLock(lockSetsGuard); + ellDelete(&lockSetsActive, &ls->node); +#ifndef LOCKSET_FREE + ellAdd(&lockSetsFree, &ls->node); +#else + epicsMutexDestroy(ls->lock); + memset(ls, 0, sizeof(*ls)); + free(ls); +#endif + epicsMutexUnlock(lockSetsGuard); +} + +lockSet* dbLockGetRef(lockRecord *lr) +{ + lockSet *ls; + epicsSpinLock(lr->spin); + ls = lr->plockSet; + dbLockIncRef(ls); + epicsSpinUnlock(lr->spin); + return ls; } unsigned long dbLockGetLockId(dbCommon *precord) { - lockRecord *plockRecord = precord->lset; - lockSet *plockSet; - long id = 0; - - assert(plockRecord); - epicsMutexMustLock(lockSetModifyLock); - plockSet = plockRecord->plockSet; - if(plockSet) id = plockSet->id; - epicsMutexUnlock(lockSetModifyLock); - return(id); -} - -void dbLockSetGblLock(void) -{ - assert(dbLockIsInitialized); - epicsMutexMustLock(globalLock); + unsigned long id=0; + epicsSpinLock(precord->lset->spin); + id = precord->lset->plockSet->id; + epicsSpinUnlock(precord->lset->spin); + return id; } -void dbLockSetGblUnlock(void) -{ - lockSet *plockSet; - lockSet *pnext; - epicsMutexMustLock(lockSetModifyLock); - plockSet = (lockSet *)ellFirst(&lockSetList[listTypeRecordLock]); - while(plockSet) { - pnext = (lockSet *)ellNext(&plockSet->node); - ellDelete(&lockSetList[listTypeRecordLock],&plockSet->node); - plockSet->type = listTypeScanLock; - plockSet->state = lockSetStateFree; - plockSet->thread_id = 0; - plockSet->precord = 0; - plockSet->nRecursion = 0; - plockSet->nWaiting = 0; - ellAdd(&lockSetList[listTypeScanLock],&plockSet->node); - plockSet = pnext; - } - epicsMutexUnlock(lockSetModifyLock); - epicsMutexUnlock(globalLock); - return; -} - -void dbLockSetRecordLock(dbCommon *precord) -{ - lockRecord *plockRecord = precord->lset; - lockSet *plockSet; - - /*Must make sure that no other thread has lock*/ - assert(plockRecord); - epicsMutexMustLock(lockSetModifyLock); - plockSet = plockRecord->plockSet; - assert(plockSet); - if(plockSet->type==listTypeRecordLock) { - epicsMutexUnlock(lockSetModifyLock); - return; - } - assert(plockSet->thread_id!=epicsThreadGetIdSelf()); - plockSet->state = lockSetStateRecordLock; - /*Wait until owner finishes and all waiting get to change state*/ - while(1) { - epicsMutexUnlock(lockSetModifyLock); - epicsMutexMustLock(plockSet->lock); - epicsMutexUnlock(plockSet->lock); - epicsMutexMustLock(lockSetModifyLock); - if(plockSet->nWaiting == 0 && plockSet->nRecursion==0) break; - epicsThreadSleep(.1); - } - assert(plockSet->nWaiting == 0 && plockSet->nRecursion==0); - assert(plockSet->type==listTypeScanLock); - assert(plockSet->state==lockSetStateRecordLock); - ellDelete(&lockSetList[plockSet->type],&plockSet->node); - ellAdd(&lockSetList[listTypeRecordLock],&plockSet->node); - plockSet->type = listTypeRecordLock; - plockSet->thread_id = epicsThreadGetIdSelf(); - plockSet->precord = 0; - epicsMutexUnlock(lockSetModifyLock); -} - void dbScanLock(dbCommon *precord) { - lockRecord *plockRecord = precord->lset; - lockSet *plockSet; - epicsMutexLockStatus status; - epicsThreadId idSelf = epicsThreadGetIdSelf(); + int cnt; + lockRecord * const lr = precord->lset; + lockSet *ls; - /* - * If this assertion is failing it is likely because iocInit - * has not completed. It must complete before normal record - * processing is possible. Consider using an initHook to - * detect when this occurs. - */ - assert(dbLockIsInitialized); - while(1) { - epicsMutexMustLock(lockSetModifyLock); - plockSet = plockRecord->plockSet; - if(!plockSet) goto getGlobalLock; - switch(plockSet->state) { - case lockSetStateFree: - status = epicsMutexTryLock(plockSet->lock); - assert(status==epicsMutexLockOK); - plockSet->nRecursion = 1; - plockSet->thread_id = idSelf; - plockSet->precord = precord; - plockSet->state = lockSetStateScanLock; - epicsMutexUnlock(lockSetModifyLock); - return; - case lockSetStateScanLock: - if(plockSet->thread_id!=idSelf) { - plockSet->nWaiting +=1; - epicsMutexUnlock(lockSetModifyLock); - epicsMutexMustLock(plockSet->lock); - epicsMutexMustLock(lockSetModifyLock); - plockSet->nWaiting -=1; - if(plockSet->state==lockSetStateRecordLock) { - epicsMutexUnlock(plockSet->lock); - goto getGlobalLock; - } - assert(plockSet->state==lockSetStateScanLock); - plockSet->nRecursion = 1; - plockSet->thread_id = idSelf; - plockSet->precord = precord; - } else { - plockSet->nRecursion += 1; - } - epicsMutexUnlock(lockSetModifyLock); - return; - case lockSetStateRecordLock: - /*Only recursive locking is permitted*/ - if((plockSet->nRecursion==0) || (plockSet->thread_id!=idSelf)) - goto getGlobalLock; - plockSet->nRecursion += 1; - epicsMutexUnlock(lockSetModifyLock); - return; - default: - cantProceed("dbScanLock. Bad case choice"); - } -getGlobalLock: - epicsMutexUnlock(lockSetModifyLock); - epicsMutexMustLock(globalLock); - epicsMutexUnlock(globalLock); + ls = dbLockGetRef(lr); + assert(ls->refcount>0); + +retry: + epicsMutexMustLock(ls->lock); + + epicsSpinLock(lr->spin); + if(ls!=lr->plockSet) { + /* oops, collided with recompute. + * take a reference to the new lockSet. + */ + lockSet *ls2 = lr->plockSet; + int newcnt = epicsAtomicIncrIntT(&ls2->refcount); + assert(newcnt>=2); /* lockRecord and us */ + epicsSpinUnlock(lr->spin); + + epicsMutexUnlock(ls->lock); + dbLockDecRef(ls); + + ls = ls2; + goto retry; } + epicsSpinUnlock(lr->spin); + + /* Release reference taken within this + * function. The count will *never* fall to zero + * as the lockRecords can't be changed while + * we hold the lock. + */ + cnt = epicsAtomicDecrIntT(&ls->refcount); + assert(cnt>0); + /* Caller does *not* hold a reference to ls. + * However, the lockRecord does, but can't + * be changed while we hold the lockSet. + */ +#ifdef LOCKSET_DEBUG + if(ls->owner) { + assert(ls->owner==epicsThreadGetIdSelf()); + assert(ls->ownercount>=1); + ls->ownercount++; + } else { + assert(ls->ownercount==0); + ls->owner = epicsThreadGetIdSelf(); + ls->ownercount = 1; + } +#endif + /* no references kept */ } - + void dbScanUnlock(dbCommon *precord) { - lockRecord *plockRecord = precord->lset; - lockSet *plockSet; - - assert(plockRecord); - epicsMutexMustLock(lockSetModifyLock); - plockSet = plockRecord->plockSet; - assert(plockSet); - assert(epicsThreadGetIdSelf()==plockSet->thread_id); - assert(plockSet->nRecursion>=1); - plockSet->nRecursion -= 1; - if(plockSet->nRecursion==0) { - plockSet->thread_id = 0; - plockSet->precord = 0; - if((plockSet->state == lockSetStateScanLock) - && (plockSet->nWaiting==0)) plockSet->state = lockSetStateFree; - epicsMutexUnlock(plockSet->lock); - } - epicsMutexUnlock(lockSetModifyLock); - return; + lockSet *ls = precord->lset->plockSet; + dbLockIncRef(ls); +#ifdef LOCKSET_DEBUG + assert(ls->owner==epicsThreadGetIdSelf()); + assert(ls->ownercount>=1); + ls->ownercount--; + if(ls->ownercount==0) + ls->owner = NULL; +#endif + epicsMutexUnlock(ls->lock); + dbLockDecRef(ls); } -static lockRecord *lockRecordAlloc; +static +int lrrcompare(const void *rawA, const void *rawB) +{ + const lockRecordRef *refA=rawA, *refB=rawB; + const lockSet *A=refA->plockSet, *B=refB->plockSet; + if(!A && !B) + return 0; /* NULL == NULL */ + else if(!A) + return 1; /* NULL > !NULL */ + else if(!B) + return -1; /* !NULL < NULL */ + else if(A < B) + return -1; + else if(A > B) + return 1; + else + return 0; +} + +/* Call w/ update=1 before locking to update cached lockSet entries. + * Call w/ update=0 after locking to verify that lockRecord weren't updated + */ +static +int dbLockUpdateRefs(dbLocker *locker, int update) +{ + int changed = 0; + size_t i, nlock = locker->maxrefs; + +#ifndef LOCKSET_NOCNT + const size_t recomp = epicsAtomicGetSizeT(&recomputeCnt); + if(locker->recomp!=recomp) { +#endif + /* some lockset recompute happened. + * must re-check our references. + */ + + for(i=0; irefs[i]; + lockSet *oldref = NULL; + if(!ref->plr) { + assert(!ref->plockSet); + continue; + } + + epicsSpinLock(ref->plr->spin); + if(ref->plockSet!=ref->plr->plockSet) { + changed = 1; + if(update) { + /* exchange saved lockSet reference */ + oldref = ref->plockSet; + ref->plockSet = ref->plr->plockSet; + dbLockIncRef(ref->plockSet); + } + } + epicsSpinUnlock(ref->plr->spin); + if(oldref) + dbLockDecRef(oldref); + if(!update && changed) + return changed; + } +#ifndef LOCKSET_NOCNT + if(update) + locker->recomp = recomp; + } +#endif + + if(changed && update) { + qsort(locker->refs, nlock, sizeof(lockRecordRef), + &lrrcompare); + } + return changed; +} + +void dbLockerPrepare(struct dbLocker *locker, + struct dbCommon **precs, + size_t nrecs) +{ + size_t i; + locker->maxrefs = nrecs; + /* intentionally spoil the recomp count to ensure that + * references will be updated this first time + */ +#ifndef LOCKSET_NOCNT + locker->recomp = epicsAtomicGetSizeT(&recomputeCnt)-1; +#endif + + for(i=0; irefs[i].plr = precs[i] ? precs[i]->lset : NULL; + } + + /* acquire a reference to all lockRecords */ + dbLockUpdateRefs(locker, 1); +} + +dbLocker *dbLockerAlloc(dbCommon **precs, + size_t nrecs, + unsigned int flags) +{ + size_t Nextra = nrecs>DBLOCKER_NALLOC ? nrecs-DBLOCKER_NALLOC : 0; + dbLocker *locker = calloc(1, sizeof(*locker)+Nextra*sizeof(lockRecordRef)); + + if(!locker) + return NULL; + + dbLockerPrepare(locker, precs, nrecs); + + return locker; +} + +void dbLockerFinalize(dbLocker *locker) +{ + size_t i; + assert(ellCount(&locker->locked)==0); + + for(i=0; imaxrefs; i++) { + if(locker->refs[i].plockSet) + dbLockDecRef(locker->refs[i].plockSet); + } +} + +void dbLockerFree(dbLocker *locker) +{ + dbLockerFinalize(locker); + free(locker); +} + +/* Lock the given list of records. + * This function modifies its arguments. + */ +void dbScanLockMany(dbLocker* locker) +{ + size_t i, nlock = locker->maxrefs; + lockSet *plock; +#ifdef LOCKSET_DEBUG + const epicsThreadId myself = epicsThreadGetIdSelf(); +#endif + +retry: + assert(ellCount(&locker->locked)==0); + dbLockUpdateRefs(locker, 1); + + for(i=0, plock=NULL; irefs[i]; + + /* skip duplicates (same lockSet + * referenced by more than one lockRecord). + */ + if(!ref->plr || (plock && plock==ref->plockSet)) + continue; + plock = ref->plockSet; + + epicsMutexMustLock(plock->lock); + assert(plock->ownerlocker==NULL); + plock->ownerlocker = locker; + ellAdd(&locker->locked, &plock->lockernode); + /* An extra ref for the locked list */ + dbLockIncRef(plock); + +#ifdef LOCKSET_DEBUG + if(plock->owner) { + if(plock->owner!=myself || plock->ownercount<1) { + errlogPrintf("dbScanLockMany(%p) ownership violation %p (%p) %u\n", + locker, plock->owner, myself, plock->ownercount); + cantProceed(NULL); + } + plock->ownercount++; + } else { + assert(plock->ownercount==0); + plock->owner = myself; + plock->ownercount = 1; + } +#endif + + } + + if(dbLockUpdateRefs(locker, 0)) { + /* oops, collided with recompute */ + dbScanUnlockMany(locker); + goto retry; + } + if(nlock!=0 && ellCount(&locker->locked)<=0) { + /* if we have at least one lockRecord, then we will always lock + * at least its present lockSet + */ + errlogPrintf("dbScanLockMany(%p) didn't lock anything\n", locker); + cantProceed(NULL); + } +} + +void dbScanUnlockMany(dbLocker* locker) +{ + ELLNODE *cur; +#ifdef LOCKSET_DEBUG + const epicsThreadId myself = epicsThreadGetIdSelf(); +#endif + + while((cur=ellGet(&locker->locked))!=NULL) { + lockSet *plock = CONTAINER(cur, lockSet, lockernode); + + assert(plock->ownerlocker==locker); + plock->ownerlocker = NULL; +#ifdef LOCKSET_DEBUG + assert(plock->owner==myself); + assert(plock->ownercount>=1); + plock->ownercount--; + if(plock->ownercount==0) + plock->owner = NULL; +#endif + + epicsMutexUnlock(plock->lock); + /* release ref for locked list */ + dbLockDecRef(plock); + } +} + +typedef int (*reciter)(void*, DBENTRY*); +static int forEachRecord(void *priv, dbBase *pdbbase, reciter fn) +{ + long status; + int ret = 0; + DBENTRY dbentry; + dbInitEntry(pdbbase,&dbentry); + status = dbFirstRecordType(&dbentry); + while(!status) + { + status = dbFirstRecord(&dbentry); + while(!status) + { + /* skip alias names */ + if(!dbentry.precnode->recordname[0] || dbentry.precnode->flags & DBRN_FLAGS_ISALIAS) { + /* skip */ + } else { + ret = fn(priv, &dbentry); + if(ret) + goto done; + } + + status = dbNextRecord(&dbentry); + } + + status = dbNextRecordType(&dbentry); + } +done: + dbFinishEntry(&dbentry); + return ret; +} + +static int createLockRecord(void* junk, DBENTRY* pdbentry) +{ + dbCommon *prec = pdbentry->precnode->precord; + size_t i, no_links=prec->rdes->no_links; + lockRecord *lrec; + size_t arrsize=(no_links-1)*sizeof(linkRef); + assert(no_links>=1); /* dbCommon has TSEL */ + assert(!prec->lset); + + lrec = callocMustSucceed(1, sizeof(*lrec)+arrsize, "lockRecord"); + if(!lrec) + cantProceed("no memory for lockRecord"); + lrec->spin = epicsSpinCreate(); + if(!lrec->spin) + cantProceed("no memory for spinlock in lockRecord"); + + lrec->precord = prec; + ellInit(&lrec->backlinks); + + for(i=0; ilinks[i].psrc = lrec; + } + + prec->lset = lrec; + return 0; +} + +static int initPVLinks(void* junk, DBENTRY* pdbentry) +{ + size_t i; + dbRecordType *rtype=pdbentry->precordType; + dbCommon *prec = pdbentry->precnode->precord; + lockSet *A=prec->lset->plockSet; + + /* for each link originating from this record */ + for(i=0; ino_links; i++) { + linkRef *bref = &prec->lset->links[i]; + DBADDR *paddr; + dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]]; + DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); + lockSet *B; + + if(plink->type!=PV_LINK) + continue; + + dbInitLink(prec, plink, pdesc->field_type); + + if(plink->type!=DB_LINK) + continue; + + paddr = (DBADDR*)plink->value.pv_link.pvt; + B = paddr->precord->lset->plockSet; + + + /* Initial population of lockSets happens here. */ + if(!A && !B) { /* neither side has a lockSet */ + A = prec->lset->plockSet = paddr->precord->lset->plockSet = makeSet(); + dbLockIncRef(A); /* ref for psecond */ + ellAdd(&A->lockRecordList, &prec->lset->node); + ellAdd(&A->lockRecordList, &paddr->precord->lset->node); + + } else if(!B) { /* fast merge paddr->precord into A */ + paddr->precord->lset->plockSet = A; + dbLockIncRef(A); + ellAdd(&A->lockRecordList, &paddr->precord->lset->node); + + } else if(!A) { /* fast merge prec into B */ + A = prec->lset->plockSet = B; + dbLockIncRef(B); + ellAdd(&B->lockRecordList, &prec->lset->node); + } + + /* initialize backward link tracking */ + bref->ptarget = paddr->precord->lset; + ellAdd(&bref->ptarget->backlinks, &bref->backlinksnode); + } + return 0; +} + +static int initSingleSets(void* junk, DBENTRY* pdbentry) +{ + dbCommon *prec = pdbentry->precnode->precord; + lockSet *ls; + + if(!prec->lset->plockSet) { + ls = prec->lset->plockSet = makeSet(); + ellAdd(&ls->lockRecordList, &prec->lset->node); + } + + return 0; +} void dbLockInitRecords(dbBase *pdbbase) { - dbRecordType *pdbRecordType; - int nrecords = 0; - lockRecord *plockRecord; + epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL); - dbLockInitialize(); - /*Allocate and initialize a lockRecord for each record instance*/ - for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + /* create all lockRecords */ + forEachRecord(NULL, pdbbase, &createLockRecord); + /* create lockSets for pairs of records with DB_LINKs */ + forEachRecord(NULL, pdbbase, &initPVLinks); + /* create lockSets for all records with no DB_LINKs */ + forEachRecord(NULL, pdbbase, &initSingleSets); +} - nrecords += ellCount(&pdbRecordType->recList) - - pdbRecordType->no_aliases; - } +static int freeLockRecord(void* junk, DBENTRY* pdbentry) +{ + dbCommon *prec = pdbentry->precnode->precord; + lockRecord *lr = prec->lset; + lockSet *ls = lr->plockSet; - /*Allocate all of them at once */ - lockRecordAlloc = plockRecord = dbCalloc(nrecords,sizeof(lockRecord)); - for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - dbRecordNode *pdbRecordNode; + prec->lset = NULL; - for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { - dbCommon *precord = pdbRecordNode->precord; + assert(ls->refcount>0); + assert(ellCount(&ls->lockRecordList)>0); + ellDelete(&ls->lockRecordList, &lr->node); + dbLockDecRef(ls); - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) - continue; - - plockRecord->precord = precord; - precord->lset = plockRecord; - plockRecord++; - } - } - - for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - dbRecordNode *pdbRecordNode; - - for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { - dbCommon *precord = pdbRecordNode->precord; - - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) - continue; - - plockRecord = precord->lset; - if (!plockRecord->plockSet) - allocLockSet(plockRecord, listTypeScanLock, lockSetStateFree, 0); - } - } + epicsSpinDestroy(lr->spin); + free(lr); + return 0; } void dbLockCleanupRecords(dbBase *pdbbase) { +#ifndef LOCKSET_FREE ELLNODE *cur; +#endif + epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL); - free(lockRecordAlloc); - lockRecordAlloc = NULL; + forEachRecord(NULL, pdbbase, &freeLockRecord); + if(ellCount(&lockSetsActive)) { + errlogMessage("Warning: dbLockCleanupRecords() leaking lockSets\n"); + dblsr(NULL,2); + } - /* free lockSets */ - /* ensure no lockSets are locked for re-compute */ - assert(ellCount(&lockSetList[listTypeRecordLock])==0); - /* move allocated locks back to the free list */ - while((cur=ellGet(&lockSetList[listTypeScanLock]))!=NULL) - { - lockSet *pset = CONTAINER(cur, lockSet, node); - assert(pset->state == lockSetStateFree); /* lock not held */ - pset->type = listTypeFree; - ellAdd(&lockSetList[listTypeFree],&pset->node); - } - /* clean up free list */ - while((cur=ellGet(&lockSetList[listTypeFree]))!=NULL) - { - lockSet *pset = CONTAINER(cur, lockSet, node); - epicsMutexDestroy(pset->lock); - free(pset); + assert(ellCount(&lockSetsActive)==0); + +#ifndef LOCKSET_FREE + while((cur=ellGet(&lockSetsFree))!=NULL) { + lockSet *ls = (lockSet*)cur; + + assert(ls->refcount==0); + assert(ellCount(&ls->lockRecordList)==0); + epicsMutexDestroy(ls->lock); + free(ls); } +#endif } -void dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond) +/* update backwards link tracking */ +static void updateBackRefs(dbCommon *prec) { - lockRecord *p1lockRecord = pfirst->lset; - lockRecord *p2lockRecord = psecond->lset; - lockSet *p1lockSet; - lockSet *p2lockSet; - lockRecord *plockRecord; - lockRecord *pnext; + size_t i; + /* for each link */ + for(i=0; irdes->no_links; i++) { + linkRef *bref = &prec->lset->links[i]; + dbFldDes *pdesc = prec->rdes->papFldDes[prec->rdes->link_ind[i]]; + DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); - epicsMutexMustLock(lockSetModifyLock); - if(pfirst==psecond) goto all_done; - p1lockSet = p1lockRecord->plockSet; - p2lockSet = p2lockRecord->plockSet; - assert(p1lockSet || p2lockSet); - if(p1lockSet == p2lockSet) goto all_done; - if(!p1lockSet) { - p1lockRecord->plockSet = p2lockSet; - ellAdd(&p2lockSet->lockRecordList,&p1lockRecord->node); - goto all_done; - } - if(!p2lockSet) { - p2lockRecord->plockSet = p1lockSet; - ellAdd(&p1lockSet->lockRecordList,&p2lockRecord->node); - goto all_done; - } - /*Move entire second list to first*/ - assert(p1lockSet->type == p2lockSet->type); - plockRecord = (lockRecord *)ellFirst(&p2lockSet->lockRecordList); - while(plockRecord) { - pnext = (lockRecord *)ellNext(&plockRecord->node); - ellDelete(&p2lockSet->lockRecordList,&plockRecord->node); - plockRecord->plockSet = p1lockSet; - ellAdd(&p1lockSet->lockRecordList,&plockRecord->node); - plockRecord = pnext; - } - ellDelete(&lockSetList[p2lockSet->type],&p2lockSet->node); - p2lockSet->type = listTypeFree; - ellAdd(&lockSetList[listTypeFree],&p2lockSet->node); -all_done: - epicsMutexUnlock(lockSetModifyLock); - return; -} - -void dbLockSetSplit(dbCommon *psource) -{ - lockSet *plockSet; - lockRecord *plockRecord; - lockRecord *pnext; - dbCommon *precord; - int link; - dbRecordType *pdbRecordType; - dbFldDes *pdbFldDes; - DBLINK *plink; - int indlockRecord,nlockRecords; - lockRecord **paplockRecord; - epicsThreadId idself = epicsThreadGetIdSelf(); - + if(plink->type!=DB_LINK && bref->ptarget) + { + /* removed link */ + ellDelete(&bref->ptarget->backlinks, &bref->backlinksnode); + bref->ptarget = NULL; + } else if(plink->type==DB_LINK) + { + DBADDR *paddr = (DBADDR*)plink->value.pv_link.pvt; - plockRecord = psource->lset; - assert(plockRecord); - plockSet = plockRecord->plockSet; - assert(plockSet); - assert(plockSet->state==lockSetStateRecordLock); - assert(plockSet->type==listTypeRecordLock); - /*First remove all records from lock set and store in paplockRecord*/ - nlockRecords = ellCount(&plockSet->lockRecordList); - paplockRecord = dbCalloc(nlockRecords,sizeof(lockRecord*)); - epicsMutexMustLock(lockSetModifyLock); - plockRecord = (lockRecord *)ellFirst(&plockSet->lockRecordList); - for(indlockRecord=0; indlockRecordnode); - ellDelete(&plockSet->lockRecordList,&plockRecord->node); - plockRecord->plockSet = 0; - paplockRecord[indlockRecord] = plockRecord; - plockRecord = pnext; - } - ellDelete(&lockSetList[plockSet->type],&plockSet->node); - plockSet->state = lockSetStateFree; - plockSet->type = listTypeFree; - ellAdd(&lockSetList[listTypeFree],&plockSet->node); - epicsMutexUnlock(lockSetModifyLock); - /*Now recompute lock sets */ - for(indlockRecord=0; indlockRecordplockSet) { - allocLockSet(plockRecord,listTypeRecordLock, - lockSetStateRecordLock,idself); - } - precord = plockRecord->precord; - epicsMutexUnlock(lockSetModifyLock); - pdbRecordType = precord->rdes; - for(link=0; linkno_links; link++) { - DBADDR *pdbAddr; + if(paddr->precord->lset != bref->ptarget) { + /* changed link */ + if(bref->ptarget) { + /* clear old */ + ellDelete(&bref->ptarget->backlinks, &bref->backlinksnode); + bref->ptarget = NULL; + } - pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[link]]; - plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - if(plink->type != DB_LINK) continue; - pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); - dbLockSetMerge(precord,pdbAddr->precord); + bref->ptarget = paddr->precord->lset; + ellAdd(&bref->ptarget->backlinks, &bref->backlinksnode); + } } } - free(paplockRecord); } - + +/* Caller must lock both pfirst and psecond. + * Assumes that pfirst has been modified + * to link to psecond. + */ +void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) +{ + ELLNODE *cur; + lockSet *A=pfirst->lset->plockSet, + *B=psecond->lset->plockSet; + int Nb; +#ifdef LOCKSET_DEBUG + const epicsThreadId myself = epicsThreadGetIdSelf(); +#endif + + assert(A && B); + +#ifdef LOCKSET_DEBUG + if(A->owner!=myself || B->owner!=myself) { + errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n", + locker, pfirst->name, psecond->name, + A->owner, B->owner, myself); + cantProceed(NULL); + } +#endif + if(A->ownerlocker!=locker || B->ownerlocker!=locker) { + errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n", + locker, pfirst->name, psecond->name, + A->ownerlocker, B->ownerlocker, locker); + cantProceed(NULL); + } + + if(A==B) + return; /* already in the same lockSet */ + + updateBackRefs(pfirst); /* not required */ + + Nb = ellCount(&B->lockRecordList); + assert(Nb>0); + + /* move all records from B to A */ + while((cur=ellGet(&B->lockRecordList))!=NULL) + { + lockRecord *lr = CONTAINER(cur, lockRecord, node); + assert(lr->plockSet==B); + epicsSpinLock(lr->spin); + ellAdd(&A->lockRecordList, cur); + lr->plockSet = A; +#ifndef LOCKSET_NOCNT + epicsAtomicIncrSizeT(&recomputeCnt); +#endif + epicsSpinUnlock(lr->spin); + } + + /* there is at least 1 ref for each lockRecord, + * and at least one for the locker's locked list + * (perhaps another for its refs cache + */ + assert(epicsAtomicGetIntT(&B->refcount)>=Nb+(locker?1:0)); + + /* update ref counters. for lockRecords */ + epicsAtomicAddIntT(&A->refcount, Nb); + epicsAtomicAddIntT(&B->refcount, -Nb+1); /* drop all but one ref, see below */ + + if(locker) { + /* at least two ref, possibly three remain. + * # One ref from above + * # locker->locked list, which is released now. + * # locker->refs array, assuming it is directly referenced, + * and not added as the result of a dbLockSetSplit, + * which will be cleaned when the locker is free'd (not here). + */ +#ifdef LOCKSET_DEBUG + B->owner = NULL; + B->ownercount = 0; +#endif + assert(B->ownerlocker==locker); + ellDelete(&locker->locked, &B->lockernode); + B->ownerlocker = NULL; + epicsAtomicDecrIntT(&B->refcount); + } + + epicsMutexUnlock(B->lock); + + dbLockDecRef(B); /* last ref from above */ + + assert(A==psecond->lset->plockSet); +} + +/* recompute assuming a link from pfirst to psecond + * may have been removed. + * pfirst and psecond must currently be in the same lockset, + * which the caller must lock before calling this function. + * If a new lockset is created, then it is locked + * when this function returns. + */ +void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) +{ + lockSet *ls = pfirst->lset->plockSet; + ELLLIST toInspect, newLS; +#ifdef LOCKSET_DEBUG + const epicsThreadId myself = epicsThreadGetIdSelf(); +#endif + +#ifdef LOCKSET_DEBUG + if(ls->owner!=myself || psecond->lset->plockSet->owner!=myself) { + errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n", + locker, pfirst->name, psecond->name, + ls->owner, psecond->lset->plockSet->owner, myself); + cantProceed(NULL); + } +#endif + + /* lockset consistency violation */ + if(ls!=psecond->lset->plockSet) { + errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") consistency violation %p %p\n", + locker, pfirst->name, psecond->name, + pfirst->lset->plockSet, psecond->lset->plockSet); + cantProceed(NULL); + } + + updateBackRefs(pfirst); + + if(pfirst==psecond) + return; + + /* at least 1 ref for each lockRecord, + * and one for the locker + */ + assert(ls->refcount>=ellCount(&ls->lockRecordList)+1); + + ellInit(&toInspect); + ellInit(&newLS); + + /* strategy is to start with psecond and do + * a breadth first traversal until all records are + * visited. If we encounter pfirst, then there + * is no need to create a new lockset so we abort + * early. + */ + ellAdd(&toInspect, &psecond->lset->compnode); + psecond->lset->compflag = 1; + + { + lockSet *splitset; + ELLNODE *cur; + while((cur=ellGet(&toInspect))!=NULL) + { + lockRecord *lr=CONTAINER(cur,lockRecord,compnode); + dbCommon *prec=lr->precord; + dbRecordType *rtype = prec->rdes; + size_t i; + ELLNODE *bcur; + + ellAdd(&newLS, cur); + prec->lset->compflag = 2; + + /* Visit all the links originating from prec */ + for(i=0; ino_links; i++) { + linkRef *bref=&lr->links[i]; + lockRecord *lr = bref->ptarget; + + if(!lr) + continue; /* not DB_LINK */ + + if(lr->precord==pfirst) { + /* so pfirst is still reachable from psecond, + * no new lock set should be created. + */ + goto nosplit; + } + + /* have we already visited this record? */ + if(lr->compflag) + continue; + + ellAdd(&toInspect, &lr->compnode); + lr->compflag = 1; + } + + /* Visit all links terminating at prec */ + for(bcur=ellFirst(&lr->backlinks); bcur; bcur=ellNext(bcur)) + { + linkRef *bref=CONTAINER(bcur, linkRef, backlinksnode); + lockRecord *lr = bref->psrc; + + if(lr->precord==pfirst) { + goto nosplit; + } + + if(lr->compflag) + continue; + + ellAdd(&toInspect, &lr->compnode); + lr->compflag = 1; + } + } + /* All links from psecond were traversed without finding + * pfirst. So we must create a new lockset. + * newLS contains the nodes which will + * make up this new lockset. + */ + /* newLS will have at least psecond in it */ + assert(ellCount(&newLS) > 0); + /* If we didn't find pfirst, then it must be in the + * original lockset, and not the new one + */ + assert(ellCount(&newLS) < ellCount(&ls->lockRecordList)); + assert(ellCount(&newLS) < ls->refcount); + + splitset = makeSet(); /* reference for locker->locked */ + + epicsMutexMustLock(splitset->lock); + + assert(splitset->ownerlocker==NULL); + ellAdd(&locker->locked, &splitset->lockernode); + splitset->ownerlocker = locker; + + assert(splitset->refcount==1); + +#ifdef LOCKSET_DEBUG + splitset->owner = ls->owner; + splitset->ownercount = 1; + assert(ls->ownercount==1); +#endif + + while((cur=ellGet(&newLS))!=NULL) + { + lockRecord *lr=CONTAINER(cur,lockRecord,compnode); + + lr->compflag = 0; /* reset for next time */ + + assert(lr->plockSet == ls); + + /* lockset is "live" at this point + * as other threads may find it. + */ + epicsSpinLock(lr->spin); + ellDelete(&ls->lockRecordList, &lr->node); + ellAdd(&splitset->lockRecordList, &lr->node); + lr->plockSet = splitset; +#ifndef LOCKSET_NOCNT + epicsAtomicIncrSizeT(&recomputeCnt); +#endif + epicsSpinUnlock(lr->spin); + } + + /* refcount of ls can't go to zero as the locker + * holds at least one reference (its locked list) + */ + epicsAtomicAddIntT(&ls->refcount, -ellCount(&splitset->lockRecordList)); + assert(ls->refcount>0); + epicsAtomicAddIntT(&splitset->refcount, ellCount(&splitset->lockRecordList)); + + assert(splitset->refcount>=ellCount(&splitset->lockRecordList)+1); + + assert(psecond->lset->plockSet==splitset); + + /* must have refs from pfirst lockRecord, + * and the locked list. + */ + assert(epicsAtomicGetIntT(&ls->refcount)>=2); + + return; + } + +nosplit: + { + /* reset compflag for all nodes visited + * during the aborted search + */ + ELLNODE *cur; + while((cur=ellGet(&toInspect))!=NULL) + { + lockRecord *lr=CONTAINER(cur,lockRecord,compnode); + lr->compflag = 0; + } + while((cur=ellGet(&newLS))!=NULL) + { + lockRecord *lr=CONTAINER(cur,lockRecord,compnode); + lr->compflag = 0; + } + return; + } +} + +static char *msstring[4]={"NMS","MS","MSI","MSS"}; + long dblsr(char *recordname,int level) { int link; @@ -524,45 +971,34 @@ long dblsr(char *recordname,int level) dbFldDes *pdbFldDes; DBLINK *plink; - printf("globalLock %p\n",globalLock); - printf("lockSetModifyLock %p\n",lockSetModifyLock); if (recordname && ((*recordname == '\0') || !strcmp(recordname,"*"))) recordname = NULL; if(recordname) { dbInitEntry(pdbbase,pdbentry); status = dbFindRecord(pdbentry,recordname); if(status) { - printf("Record not found\n"); + errlogPrintf("Record not found\n"); dbFinishEntry(pdbentry); - return(0); + goto done; } precord = pdbentry->precnode->precord; dbFinishEntry(pdbentry); plockRecord = precord->lset; - if(!plockRecord) return(0); + if(!plockRecord) goto done; /* too early (before iocInit) */ plockSet = plockRecord->plockSet; } else { - plockSet = (lockSet *)ellFirst(&lockSetList[listTypeScanLock]); + plockSet = (lockSet *)ellFirst(&lockSetsActive); } for( ; plockSet; plockSet = (lockSet *)ellNext(&plockSet->node)) { - printf("Lock Set %lu %d members epicsMutexId %p", - plockSet->id,ellCount(&plockSet->lockRecordList),plockSet->lock); - if(epicsMutexTryLock(plockSet->lock)==epicsMutexLockOK) { - epicsMutexUnlock(plockSet->lock); - printf(" Not Locked\n"); - } else { - printf(" thread %p",plockSet->thread_id); - if(! plockSet->precord || !plockSet->precord->name) - printf(" NULL record or record name\n"); - else - printf(" record %s\n",plockSet->precord->name); - } + errlogPrintf("Lock Set %lu %d members %d refs epicsMutexId %p\n", + plockSet->id,ellCount(&plockSet->lockRecordList),plockSet->refcount,plockSet->lock); + if(level==0) { if(recordname) break; continue; } for(plockRecord = (lockRecord *)ellFirst(&plockSet->lockRecordList); plockRecord; plockRecord = (lockRecord *)ellNext(&plockRecord->node)) { precord = plockRecord->precord; pdbRecordType = precord->rdes; - printf("%s\n",precord->name); + errlogPrintf("%s\n",precord->name); if(level<=1) continue; for(link=0; (linkno_links) ; link++) { DBADDR *pdbAddr; @@ -570,22 +1006,24 @@ long dblsr(char *recordname,int level) plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if(plink->type != DB_LINK) continue; pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); - printf("\t%s",pdbFldDes->name); + errlogPrintf("\t%s",pdbFldDes->name); if(pdbFldDes->field_type==DBF_INLINK) { - printf("\t INLINK"); + errlogPrintf("\t INLINK"); } else if(pdbFldDes->field_type==DBF_OUTLINK) { - printf("\tOUTLINK"); + errlogPrintf("\tOUTLINK"); } else if(pdbFldDes->field_type==DBF_FWDLINK) { - printf("\tFWDLINK"); + errlogPrintf("\tFWDLINK"); } - printf(" %s %s", + errlogPrintf(" %s %s", ((plink->value.pv_link.pvlMask&pvlOptPP)?" PP":"NPP"), msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); - printf(" %s\n",pdbAddr->precord->name); + errlogPrintf(" %s\n",pdbAddr->precord->name); } } if(recordname) break; } +done: + errlogFlush(); return(0); } @@ -593,36 +1031,22 @@ long dbLockShowLocked(int level) { int indListType; lockSet *plockSet; - epicsMutexLockStatus status; - epicsMutexLockStatus lockSetModifyLockStatus = epicsMutexLockOK; - int itry; - printf("listTypeScanLock %d listTypeRecordLock %d listTypeFree %d\n", - ellCount(&lockSetList[0]), - ellCount(&lockSetList[1]), - ellCount(&lockSetList[2])); - for(itry=0; itry<100; itry++) { - lockSetModifyLockStatus = epicsMutexTryLock(lockSetModifyLock); - if(lockSetModifyLockStatus==epicsMutexLockOK) break; - epicsThreadSleep(.05); - } - if(lockSetModifyLockStatus!=epicsMutexLockOK) { - printf("Could not lock lockSetModifyLock\n"); - epicsMutexShow(lockSetModifyLock,level); - } - status = epicsMutexTryLock(globalLock); - if(status==epicsMutexLockOK) { - epicsMutexUnlock(globalLock); - } else { - printf("globalLock is locked\n"); - epicsMutexShow(globalLock,level); - } + errlogPrintf("lockSets %d listTypeFree %d\n", + ellCount(&lockSetsActive), +#ifndef LOCKSET_FREE + ellCount(&lockSetsFree) +#else + -1 +#endif + ); + /*Even if failure on lockSetModifyLock will continue */ for(indListType=0; indListType <= 1; ++indListType) { - plockSet = (lockSet *)ellFirst(&lockSetList[indListType]); + plockSet = (lockSet *)ellFirst(&lockSetsActive); if(plockSet) { - if(indListType==0) printf("listTypeScanLock\n"); - else printf("listTypeRecordLock\n"); + if(indListType==0) errlogPrintf("listTypeScanLock\n"); + else errlogPrintf("listTypeRecordLock\n"); } while(plockSet) { epicsMutexLockStatus status; @@ -630,18 +1054,13 @@ long dbLockShowLocked(int level) status = epicsMutexTryLock(plockSet->lock); if(status==epicsMutexLockOK) epicsMutexUnlock(plockSet->lock); if(status!=epicsMutexLockOK || indListType==1) { - if(plockSet->precord) - printf("%s ",plockSet->precord->name); - printf("state %d thread_id %p nRecursion %d nWaiting %d\n", - plockSet->state,plockSet->thread_id, - plockSet->nRecursion,plockSet->nWaiting); + epicsMutexShow(plockSet->lock,level); } plockSet = (lockSet *)ellNext(&plockSet->node); } } - if(lockSetModifyLockStatus==epicsMutexLockOK) - epicsMutexUnlock(lockSetModifyLock); + errlogFlush(); return(0); } diff --git a/src/ioc/db/dbLock.h b/src/ioc/db/dbLock.h index e13ce38b1..bed0199d0 100644 --- a/src/ioc/db/dbLock.h +++ b/src/ioc/db/dbLock.h @@ -13,6 +13,7 @@ #ifndef INCdbLockh #define INCdbLockh +#include "ellLib.h" #include "shareLib.h" #ifdef __cplusplus @@ -21,21 +22,27 @@ extern "C" { struct dbCommon; struct dbBase; +//struct dbLockSet; +typedef struct dbLocker dbLocker; epicsShareFunc void dbScanLock(struct dbCommon *precord); epicsShareFunc void dbScanUnlock(struct dbCommon *precord); + +epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon **precs, + size_t nrecs, + unsigned int flags); + +epicsShareFunc void dbLockerFree(dbLocker *); + +epicsShareFunc void dbScanLockMany(dbLocker*); +epicsShareFunc void dbScanUnlockMany(dbLocker*); + epicsShareFunc unsigned long dbLockGetLockId( struct dbCommon *precord); epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase); epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase); -epicsShareFunc void dbLockSetMerge( - struct dbCommon *pfirst,struct dbCommon *psecond); -epicsShareFunc void dbLockSetSplit(struct dbCommon *psource); -/*The following are for code that modifies lock sets*/ -epicsShareFunc void dbLockSetGblLock(void); -epicsShareFunc void dbLockSetGblUnlock(void); -epicsShareFunc void dbLockSetRecordLock(struct dbCommon *precord); + /* Lock Set Report */ epicsShareFunc long dblsr(char *recordname,int level); @@ -47,6 +54,10 @@ epicsShareFunc long dbLockShowLocked(int level); /*KLUDGE to support field TPRO*/ epicsShareFunc int * dbLockSetAddrTrace(struct dbCommon *precord); +/* debugging */ +epicsShareFunc unsigned long dbLockGetRefs(struct dbCommon*); +epicsShareFunc unsigned long dbLockCountSets(void); + #ifdef __cplusplus } #endif diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h new file mode 100644 index 000000000..4a5646dd7 --- /dev/null +++ b/src/ioc/db/dbLockPvt.h @@ -0,0 +1,109 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Brookhaven +* National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef DBLOCKPVT_H +#define DBLOCKPVT_H + +#include "dbLock.h" + +#define LOCKSET_DEBUG +#define LOCKSET_FREE +#define LOCKSET_NOCNT + +/* except for refcount (and lock), all members of dbLockSet + * are guarded by its lock. + */ +typedef struct dbLockSet { + ELLNODE node; + ELLLIST lockRecordList; /* holds lockRecord::node */ + epicsMutexId lock; + unsigned long id; + + int refcount; +#ifdef LOCKSET_DEBUG + int ownercount; + epicsThreadId owner; +#endif + dbLocker *ownerlocker; + ELLNODE lockernode; + + int trace; /*For field TPRO*/ +} lockSet; + +struct lockRecord; + +typedef struct { + ELLNODE backlinksnode; + struct lockRecord *psrc; + struct lockRecord *ptarget; +} linkRef; + +/* dbCommon.LSET is a plockRecord. + * Except for spin and plockSet, all members of lockRecord are guarded + * by the present lockset lock. + * plockSet is guarded by spin. + */ +typedef struct lockRecord { + ELLNODE node; + /* The association between lockRecord and lockSet + * can only be changed while the lockSet is held, + * and the lockRecord's spinlock is held. + * It may be read which either lock is held. + */ + lockSet *plockSet; + dbCommon *precord; + epicsSpinId spin; + + /* temp used during lockset split */ + ELLNODE compnode; + unsigned int compflag; + + ELLLIST backlinks; + linkRef links[1]; /* actual size based on no_links from dbRecordType */ +} lockRecord; + +typedef struct { + lockRecord *plr; + /* the last lock found associated with the ref. + * not stable unless lock is locked, or ref spin + * is locked. + */ + lockSet *plockSet; +} lockRecordRef; + +#define DBLOCKER_NALLOC 3 +/* a dbLocker can only be used by a single thread. */ +struct dbLocker { + ELLLIST locked; +#ifndef LOCKSET_NOCNT + size_t recomp; /* snapshot of recomputeCnt when refs[] cache updated */ +#endif + size_t maxrefs; + lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */ +}; + +lockSet* dbLockGetRef(lockRecord *lr); +void dbLockIncRef(lockSet* ls); +void dbLockDecRef(lockSet *ls); + +/* Optimization used by for dbLocker on the stack. + * nrecs must be <=DBLOCKER_NALLOC. + */ +void dbLockerPrepare(struct dbLocker *locker, + struct dbCommon **precs, + size_t nrecs); +void dbLockerFinalize(dbLocker *); + +void dbLockSetMerge(struct dbLocker *locker, + struct dbCommon *pfirst, + struct dbCommon *psecond); +void dbLockSetSplit(struct dbLocker *locker, + struct dbCommon *psource, + struct dbCommon *psecond); + +#endif // DBLOCKPVT_H From ffd188bea36a7715df80a2add4a1e8d3c6555352 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:14:46 -0400 Subject: [PATCH 058/204] dbLockTest and dbStressLock --- src/ioc/db/test/Makefile | 8 + src/ioc/db/test/dbLockTest.c | 340 +++++++++++++++++++++++++++++++- src/ioc/db/test/dbLockTest.db | 3 + src/ioc/db/test/dbStressLock.c | 311 +++++++++++++++++++++++++++++ src/ioc/db/test/dbStressLock.db | 40 ++++ 5 files changed, 698 insertions(+), 4 deletions(-) create mode 100644 src/ioc/db/test/dbStressLock.c create mode 100644 src/ioc/db/test/dbStressLock.db diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index c51f1a119..afed4f7b0 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -10,6 +10,9 @@ TOP=../../../.. include $(TOP)/configure/CONFIG +# Allow access to private headers in db/ +USR_CPPFLAGS = -I ../.. + TESTLIBRARY = dbTestIoc dbTestIoc_SRCS += xRecord.c @@ -54,6 +57,11 @@ testHarness_SRCS += dbLockTest.c TESTS += dbLockTest TESTFILES += ../dbLockTest.db +TESTPROD_HOST += dbStressTest +dbStressTest_SRCS += dbStressLock.c +dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +TESTS += dbStressTest + TESTPROD_HOST += testdbConvert testdbConvert_SRCS += testdbConvert.c testHarness_SRCS += testdbConvert.c diff --git a/src/ioc/db/test/dbLockTest.c b/src/ioc/db/test/dbLockTest.c index defb92f87..042917546 100644 --- a/src/ioc/db/test/dbLockTest.c +++ b/src/ioc/db/test/dbLockTest.c @@ -9,7 +9,15 @@ * Author: Michael Davidsaver */ -#include "dbLock.h" +#include + +#include "epicsSpin.h" +#include "epicsMutex.h" +#include "dbCommon.h" +#include "epicsThread.h" + +#include "dbLockPvt.h" +#include "dbStaticLib.h" #include "dbUnitTest.h" #include "testMain.h" @@ -28,15 +36,19 @@ void compareSets(int match, const char *A, const char *B) rA = testdbRecordPtr(A); rB = testdbRecordPtr(B); - actual = dbLockGetLockId(rA)==dbLockGetLockId(rB); + actual = rA->lset->plockSet==rB->lset->plockSet; testOk(match==actual, "dbLockGetLockId(\"%s\")%c=dbLockGetLockId(\"%s\")", A, match?'=':'!', B); } +#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B); +#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B); + static void testSets(void) { - testDiag("Check initial creation of DB links"); + DBENTRY entry; + long status; testdbPrepare(); @@ -48,6 +60,27 @@ void testSets(void) { testIocInitOk(); eltc(1); + testDiag("Check that all records have initialized lockRecord and lockSet"); + + dbInitEntry(pdbbase, &entry); + for(status = dbFirstRecordType(&entry); + !status; + status = dbNextRecordType(&entry)) { + for(status = dbFirstRecord(&entry); + !status; + status = dbNextRecord(&entry)) { + dbCommon *prec = entry.precnode->precord; + testOk(prec->lset!=NULL, "%s.LSET != NULL", prec->name); + if(prec->lset!=NULL) + testOk(prec->lset->plockSet!=NULL, "%s.LSET.plockSet != NULL", prec->name); + else + testSkip(1, "lockRecord missing"); + } + } + dbFinishEntry(&entry); + + testDiag("Check initial creation of DB links"); + /* reca is by itself */ compareSets(0, "reca", "recb"); compareSets(0, "reca", "recc"); @@ -71,6 +104,299 @@ void testSets(void) { compareSets(1, "rece", "recf"); + testOk1(testdbRecordPtr("reca")->lset->plockSet->refcount==1); + testOk1(testdbRecordPtr("recb")->lset->plockSet->refcount==2); + testOk1(testdbRecordPtr("recd")->lset->plockSet->refcount==3); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void testSingleLock(void) +{ + dbCommon *prec; + testDiag("testing dbScanLock()/dbScanUnlock()"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + prec = testdbRecordPtr("reca"); + testOk1(prec->lset->plockSet->refcount==1); + dbScanLock(prec); + /* scan lock does not keep a reference to the lockSet */ + testOk1(prec->lset->plockSet->refcount==1); + dbScanUnlock(prec); + + dbScanLock(prec); + dbScanLock(prec); + /* scan lock can be recursive */ + testOk1(prec->lset->plockSet->refcount==1); + dbScanUnlock(prec); + dbScanUnlock(prec); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void testMultiLock(void) +{ + dbCommon *prec[8]; + dbLocker *plockA; + epicsThreadId myself = epicsThreadGetIdSelf(); + + testDiag("Test multi-locker function (lock everything)"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + prec[0] = testdbRecordPtr("reca"); + prec[1] = testdbRecordPtr("recb"); + prec[2] = testdbRecordPtr("recc"); + prec[3] = NULL; + prec[4] = testdbRecordPtr("recd"); + prec[5] = testdbRecordPtr("rece"); + prec[6] = testdbRecordPtr("recf"); + prec[7] = testdbRecordPtr("recg"); + + testDiag("Test init refcounts"); + testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1); + testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2); + testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3); + testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1); + + plockA = dbLockerAlloc(prec, 8, 0); + if(!plockA) + testAbort("dbLockerAlloc() failed"); + + + testDiag("After locker created"); + /* locker takes 7 references, one for each lockRecord. */ + testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2); + testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4); + testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6); + testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2); +#ifdef LOCKSET_DEBUG + testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL); + testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL); + testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL); + testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL); +#endif + + dbScanLockMany(plockA); + + testDiag("After locker locked"); + /* locker takes 4 references, one for each lockSet. */ + testIntOk1(ellCount(&plockA->locked),==,4); + testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3); + testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,5); + testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,7); + testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,3); +#ifdef LOCKSET_DEBUG + testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,myself); + testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,myself); + testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,myself); + testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,myself); +#endif + + dbScanUnlockMany(plockA); + + testDiag("After locker unlocked"); + testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2); + testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4); + testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6); + testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2); +#ifdef LOCKSET_DEBUG + testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL); + testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL); + testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL); + testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL); +#endif + + dbLockerFree(plockA); + + testDiag("After locker free'd"); + testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1); + testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2); + testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3); + testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void testLinkBreak(void) +{ + dbCommon *precB, *precC; + testDiag("Test break link"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + precB = testdbRecordPtr("recb"); + precC = testdbRecordPtr("recc"); + + testOk1(precB->lset->plockSet==precC->lset->plockSet); + testOk1(precB->lset->plockSet->refcount==2); + + /* break the link between B and C */ + testdbPutFieldOk("recb.SDIS", DBR_STRING, ""); + + testOk1(precB->lset->plockSet!=precC->lset->plockSet); + testIntOk1(precB->lset->plockSet->refcount, ==, 1); + testIntOk1(precC->lset->plockSet->refcount, ==, 1); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void testLinkMake(void) +{ + dbCommon *precA, *precG; + lockSet *lA, *lG; + testDiag("Test make link"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + precA = testdbRecordPtr("reca"); + lA = dbLockGetRef(precA->lset); + precG = testdbRecordPtr("recg"); + lG = dbLockGetRef(precG->lset); + + testPtrOk1(precA->lset->plockSet, !=, precG->lset->plockSet); + testIntOk1(precA->lset->plockSet->refcount, ==, 2); + testIntOk1(precG->lset->plockSet->refcount, ==, 2); + + /* make a link between A and G */ + testdbPutFieldOk("reca.SDIS", DBR_STRING, "recg"); + + testPtrOk1(precA->lset->plockSet, ==, precG->lset->plockSet); + testIntOk1(precA->lset->plockSet->refcount, ==, 3); + + if(precA->lset->plockSet==lG) { + testIntOk1(lA->refcount, ==, 1); + testIntOk1(lG->refcount, ==, 3); + } else { + testIntOk1(lA->refcount, ==, 3); + testIntOk1(lG->refcount, ==, 1); + } + dbLockDecRef(lG); + dbLockDecRef(lA); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void testLinkChange(void) +{ + dbCommon *precB, *precC, *precG; + lockSet *lB, *lG; + testDiag("Test re-target link"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + precB = testdbRecordPtr("recb"); + precC = testdbRecordPtr("recc"); + precG = testdbRecordPtr("recg"); + + lB = dbLockGetRef(precB->lset); + lG = dbLockGetRef(precG->lset); + + testPtrOk1(lB,==,precC->lset->plockSet); + testPtrOk1(lB,!=,lG); + testIntOk1(lB->refcount,==,3); + testIntOk1(lG->refcount,==,2); + + /* break the link between B and C and replace it + * with a link between B and G + */ + testdbPutFieldOk("recb.SDIS", DBR_STRING, "recg"); + + testPtrOk1(precB->lset->plockSet,==,lB); + testPtrOk1(precG->lset->plockSet,==,lB); + testPtrOk1(precC->lset->plockSet,!=,lB); + testPtrOk1(precC->lset->plockSet,!=,lG); + + testIntOk1(lB->refcount,==,3); + testIntOk1(lG->refcount,==,1); + testIntOk1(precC->lset->plockSet->refcount,==,1); + + dbLockDecRef(lB); + dbLockDecRef(lG); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static void testLinkNOP(void) +{ + dbCommon *precB, *precC; + testDiag("Test re-target link to the same destination"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + precB = testdbRecordPtr("recb"); + precC = testdbRecordPtr("recc"); + + testOk1(precB->lset->plockSet==precC->lset->plockSet); + testOk1(precB->lset->plockSet->refcount==2); + + /* renew link between B and C */ + testdbPutFieldOk("recb.SDIS", DBR_STRING, "recc"); + + testOk1(precB->lset->plockSet==precC->lset->plockSet); + testOk1(precB->lset->plockSet->refcount==2); + testIocShutdownOk(); testdbCleanup(); @@ -78,7 +404,13 @@ void testSets(void) { MAIN(dbLockTest) { - testPlan(15); + testPlan(99); testSets(); + testSingleLock(); + testMultiLock(); + testLinkBreak(); + testLinkMake(); + testLinkChange(); + testLinkNOP(); return testDone(); } diff --git a/src/ioc/db/test/dbLockTest.db b/src/ioc/db/test/dbLockTest.db index 5c8ceb19c..37ff6022e 100644 --- a/src/ioc/db/test/dbLockTest.db +++ b/src/ioc/db/test/dbLockTest.db @@ -19,3 +19,6 @@ record(x, "rece") { record(x, "recf") { } + +record(x, "recg") { +} diff --git a/src/ioc/db/test/dbStressLock.c b/src/ioc/db/test/dbStressLock.c new file mode 100644 index 000000000..c4cb5b2fb --- /dev/null +++ b/src/ioc/db/test/dbStressLock.c @@ -0,0 +1,311 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Lockset stress test. + * + * The test stratagy is for N threads to contend for M records. + * Each thread will perform one of three operations: + * 1) Lock a single record. + * 2) Lock several records. + * 3) Retarget the TSEL link of a record + * + * Author: Michael Davidsaver + */ + +#include +#include +#include + +#include "envDefs.h" +#include "epicsStdlib.h" +#include "epicsSpin.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "dbCommon.h" + +#include "dbLockPvt.h" +#include "dbStaticLib.h" + +#include "dbUnitTest.h" +#include "testMain.h" + +#include "dbAccess.h" +#include "errlog.h" + +#include "xRecord.h" + +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); + +/* number of seconds for the test to run */ +static double runningtime = 18.0; + +/* number of worker threads */ +static unsigned int nworkers = 5; + +static unsigned int nrecords; + +#define MAXLOCK 20 + +static dbCommon **precords; + +typedef struct { + int id; + unsigned long N[3]; + double X[3]; + double X2[3]; + + unsigned int done; + epicsEventId donevent; + + dbCommon *prec[MAXLOCK]; +} workerPriv; + +/* hopefully a uniform random number in [0.0, 1.0] */ +static +double getRand(void) +{ + return rand()/(double)RAND_MAX; +} + +static +void doSingle(workerPriv *p) +{ + size_t recn = (size_t)(getRand()*(nrecords-1)); + dbCommon *prec = precords[recn]; + xRecord *px = (xRecord*)prec; + + dbScanLock(prec); + px->val++; + dbScanUnlock(prec); +} + +static volatile int bitbucket; + +static +void doMulti(workerPriv *p) +{ + int sum = 0; + size_t i; + size_t nlock = 2 + (size_t)(getRand()*(MAXLOCK-3)); + size_t nrec = (size_t)(getRand()*(nrecords-1)); + dbLocker *locker; + + assert(nlock>=2); + assert(nlockprec[i] = precords[nrec]; + } + + locker = dbLockerAlloc(p->prec, nlock, 0); + if(!locker) + testAbort("locker allocation fails"); + + dbScanLockMany(locker); + for(i=0; iprec[i]; + sum += px->val; + } + dbScanUnlockMany(locker); + + dbLockerFree(locker); +} + +static +void doreTarget(workerPriv *p) +{ + char scratchsrc[60]; + char scratchdst[MAX_STRING_SIZE]; + long ret; + DBADDR dbaddr; + double action = getRand(); + size_t nsrc = (size_t)(getRand()*(nrecords-1)); + size_t ntarg = (size_t)(getRand()*(nrecords-1)); + xRecord *psrc = (xRecord*)precords[nsrc]; + xRecord *ptarg = (xRecord*)precords[ntarg]; + + strcpy(scratchsrc, psrc->name); + strcat(scratchsrc, ".TSEL"); + + ret = dbNameToAddr(scratchsrc, &dbaddr); + if(ret) + testAbort("bad record name? %ld", ret); + + if(action<0.25) { + scratchdst[0] = '\0'; + } else { + strcpy(scratchdst, ptarg->name); + } + + ret = dbPutField(&dbaddr, DBR_STRING, ptarg->name, 1); + if(ret) + testAbort("put fails with %ld", ret); +} + +static +void worker(void *raw) +{ + struct timespec before; + workerPriv *priv = raw; + + testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf()); + + clock_gettime(CLOCK_MONOTONIC, &before); + + while(!priv->done) { + double sel = getRand(); + struct timespec after; + double duration; + + int act; + if(sel<0.33) { + doSingle(priv); + act = 0; + } else if(sel<0.66) { + doMulti(priv); + act = 1; + } else { + doreTarget(priv); + act = 2; + } + + clock_gettime(CLOCK_MONOTONIC, &after); + + duration = (double)((long)after.tv_nsec - (long)before.tv_nsec); + duration *= 1e-9; + duration += (double)(after.tv_sec - before.tv_sec); + + priv->N[act]++; + priv->X[act] += duration; + priv->X2[act] += duration*duration; + } + + epicsEventMustTrigger(priv->donevent); +} + +MAIN(dbStressTest) +{ + DBENTRY ent; + long status; + unsigned int i; + workerPriv *priv; + char *nwork=getenv("NWORK"); + struct timespec seed; + + testPlan(0); + + clock_gettime(CLOCK_REALTIME, &seed); + srand(seed.tv_nsec); + + if(nwork) { + long val = 0; + epicsParseLong(nwork, &val, 0, NULL); + if(val>2) + nworkers = val; + } + + priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory"); + + testDiag("lock set stress test"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbStressLock.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + /* collect an array of all records */ + dbInitEntry(pdbbase, &ent); + for(status = dbFirstRecordType(&ent); + !status; + status = dbNextRecordType(&ent)) + { + for(status = dbFirstRecord(&ent); + !status; + status = dbNextRecord(&ent)) + { + if(ent.precnode->flags&DBRN_FLAGS_ISALIAS) + continue; + nrecords++; + } + + } + if(nrecords<2) + testAbort("where are the records!"); + precords = callocMustSucceed(nrecords, sizeof(*precords), "no mem"); + for(status = dbFirstRecordType(&ent), i = 0; + !status; + status = dbNextRecordType(&ent)) + { + for(status = dbFirstRecord(&ent); + !status; + status = dbNextRecord(&ent)) + { + if(ent.precnode->flags&DBRN_FLAGS_ISALIAS) + continue; + precords[i++] = ent.precnode->precord; + } + + } + dbFinishEntry(&ent); + + testDiag("Running with %u workers and %u records", + nworkers, nrecords); + + for(i=0; i0); + testOk1(priv[i].N[1]>0); + testOk1(priv[i].N[2]>0); + } + + testIocShutdownOk(); + + testdbCleanup(); + + free(priv); + free(precords); + + return testDone(); +} diff --git a/src/ioc/db/test/dbStressLock.db b/src/ioc/db/test/dbStressLock.db new file mode 100644 index 000000000..5aecf860a --- /dev/null +++ b/src/ioc/db/test/dbStressLock.db @@ -0,0 +1,40 @@ +record(x, "rec01") {} +record(x, "rec02") {} +record(x, "rec03") {} +record(x, "rec04") {} +record(x, "rec05") {} +record(x, "rec06") {} +record(x, "rec07") {} +record(x, "rec08") {} +record(x, "rec09") {} +record(x, "rec10") {} +record(x, "rec11") {} +record(x, "rec12") {} +record(x, "rec13") {} +record(x, "rec14") {} +record(x, "rec15") {} +record(x, "rec16") {} +record(x, "rec17") {} +record(x, "rec18") {} +record(x, "rec19") {} +record(x, "rec20") {} +record(x, "rec21") {} +record(x, "rec22") {} +record(x, "rec23") {} +record(x, "rec24") {} +record(x, "rec25") {} +record(x, "rec26") {} +record(x, "rec27") {} +record(x, "rec28") {} +record(x, "rec29") {} +record(x, "rec30") {} +record(x, "rec31") {} +record(x, "rec32") {} +record(x, "rec33") {} +record(x, "rec34") {} +record(x, "rec35") {} +record(x, "rec36") {} +record(x, "rec37") {} +record(x, "rec38") {} +record(x, "rec39") {} +record(x, "rec40") {} From 8af3ffb6539cf66d44638a48ee6616c3f957239e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 059/204] dbAccess: multi-locking in dbPutFieldLink Use new locking API in dbPutFieldLink() Adjust dbAddLink() and dbRemoveLink() to pass a dbLocker* through to lockSet merge/split --- src/ioc/db/dbAccess.c | 25 +++++++++++++++++-------- src/ioc/db/dbLink.c | 22 +++++++++++++--------- src/ioc/db/dbLink.h | 6 ++++-- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index b13d7a138..b24739d2c 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -32,6 +32,7 @@ #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" +#include "epicsSpin.h" #include "errMdef.h" #define epicsExportSharedSymbols @@ -50,7 +51,7 @@ #include "dbFldTypes.h" #include "dbFldTypes.h" #include "dbLink.h" -#include "dbLock.h" +#include "dbLockPvt.h" #include "dbNotify.h" #include "dbScan.h" #include "dbServer.h" @@ -944,6 +945,8 @@ static long dbPutFieldLink(DBADDR *paddr, dbLinkInfo link_info; DBADDR *pdbaddr = NULL; dbCommon *precord = paddr->precord; + dbCommon *lockrecs[3]; + dbLocker locker; dbFldDes *pfldDes = paddr->pfldDes; long special = paddr->special; struct link *plink = (struct link *)paddr->pfield; @@ -956,6 +959,8 @@ static long dbPutFieldLink(DBADDR *paddr, int isDevLink; short scan; + STATIC_ASSERT(DBLOCKER_NALLOC>=3); + switch (dbrType) { case DBR_CHAR: case DBR_UCHAR: @@ -992,10 +997,13 @@ static long dbPutFieldLink(DBADDR *paddr, isDevLink = ellCount(&precord->rdes->devList) > 0 && pfldDes->isDevLink; - dbLockSetGblLock(); - dbLockSetRecordLock(precord); - if (pdbaddr) - dbLockSetRecordLock(pdbaddr->precord); + memset(&locker, 0, sizeof(locker)); + lockrecs[0] = precord; + lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL; + lockrecs[2] = NULL; + dbLockerPrepare(&locker, lockrecs, 3); + + dbScanLockMany(&locker); scan = precord->scan; @@ -1044,7 +1052,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* Old link type */ case DB_LINK: case CA_LINK: - dbRemoveLink(plink); + dbRemoveLink(&locker, precord, plink); break; case PV_LINK: @@ -1091,7 +1099,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* New link type */ case PV_LINK: - dbAddLink(precord, plink, pfldDes->field_type, pdbaddr); + dbAddLink(&locker, precord, plink, pfldDes->field_type, pdbaddr); break; case CONSTANT: @@ -1121,7 +1129,8 @@ postScanEvent: if (scan != precord->scan) db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG); unlock: - dbLockSetGblUnlock(); + dbScanUnlockMany(&locker); + dbLockerFinalize(&locker); cleanup: free(link_info.target); return status; diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 2ad9c6606..4ab5dcde5 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -23,6 +23,7 @@ #include "cantProceed.h" #include "cvtFast.h" #include "dbDefs.h" +#include "epicsSpin.h" #include "ellLib.h" #include "epicsThread.h" #include "epicsTime.h" @@ -45,7 +46,7 @@ #include "dbFldTypes.h" #include "dbFldTypes.h" #include "dbLink.h" -#include "dbLock.h" +#include "dbLockPvt.h" #include "dbNotify.h" #include "dbScan.h" #include "dbStaticLib.h" @@ -140,18 +141,21 @@ static long dbDbInitLink(struct link *plink, short dbfType) pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); *pdbAddr = dbaddr; /* structure copy */ plink->value.pv_link.pvt = pdbAddr; - dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord); + /* merging into the same lockset is deferred to the caller. + * cf. initPVLinks() + */ return 0; } -static void dbDbRemoveLink(struct link *plink) +static void dbDbRemoveLink(dbLocker *locker, struct dbCommon *prec, struct link *plink) { - free(plink->value.pv_link.pvt); + DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt; plink->value.pv_link.pvt = 0; plink->value.pv_link.getCvt = 0; plink->value.pv_link.lastGetdbrType = 0; plink->type = PV_LINK; - dbLockSetSplit(plink->value.pv_link.precord); + dbLockSetSplit(locker, prec, pdbAddr->precord); + free(pdbAddr); } static int dbDbIsLinkConnected(const struct link *plink) @@ -422,7 +426,7 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) } } -void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType, DBADDR *ptargetaddr) +void dbAddLink(dbLocker *locker, struct dbCommon *precord, struct link *plink, short dbfType, DBADDR *ptargetaddr) { plink->value.pv_link.precord = precord; @@ -436,7 +440,7 @@ void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType, DBAD plink->value.pv_link.pvt = ptargetaddr; /* target record is already locked in dbPutFieldLink() */ - dbLockSetMerge(plink->value.pv_link.precord, ptargetaddr->precord); + dbLockSetMerge(locker, plink->value.pv_link.precord, ptargetaddr->precord); return; } @@ -463,11 +467,11 @@ long dbLoadLink(struct link *plink, short dbrType, void *pbuffer) return S_db_notFound; } -void dbRemoveLink(struct link *plink) +void dbRemoveLink(dbLocker *locker, dbCommon *prec, struct link *plink) { switch (plink->type) { case DB_LINK: - dbDbRemoveLink(plink); + dbDbRemoveLink(locker, prec, plink); break; case CA_LINK: dbCaRemoveLink(plink); diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index f96f8ece1..006189728 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -50,13 +50,15 @@ typedef struct lset { #define dbGetSevr(PLINK, PSEVERITY) \ dbGetAlarm((PLINK), NULL, (PSEVERITY)) +struct dbLocker; + epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType); -epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink, +epicsShareFunc void dbAddLink(struct dbLocker *locker, struct dbCommon *precord, struct link *plink, short dbfType, DBADDR *ptargetaddr); epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, void *pbuffer); -epicsShareFunc void dbRemoveLink(struct link *plink); +epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct dbCommon *prec, struct link *plink); epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements); epicsShareFunc int dbIsLinkConnected(const struct link *plink); epicsShareFunc int dbGetLinkDBFtype(const struct link *plink); From b8a7da18d21c83ac159343a08ac1fd07dbafb425 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 060/204] iocInit: links now initialized in dbLockInitRecords() --- src/ioc/misc/iocInit.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 1f418223d..1c148fbbb 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -507,9 +507,6 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, } } } - - if (plink->type == PV_LINK) - dbInitLink(precord, plink, pdbFldDes->field_type); } } From 8ce0ba1e542598af35fb9ca1e18165de5505fbf1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 061/204] dbLink: backward link tracking --- src/ioc/db/dbCommon.dbd | 6 ++++++ src/ioc/db/dbLink.c | 3 +++ src/ioc/dbStatic/link.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/src/ioc/db/dbCommon.dbd b/src/ioc/db/dbCommon.dbd index f87853bec..f58d8d78c 100644 --- a/src/ioc/db/dbCommon.dbd +++ b/src/ioc/db/dbCommon.dbd @@ -92,6 +92,12 @@ interest(4) extra("ELLLIST mlis") } + field(BKLNK,DBF_NOACCESS) { + prompt("Backwards link tracking") + special(SPC_NOMOD) + interest(4) + extra("ELLLIST bklnk") + } field(DISP,DBF_UCHAR) { prompt("Disable putField") } diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 4ab5dcde5..0a440cfcb 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -141,6 +141,7 @@ static long dbDbInitLink(struct link *plink, short dbfType) pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); *pdbAddr = dbaddr; /* structure copy */ plink->value.pv_link.pvt = pdbAddr; + ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode); /* merging into the same lockset is deferred to the caller. * cf. initPVLinks() */ @@ -154,6 +155,7 @@ static void dbDbRemoveLink(dbLocker *locker, struct dbCommon *prec, struct link plink->value.pv_link.getCvt = 0; plink->value.pv_link.lastGetdbrType = 0; plink->type = PV_LINK; + ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode); dbLockSetSplit(locker, prec, pdbAddr->precord); free(pdbAddr); } @@ -438,6 +440,7 @@ void dbAddLink(dbLocker *locker, struct dbCommon *precord, struct link *plink, s plink->type = DB_LINK; plink->value.pv_link.pvt = ptargetaddr; + ellAdd(&ptargetaddr->precord->bklnk, &plink->value.pv_link.backlinknode); /* target record is already locked in dbPutFieldLink() */ dbLockSetMerge(locker, plink->value.pv_link.precord, ptargetaddr->precord); diff --git a/src/ioc/dbStatic/link.h b/src/ioc/dbStatic/link.h index 5ad7aeee3..5f2510616 100644 --- a/src/ioc/dbStatic/link.h +++ b/src/ioc/dbStatic/link.h @@ -17,6 +17,7 @@ #define INC_link_H #include "dbDefs.h" +#include "ellLib.h" #include "shareLib.h" #ifdef __cplusplus @@ -79,6 +80,7 @@ struct dbCommon; struct pvlet; struct pv_link { + ELLNODE backlinknode; char *pvname; /* pvname link points to */ struct dbCommon *precord; /* Address of record owning link */ void *pvt; /* CA or DB private */ From ee297dc55879e71dd963d79fce0d9566df68f07a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 062/204] dbLock: use new backref tracking --- src/ioc/db/dbAccess.c | 2 +- src/ioc/db/dbLock.c | 80 ++++++++++------------------------ src/ioc/db/dbLockPvt.h | 9 ---- src/ioc/db/test/dbStressLock.c | 33 ++++++++++++-- src/ioc/misc/iocInit.c | 7 ++- 5 files changed, 58 insertions(+), 73 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index b24739d2c..732e783dc 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -1052,7 +1052,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* Old link type */ case DB_LINK: case CA_LINK: - dbRemoveLink(&locker, precord, plink); + dbRemoveLink(&locker, precord, plink); /* link type becomes PV_LINK */ break; case PV_LINK: diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index a2fb4da28..2be1f5f82 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -135,11 +135,18 @@ void dbLockDecRef(lockSet *ls) if(cnt) return; + /* not necessary as no one else holds a reference, + * but lock anyway to quiet valgrind + */ + epicsMutexMustLock(ls->lock); + if(ellCount(&ls->lockRecordList)!=0) { errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n", ls, ellCount(&ls->lockRecordList)); cantProceed(NULL); } + epicsMutexUnlock(ls->lock); + epicsMutexMustLock(lockSetsGuard); ellDelete(&lockSetsActive, &ls->node); #ifndef LOCKSET_FREE @@ -491,13 +498,11 @@ done: static int createLockRecord(void* junk, DBENTRY* pdbentry) { dbCommon *prec = pdbentry->precnode->precord; - size_t i, no_links=prec->rdes->no_links; lockRecord *lrec; - size_t arrsize=(no_links-1)*sizeof(linkRef); - assert(no_links>=1); /* dbCommon has TSEL */ assert(!prec->lset); - lrec = callocMustSucceed(1, sizeof(*lrec)+arrsize, "lockRecord"); + /* TODO: one allocation for all records? */ + lrec = callocMustSucceed(1, sizeof(*lrec), "lockRecord"); if(!lrec) cantProceed("no memory for lockRecord"); lrec->spin = epicsSpinCreate(); @@ -505,11 +510,6 @@ static int createLockRecord(void* junk, DBENTRY* pdbentry) cantProceed("no memory for spinlock in lockRecord"); lrec->precord = prec; - ellInit(&lrec->backlinks); - - for(i=0; ilinks[i].psrc = lrec; - } prec->lset = lrec; return 0; @@ -524,7 +524,6 @@ static int initPVLinks(void* junk, DBENTRY* pdbentry) /* for each link originating from this record */ for(i=0; ino_links; i++) { - linkRef *bref = &prec->lset->links[i]; DBADDR *paddr; dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]]; DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); @@ -559,10 +558,6 @@ static int initPVLinks(void* junk, DBENTRY* pdbentry) dbLockIncRef(B); ellAdd(&B->lockRecordList, &prec->lset->node); } - - /* initialize backward link tracking */ - bref->ptarget = paddr->precord->lset; - ellAdd(&bref->ptarget->backlinks, &bref->backlinksnode); } return 0; } @@ -637,40 +632,6 @@ void dbLockCleanupRecords(dbBase *pdbbase) #endif } -/* update backwards link tracking */ -static void updateBackRefs(dbCommon *prec) -{ - size_t i; - /* for each link */ - for(i=0; irdes->no_links; i++) { - linkRef *bref = &prec->lset->links[i]; - dbFldDes *pdesc = prec->rdes->papFldDes[prec->rdes->link_ind[i]]; - DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); - - if(plink->type!=DB_LINK && bref->ptarget) - { - /* removed link */ - ellDelete(&bref->ptarget->backlinks, &bref->backlinksnode); - bref->ptarget = NULL; - } else if(plink->type==DB_LINK) - { - DBADDR *paddr = (DBADDR*)plink->value.pv_link.pvt; - - if(paddr->precord->lset != bref->ptarget) { - /* changed link */ - if(bref->ptarget) { - /* clear old */ - ellDelete(&bref->ptarget->backlinks, &bref->backlinksnode); - bref->ptarget = NULL; - } - - bref->ptarget = paddr->precord->lset; - ellAdd(&bref->ptarget->backlinks, &bref->backlinksnode); - } - } - } -} - /* Caller must lock both pfirst and psecond. * Assumes that pfirst has been modified * to link to psecond. @@ -705,8 +666,6 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) if(A==B) return; /* already in the same lockSet */ - updateBackRefs(pfirst); /* not required */ - Nb = ellCount(&B->lockRecordList); assert(Nb>0); @@ -791,7 +750,6 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) cantProceed(NULL); } - updateBackRefs(pfirst); if(pfirst==psecond) return; @@ -829,8 +787,16 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) /* Visit all the links originating from prec */ for(i=0; ino_links; i++) { - linkRef *bref=&lr->links[i]; - lockRecord *lr = bref->ptarget; + dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]]; + DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); + DBADDR *ptarget; + lockRecord *lr; + + if(plink->type!=DB_LINK) + continue; + + ptarget = plink->value.pv_link.pvt; + lr = ptarget->precord->lset; if(!lr) continue; /* not DB_LINK */ @@ -851,10 +817,12 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) } /* Visit all links terminating at prec */ - for(bcur=ellFirst(&lr->backlinks); bcur; bcur=ellNext(bcur)) + for(bcur=ellFirst(&prec->bklnk); bcur; bcur=ellNext(bcur)) { - linkRef *bref=CONTAINER(bcur, linkRef, backlinksnode); - lockRecord *lr = bref->psrc; + struct pv_link *plink1 = CONTAINER(bcur, struct pv_link, backlinknode); + union value *plink2 = CONTAINER(plink1, union value, pv_link); + DBLINK *plink = CONTAINER(plink2, DBLINK, value); + lockRecord *lr = plink->value.pv_link.precord->lset; if(lr->precord==pfirst) { goto nosplit; diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index 4a5646dd7..4913ac719 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -37,12 +37,6 @@ typedef struct dbLockSet { struct lockRecord; -typedef struct { - ELLNODE backlinksnode; - struct lockRecord *psrc; - struct lockRecord *ptarget; -} linkRef; - /* dbCommon.LSET is a plockRecord. * Except for spin and plockSet, all members of lockRecord are guarded * by the present lockset lock. @@ -62,9 +56,6 @@ typedef struct lockRecord { /* temp used during lockset split */ ELLNODE compnode; unsigned int compflag; - - ELLLIST backlinks; - linkRef links[1]; /* actual size based on no_links from dbRecordType */ } lockRecord; typedef struct { diff --git a/src/ioc/db/test/dbStressLock.c b/src/ioc/db/test/dbStressLock.c index c4cb5b2fb..7aa33100c 100644 --- a/src/ioc/db/test/dbStressLock.c +++ b/src/ioc/db/test/dbStressLock.c @@ -39,6 +39,9 @@ #include "xRecord.h" +#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B); +#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B); + void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); /* number of seconds for the test to run */ @@ -136,7 +139,7 @@ void doreTarget(workerPriv *p) if(ret) testAbort("bad record name? %ld", ret); - if(action<0.25) { + if(action<=0.6) { scratchdst[0] = '\0'; } else { strcpy(scratchdst, ptarg->name); @@ -197,7 +200,7 @@ MAIN(dbStressTest) char *nwork=getenv("NWORK"); struct timespec seed; - testPlan(0); + testPlan(95); clock_gettime(CLOCK_REALTIME, &seed); srand(seed.tv_nsec); @@ -272,7 +275,7 @@ MAIN(dbStressTest) &worker, &priv[i]); } - testDiag("All started"); + testDiag("All started. Will run for %f sec", runningtime); epicsThreadSleep(runningtime); @@ -289,6 +292,30 @@ MAIN(dbStressTest) testDiag("All stopped"); + testDiag("Validate lockSet ref counts"); + dbInitEntry(pdbbase, &ent); + for(status = dbFirstRecordType(&ent); + !status; + status = dbNextRecordType(&ent)) + { + for(status = dbFirstRecord(&ent); + !status; + status = dbNextRecord(&ent)) + { + dbCommon *prec = ent.precnode->precord; + lockSet *ls; + if(ent.precnode->flags&DBRN_FLAGS_ISALIAS) + continue; + ls = prec->lset->plockSet; + testOk(ellCount(&ls->lockRecordList)==ls->refcount, "%s only lockRecords hold refs. %d == %d", + prec->name,ellCount(&ls->lockRecordList),ls->refcount); + testOk1(ls->ownerlocker==NULL); + } + + } + dbFinishEntry(&ent); + + testDiag("Statistics"); for(i=0; ino_links; j++) { dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; - DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if (ellCount(&precord->rdes->devList) > 0 && pdbFldDes->isDevLink) { devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); @@ -631,13 +630,14 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, locked = 1; } dbCaRemoveLink(plink); - plink->type = CONSTANT; + plink->type = PV_LINK; } else if (plink->type == DB_LINK) { /* free link, but don't split lockset like dbDbRemoveLink() */ free(plink->value.pv_link.pvt); - plink->type = CONSTANT; + plink->type = PV_LINK; } + dbFreeLinkContents(plink); } if (precord->dset && @@ -672,7 +672,6 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - dbFreeLinkContents(plink); } } From c26b02c20d5ec2fa649e02ab8988dec599ea451c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 063/204] dbCaTest: adjust locking in dbcar() --- src/ioc/db/dbCaTest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/dbCaTest.c b/src/ioc/db/dbCaTest.c index 409fd526b..450eb9463 100644 --- a/src/ioc/db/dbCaTest.c +++ b/src/ioc/db/dbCaTest.c @@ -87,10 +87,10 @@ long dbcar(char *precordname, int level) !dbIsAlias(pdbentry)) { pdbRecordType = pdbentry->precordType; precord = (dbCommon *)pdbentry->precnode->precord; + dbScanLock(precord); for (j=0; jno_links; j++) { pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - dbLockSetGblLock(); if (plink->type == CA_LINK) { ncalinks++; pca = (caLink *)plink->value.pv_link.pvt; @@ -135,8 +135,8 @@ long dbcar(char *precordname, int level) } } } - dbLockSetGblUnlock(); } + dbScanUnlock(precord); if (precordname) goto done; } status = dbNextRecord(pdbentry); From a78abd00704dc21f930192982840b813ca1ac0e6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 064/204] dbLock: describe build options --- src/ioc/db/dbLock.c | 24 ++++++++++++++---------- src/ioc/db/dbLockPvt.h | 5 ++++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 2be1f5f82..98da7cec3 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -41,7 +41,7 @@ typedef struct dbScanLockNode dbScanLockNode; static epicsThreadOnceId dbLockOnceInit = EPICS_THREAD_ONCE_INIT; static ELLLIST lockSetsActive; /* in use */ -#ifndef LOCKSET_FREE +#ifndef LOCKSET_NOFREE static ELLLIST lockSetsFree; /* free list */ #endif @@ -49,10 +49,10 @@ static ELLLIST lockSetsFree; /* free list */ static epicsMutexId lockSetsGuard; #ifndef LOCKSET_NOCNT -/* Counter which is incremented whenever +/* Counter which we increment whenever * any lockRecord::plockSet is changed. - * An optimization to avoid a re-sort - * when no links have changed. + * An optimization to avoid checking lockSet + * associations when no links have changed. */ static size_t recomputeCnt; #endif @@ -73,7 +73,7 @@ static lockSet* makeSet(void) lockSet *ls; int iref; epicsMutexMustLock(lockSetsGuard); -#ifndef LOCKSET_FREE +#ifndef LOCKSET_NOFREE ls = (lockSet*)ellGet(&lockSetsFree); if(!ls) { epicsMutexUnlock(lockSetsGuard); @@ -84,7 +84,7 @@ static lockSet* makeSet(void) ls->lock = epicsMutexMustCreate(); ls->id = epicsAtomicIncrSizeT(&next_id); -#ifndef LOCKSET_FREE +#ifndef LOCKSET_NOFREE epicsMutexMustLock(lockSetsGuard); } #endif @@ -149,7 +149,7 @@ void dbLockDecRef(lockSet *ls) epicsMutexMustLock(lockSetsGuard); ellDelete(&lockSetsActive, &ls->node); -#ifndef LOCKSET_FREE +#ifndef LOCKSET_NOFREE ellAdd(&lockSetsFree, &ls->node); #else epicsMutexDestroy(ls->lock); @@ -309,6 +309,10 @@ int dbLockUpdateRefs(dbLocker *locker, int update) return changed; } #ifndef LOCKSET_NOCNT + /* Use the value captured before we started. + * If it has changed in the intrim we will catch this later + * during the update==0 pass (which triggers a re-try) + */ if(update) locker->recomp = recomp; } @@ -607,7 +611,7 @@ static int freeLockRecord(void* junk, DBENTRY* pdbentry) void dbLockCleanupRecords(dbBase *pdbbase) { -#ifndef LOCKSET_FREE +#ifndef LOCKSET_NOFREE ELLNODE *cur; #endif epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL); @@ -620,7 +624,7 @@ void dbLockCleanupRecords(dbBase *pdbbase) assert(ellCount(&lockSetsActive)==0); -#ifndef LOCKSET_FREE +#ifndef LOCKSET_NOFREE while((cur=ellGet(&lockSetsFree))!=NULL) { lockSet *ls = (lockSet*)cur; @@ -1002,7 +1006,7 @@ long dbLockShowLocked(int level) errlogPrintf("lockSets %d listTypeFree %d\n", ellCount(&lockSetsActive), -#ifndef LOCKSET_FREE +#ifndef LOCKSET_NOFREE ellCount(&lockSetsFree) #else -1 diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index 4913ac719..d74562d49 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -11,8 +11,11 @@ #include "dbLock.h" +/* enable additional error checking */ #define LOCKSET_DEBUG -#define LOCKSET_FREE +/* disable the free list for lockSets */ +#define LOCKSET_NOFREE +/* disable use of recomputeCnt optimization */ #define LOCKSET_NOCNT /* except for refcount (and lock), all members of dbLockSet From 58a8a07cc481f2c4f3200facc0a89344849f7017 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 065/204] dbAccess.c: dbLocker needs at most two refs --- src/ioc/db/dbAccess.c | 7 +++---- src/ioc/db/dbLockPvt.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 732e783dc..3da0d627d 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -945,7 +945,7 @@ static long dbPutFieldLink(DBADDR *paddr, dbLinkInfo link_info; DBADDR *pdbaddr = NULL; dbCommon *precord = paddr->precord; - dbCommon *lockrecs[3]; + dbCommon *lockrecs[2]; dbLocker locker; dbFldDes *pfldDes = paddr->pfldDes; long special = paddr->special; @@ -959,7 +959,7 @@ static long dbPutFieldLink(DBADDR *paddr, int isDevLink; short scan; - STATIC_ASSERT(DBLOCKER_NALLOC>=3); + STATIC_ASSERT(DBLOCKER_NALLOC>=2); switch (dbrType) { case DBR_CHAR: @@ -1000,8 +1000,7 @@ static long dbPutFieldLink(DBADDR *paddr, memset(&locker, 0, sizeof(locker)); lockrecs[0] = precord; lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL; - lockrecs[2] = NULL; - dbLockerPrepare(&locker, lockrecs, 3); + dbLockerPrepare(&locker, lockrecs, 2); dbScanLockMany(&locker); diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index d74562d49..d316ea359 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -70,7 +70,7 @@ typedef struct { lockSet *plockSet; } lockRecordRef; -#define DBLOCKER_NALLOC 3 +#define DBLOCKER_NALLOC 2 /* a dbLocker can only be used by a single thread. */ struct dbLocker { ELLLIST locked; From 07bb2fbef7fbf19863f309580ced4374ef7a37ae Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 066/204] dbLock: no c++ comments in c code --- src/ioc/db/dbLock.h | 1 - src/ioc/db/dbLockPvt.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ioc/db/dbLock.h b/src/ioc/db/dbLock.h index bed0199d0..9f2714057 100644 --- a/src/ioc/db/dbLock.h +++ b/src/ioc/db/dbLock.h @@ -22,7 +22,6 @@ extern "C" { struct dbCommon; struct dbBase; -//struct dbLockSet; typedef struct dbLocker dbLocker; epicsShareFunc void dbScanLock(struct dbCommon *precord); diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index d316ea359..5dd4aa44d 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -100,4 +100,4 @@ void dbLockSetSplit(struct dbLocker *locker, struct dbCommon *psource, struct dbCommon *psecond); -#endif // DBLOCKPVT_H +#endif /* DBLOCKPVT_H */ From 8fea2f5ae686245c9b4f1cca22fc39c889c44a3a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 067/204] dbLock: default build options Enable extra debugging. Disable lockSet free list. Enable recomputeCnt optimization --- src/ioc/db/dbLockPvt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index 5dd4aa44d..044be7eff 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -16,7 +16,7 @@ /* disable the free list for lockSets */ #define LOCKSET_NOFREE /* disable use of recomputeCnt optimization */ -#define LOCKSET_NOCNT +/*#define LOCKSET_NOCNT*/ /* except for refcount (and lock), all members of dbLockSet * are guarded by its lock. From 127bdfd9aa5384eba807a51a43fef10ddf60ae41 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 068/204] dbLock: comments --- src/ioc/db/dbLock.c | 14 ++++++++------ src/ioc/db/dbLockPvt.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 98da7cec3..aed9cc44e 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -687,9 +687,9 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) epicsSpinUnlock(lr->spin); } - /* there is at least 1 ref for each lockRecord, - * and at least one for the locker's locked list - * (perhaps another for its refs cache + /* there are at minimum, 1 ref for each lockRecord, + * and one for the locker's locked list + * (and perhaps another for its refs cache) */ assert(epicsAtomicGetIntT(&B->refcount)>=Nb+(locker?1:0)); @@ -698,7 +698,7 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) epicsAtomicAddIntT(&B->refcount, -Nb+1); /* drop all but one ref, see below */ if(locker) { - /* at least two ref, possibly three remain. + /* at least two refs, possibly three, remain. * # One ref from above * # locker->locked list, which is released now. * # locker->refs array, assuming it is directly referenced, @@ -717,7 +717,7 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) epicsMutexUnlock(B->lock); - dbLockDecRef(B); /* last ref from above */ + dbLockDecRef(B); /* last ref we hold */ assert(A==psecond->lset->plockSet); } @@ -828,6 +828,8 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) DBLINK *plink = CONTAINER(plink2, DBLINK, value); lockRecord *lr = plink->value.pv_link.precord->lset; + /* plink->type==DB_LINK is implied. Only DB_LINKs are tracked from BKLNK */ + if(lr->precord==pfirst) { goto nosplit; } @@ -839,7 +841,7 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) lr->compflag = 1; } } - /* All links from psecond were traversed without finding + /* All links involving psecond were traversed without finding * pfirst. So we must create a new lockset. * newLS contains the nodes which will * make up this new lockset. diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index 044be7eff..fd13929c2 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -46,7 +46,7 @@ struct lockRecord; * plockSet is guarded by spin. */ typedef struct lockRecord { - ELLNODE node; + ELLNODE node; /* in lockSet::lockRecordList */ /* The association between lockRecord and lockSet * can only be changed while the lockSet is held, * and the lockRecord's spinlock is held. From fa4678798c76c3eb488fb312ab0d41963d24f642 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 069/204] iocInit: remove no-op The work which was done here is moved to dbCloseLinks() --- src/ioc/misc/iocInit.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 6237a8cfe..3bede8547 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -660,19 +660,7 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { - int j; - struct rset *prset = pdbRecordType->prset; - - if (!prset) return; /* unlikely */ - epicsMutexDestroy(precord->mlok); - - for (j = 0; j < pdbRecordType->no_links; j++) { - dbFldDes *pdbFldDes = - pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; - DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - - } } int iocShutdown(void) From 765fb7c63ec0c3d02951d4adc1366652b9d4da51 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 070/204] dbLock: remove some unnecessary code no need to hold spinlock for lockRecordList the lockRecordList is protected by the lockSet::lock --- src/ioc/db/dbLock.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index aed9cc44e..041e8f98b 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -678,8 +678,9 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) { lockRecord *lr = CONTAINER(cur, lockRecord, node); assert(lr->plockSet==B); - epicsSpinLock(lr->spin); ellAdd(&A->lockRecordList, cur); + + epicsSpinLock(lr->spin); lr->plockSet = A; #ifndef LOCKSET_NOCNT epicsAtomicIncrSizeT(&recomputeCnt); @@ -801,9 +802,7 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) ptarget = plink->value.pv_link.pvt; lr = ptarget->precord->lset; - - if(!lr) - continue; /* not DB_LINK */ + assert(lr); if(lr->precord==pfirst) { /* so pfirst is still reachable from psecond, @@ -877,18 +876,18 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) lr->compflag = 0; /* reset for next time */ assert(lr->plockSet == ls); - - /* lockset is "live" at this point - * as other threads may find it. - */ - epicsSpinLock(lr->spin); ellDelete(&ls->lockRecordList, &lr->node); ellAdd(&splitset->lockRecordList, &lr->node); + + epicsSpinLock(lr->spin); lr->plockSet = splitset; #ifndef LOCKSET_NOCNT epicsAtomicIncrSizeT(&recomputeCnt); #endif epicsSpinUnlock(lr->spin); + /* new lockSet is "live" at this point + * as other threads may find it. + */ } /* refcount of ls can't go to zero as the locker From 7a5d4cf6cc9e917b2ccda04ac10f006555c3771e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 071/204] dbLock: minor --- src/ioc/db/dbLock.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 041e8f98b..8c36289df 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -118,8 +118,8 @@ unsigned long dbLockCountSets(void) void dbLockIncRef(lockSet* ls) { int cnt = epicsAtomicIncrIntT(&ls->refcount); - if(cnt<=0) { - errlogPrintf("dbLockIncRef(%p) on dead lockSet\n", ls); + if(cnt<=1) { + errlogPrintf("dbLockIncRef(%p) on dead lockSet. refs: %d\n", ls, cnt); cantProceed(NULL); } } @@ -185,7 +185,7 @@ void dbScanLock(dbCommon *precord) lockSet *ls; ls = dbLockGetRef(lr); - assert(ls->refcount>0); + assert(epicsAtomicGetIntT(&ls->refcount)>0); retry: epicsMutexMustLock(ls->lock); @@ -353,10 +353,8 @@ dbLocker *dbLockerAlloc(dbCommon **precs, size_t Nextra = nrecs>DBLOCKER_NALLOC ? nrecs-DBLOCKER_NALLOC : 0; dbLocker *locker = calloc(1, sizeof(*locker)+Nextra*sizeof(lockRecordRef)); - if(!locker) - return NULL; - - dbLockerPrepare(locker, precs, nrecs); + if(locker) + dbLockerPrepare(locker, precs, nrecs); return locker; } @@ -507,8 +505,6 @@ static int createLockRecord(void* junk, DBENTRY* pdbentry) /* TODO: one allocation for all records? */ lrec = callocMustSucceed(1, sizeof(*lrec), "lockRecord"); - if(!lrec) - cantProceed("no memory for lockRecord"); lrec->spin = epicsSpinCreate(); if(!lrec->spin) cantProceed("no memory for spinlock in lockRecord"); @@ -762,7 +758,7 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) /* at least 1 ref for each lockRecord, * and one for the locker */ - assert(ls->refcount>=ellCount(&ls->lockRecordList)+1); + assert(epicsAtomicGetIntT(&ls->refcount)>=ellCount(&ls->lockRecordList)+1); ellInit(&toInspect); ellInit(&newLS); From ff4c88ed0562c5cdb72fb5332e43172676e67bee Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 14:18:11 -0400 Subject: [PATCH 072/204] dbLock: comments --- src/ioc/db/dbLock.c | 28 ++++++++++++++++------------ src/ioc/db/dbLockPvt.h | 10 +++++++--- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 8c36289df..df2924a70 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -64,7 +64,10 @@ static void dbLockOnce(void* ignore) } /* global ID number assigned to each lockSet on creation. - * Will never exceed the number of records +1 + * When the free-list is in use will never exceed + * the number of records +1. + * Without the free-list it can roll over, potentially + * leading to duplicate IDs. */ static size_t next_id = 1; @@ -135,13 +138,16 @@ void dbLockDecRef(lockSet *ls) if(cnt) return; - /* not necessary as no one else holds a reference, + /* lockSet is unused and will be free'd */ + + /* not necessary as no one else (should) hold a reference, * but lock anyway to quiet valgrind */ epicsMutexMustLock(ls->lock); if(ellCount(&ls->lockRecordList)!=0) { - errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n", ls, ellCount(&ls->lockRecordList)); + errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n", + ls, ellCount(&ls->lockRecordList)); cantProceed(NULL); } @@ -153,7 +159,7 @@ void dbLockDecRef(lockSet *ls) ellAdd(&lockSetsFree, &ls->node); #else epicsMutexDestroy(ls->lock); - memset(ls, 0, sizeof(*ls)); + memset(ls, 0, sizeof(*ls)); /* paranoia */ free(ls); #endif epicsMutexUnlock(lockSetsGuard); @@ -197,7 +203,7 @@ retry: */ lockSet *ls2 = lr->plockSet; int newcnt = epicsAtomicIncrIntT(&ls2->refcount); - assert(newcnt>=2); /* lockRecord and us */ + assert(newcnt>=2); /* at least lockRecord and us */ epicsSpinUnlock(lr->spin); epicsMutexUnlock(ls->lock); @@ -215,10 +221,7 @@ retry: */ cnt = epicsAtomicDecrIntT(&ls->refcount); assert(cnt>0); - /* Caller does *not* hold a reference to ls. - * However, the lockRecord does, but can't - * be changed while we hold the lockSet. - */ + #ifdef LOCKSET_DEBUG if(ls->owner) { assert(ls->owner==epicsThreadGetIdSelf()); @@ -230,7 +233,6 @@ retry: ls->ownercount = 1; } #endif - /* no references kept */ } void dbScanUnlock(dbCommon *precord) @@ -287,7 +289,7 @@ int dbLockUpdateRefs(dbLocker *locker, int update) for(i=0; irefs[i]; lockSet *oldref = NULL; - if(!ref->plr) { + if(!ref->plr) { /* this lockRecord slot not used */ assert(!ref->plockSet); continue; } @@ -297,7 +299,7 @@ int dbLockUpdateRefs(dbLocker *locker, int update) changed = 1; if(update) { /* exchange saved lockSet reference */ - oldref = ref->plockSet; + oldref = ref->plockSet; /* will be NULL on first iteration */ ref->plockSet = ref->plr->plockSet; dbLockIncRef(ref->plockSet); } @@ -364,6 +366,7 @@ void dbLockerFinalize(dbLocker *locker) size_t i; assert(ellCount(&locker->locked)==0); + /* release references taken in dbLockUpdateRefs() */ for(i=0; imaxrefs; i++) { if(locker->refs[i].plockSet) dbLockDecRef(locker->refs[i].plockSet); @@ -396,6 +399,7 @@ retry: /* skip duplicates (same lockSet * referenced by more than one lockRecord). + * Sorting will group these together. */ if(!ref->plr || (plock && plock==ref->plockSet)) continue; diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index fd13929c2..86d186619 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -53,10 +53,13 @@ typedef struct lockRecord { * It may be read which either lock is held. */ lockSet *plockSet; + /* the association between lockRecord and dbCommon never changes */ dbCommon *precord; epicsSpinId spin; - /* temp used during lockset split */ + /* temp used during lockset split. + * lockSet must be locked for access + */ ELLNODE compnode; unsigned int compflag; } lockRecord; @@ -81,11 +84,12 @@ struct dbLocker { lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */ }; -lockSet* dbLockGetRef(lockRecord *lr); +lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */ void dbLockIncRef(lockSet* ls); void dbLockDecRef(lockSet *ls); -/* Optimization used by for dbLocker on the stack. +/* Calling dbLockerPrepare directly is an internal + * optimization used when dbLocker on the stack. * nrecs must be <=DBLOCKER_NALLOC. */ void dbLockerPrepare(struct dbLocker *locker, From 6ac1e050bdbbd68e920f9c84aed2d6d5879017b5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 24 Mar 2015 18:44:28 -0400 Subject: [PATCH 073/204] dbCa: missing inc. ref. when scan requests are queued Missing caLinkInc() in scanComplete() drops ref. --- src/ioc/db/dbCa.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 984b59f13..1525bf5ab 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -649,7 +649,8 @@ static void scanComplete(void *raw, dbCommon *prec) /* another scan is queued */ if(scanOnceCallback(prec, scanComplete, raw)) { errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); - } + } else + caLinkInc(pca); } epicsMutexUnlock(pca->lock); caLinkDec(pca); @@ -658,11 +659,10 @@ static void scanComplete(void *raw, dbCommon *prec) /* must be called with pca->lock held */ static void scanLinkOnce(dbCommon *prec, caLink *pca) { if(pca->scanningOnce==0) { - caLinkInc(pca); if(scanOnceCallback(prec, scanComplete, pca)) { - caLinkDec(pca); errlogPrintf("dbCa.c failed to queue scanOnce\n"); - } + } else + caLinkInc(pca); } if(pca->scanningOnce<5) pca->scanningOnce++; From f72ebb9fb92d8c76c09be62be65fe3919dbc0aca Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 2 Apr 2015 18:58:15 -0400 Subject: [PATCH 074/204] dbmf: add flag to disable freelist --- src/libCom/dbmf/dbmf.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/libCom/dbmf/dbmf.c b/src/libCom/dbmf/dbmf.c index 4b47d8c02..c0ad12216 100644 --- a/src/libCom/dbmf/dbmf.c +++ b/src/libCom/dbmf/dbmf.c @@ -23,6 +23,10 @@ #include "epicsMutex.h" #include "ellLib.h" #include "dbmf.h" +/* +#define DBMF_FREELIST_DEBUG 1 +*/ +#ifndef DBMF_FREELIST_DEBUG /*Default values for dblfInit */ #define DBMF_SIZE 64 @@ -236,3 +240,24 @@ void epicsShareAPI dbmfFreeChunks(void) pdbmfPvt->nFree = 0; pdbmfPvt->freeList = NULL; epicsMutexUnlock(pdbmfPvt->lock); } + +#else /* DBMF_FREELIST_DEBUG */ + +int epicsShareAPI dbmfInit(size_t size, int chunkItems) +{ return 0; } + +void* epicsShareAPI dbmfMalloc(size_t size) +{ return malloc(size); } + +char * epicsShareAPI dbmfStrdup(unsigned char *str) +{ return strdup((char*)str); } + +void epicsShareAPI dbmfFree(void* mem) +{ free(mem); } + +int epicsShareAPI dbmfShow(int level) +{ return 0; } + +void epicsShareAPI dbmfFreeChunks(void) {} + +#endif /* DBMF_FREELIST_DEBUG */ From e852de64ff967071027177c04a7e3a413cbf85a6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 2 Apr 2015 18:58:15 -0400 Subject: [PATCH 075/204] dbStatic: fix leak in parser "% cdef" not free'd --- src/ioc/dbStatic/dbLexRoutines.c | 2 +- src/ioc/dbStatic/dbYacc.y | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index 01b7dafdb..e224aa81a 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -259,7 +259,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, if (yyAbort) while (ellCount(&tempList)) - popFirstTemp(); + popFirstTemp(); // Memory leak on parser failure dbFreePath(pdbbase); if(!status) { /*add RTYP and VERS as an attribute */ diff --git a/src/ioc/dbStatic/dbYacc.y b/src/ioc/dbStatic/dbYacc.y index 0f0c757fe..272f509c4 100644 --- a/src/ioc/dbStatic/dbYacc.y +++ b/src/ioc/dbStatic/dbYacc.y @@ -118,7 +118,7 @@ recordtype_field: tokenFIELD recordtype_field_head recordtype_field_body | tokenCDEFS { if(dbStaticDebug>2) printf("recordtype_cdef %s", $1); - dbRecordtypeCdef($1); + dbRecordtypeCdef($1); dbmfFree($1); } | include ; From ad6f55d92fe8c5fcef18290e7a4d635e23152108 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 8 Apr 2015 18:37:12 -0400 Subject: [PATCH 076/204] dbStatic: always clear parser tempList Warn if tempList is not empty when it should be --- src/ioc/dbStatic/dbLexRoutines.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index e224aa81a..c77f82313 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -200,6 +200,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, char *penv; char **macPairs; + if(ellCount(&tempList)) { + epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList)); + } + if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); pdbbase = *ppdbbase; if(path && strlen(path)>0) { @@ -257,9 +261,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, ellAdd(&inputFileList,&pinputFile->node); status = pvt_yy_parse(); - if (yyAbort) - while (ellCount(&tempList)) - popFirstTemp(); // Memory leak on parser failure + if (ellCount(&tempList) && !yyAbort) + epicsPrintf("dbReadCOM: Parser stack dirty w/o error. %d\n", ellCount(&tempList)); + while (ellCount(&tempList)) + popFirstTemp(); /* Memory leak on parser failure */ dbFreePath(pdbbase); if(!status) { /*add RTYP and VERS as an attribute */ From 0c16937a5a80ebbc9cb99858df40ab05273d60b3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 9 Apr 2015 14:36:14 -0400 Subject: [PATCH 077/204] ioc/db/test: extend dbCaLinkTest to check CAC get operation --- src/ioc/db/test/Makefile | 3 +- src/ioc/db/test/dbCACTest.cpp | 82 ++++++++++++++++++++++++++++++++++ src/ioc/db/test/dbCaLinkTest.c | 46 ++++++++++++++++++- 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/ioc/db/test/dbCACTest.cpp diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 2a7c15272..71140b316 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -90,10 +90,11 @@ TESTFILES += ../dbCaStatsTest.db TESTPROD_HOST += dbCaLinkTest dbCaLinkTest_SRCS += dbCaLinkTest.c +dbCaLinkTest_SRCS += dbCACTest.cpp dbCaLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbCaLinkTest.c TESTS += dbCaLinkTest -TESTFILES += ../dbCaLinkTest1.db +TESTFILES += ../dbCaLinkTest1.db ../dbCaLinkTest2.db ../dbCaLinkTest3.db scanIoTest_DBD += menuScan.dbd TESTPROD_HOST += scanIoTest diff --git a/src/ioc/db/test/dbCACTest.cpp b/src/ioc/db/test/dbCACTest.cpp new file mode 100644 index 000000000..538d343bf --- /dev/null +++ b/src/ioc/db/test/dbCACTest.cpp @@ -0,0 +1,82 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ +/* + * Part of dbCaLinkTest, compiled seperately to avoid + * dbAccess.h vs. db_access.h conflicts + */ + +#include + +#include +#include + +#include + +#include "epicsUnitTest.h" + +#include "cadef.h" + +#define testECA(OP) if((OP)!=ECA_NORMAL) {testAbort("%s", #OP);} else {testPass("%s", #OP);} + +void putgetarray(chid chanid, double first, size_t count) +{ + testDiag("putgetarray(%f,%u)", first, (unsigned)count); + + std::vector buf(count); + for(size_t i=0; i buf2(count); + + testECA(ca_array_get(DBR_DOUBLE, count, chanid, &buf2[0])); + + testECA(ca_pend_io(1.0)); + + for(size_t i=0; ibptr; + buftarg1= ptarg1->bptr; + buftarg2= ptarg2->bptr; + + dbCaLinkTest_testCAC(); + + testIocShutdownOk(); + + testdbCleanup(); + + /* records don't cleanup after themselves + * so do here to silence valgrind + */ + free(bufsrc); + free(buftarg1); + free(buftarg2); +} + MAIN(dbCaLinkTest) { - testPlan(62); + testPlan(85); testNativeLink(); testStringLink(); testCP(); @@ -549,5 +592,6 @@ MAIN(dbCaLinkTest) testArrayLink(1,10); testArrayLink(10,10); testreTargetTypeChange(); + testCAC(); return testDone(); } From 7a8266c419e12c55ce73b791148b4b242a54daab Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 9 Apr 2015 14:55:56 -0400 Subject: [PATCH 078/204] ioc/db/test: ensure dbCaLinkTest tests dbContextReadNotifyCacheAllocator Read the same size twice to ensure that a cached buffer is reused --- src/ioc/db/test/dbCACTest.cpp | 2 ++ src/ioc/db/test/dbCaLinkTest.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbCACTest.cpp b/src/ioc/db/test/dbCACTest.cpp index 538d343bf..4343ce5c0 100644 --- a/src/ioc/db/test/dbCACTest.cpp +++ b/src/ioc/db/test/dbCACTest.cpp @@ -73,6 +73,8 @@ void dbCaLinkTest_testCAC(void) testECA(ca_pend_io(1.0)); putgetarray(chanid, 1.0, 1); putgetarray(chanid, 2.0, 2); + // repeat to ensure a cache hit in dbContextReadNotifyCacheAllocator + putgetarray(chanid, 2.0, 2); putgetarray(chanid, 5.0, 5); testECA(ca_clear_channel(chanid)); diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index 487e26cbb..1f20028f1 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -583,7 +583,7 @@ static void testCAC(void) MAIN(dbCaLinkTest) { - testPlan(85); + testPlan(91); testNativeLink(); testStringLink(); testCP(); From ee8bf98f942d2fb91ce92ce71eb3ac1cb86b40b7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 15 Apr 2015 18:01:26 -0500 Subject: [PATCH 079/204] Generate documentation from Perl modules Add rules to generate HTML from POD in Perl modules and scripts. Generate docs from several EPICS/ Perl modules. --- configure/RULES.Db | 9 +++- src/tools/EPICS/Getopts.pm | 70 +++++++++++++++++++++++++++++++ src/tools/EPICS/Path.pm | 51 +++++++++++++--------- src/tools/EPICS/Readfile.pm | 84 ++++++++++++++++++++++++++++++++++++- src/tools/Makefile | 4 ++ src/tools/podToHtml.pl | 69 +++++++++++++++++++++++++++--- 6 files changed, 259 insertions(+), 28 deletions(-) diff --git a/configure/RULES.Db b/configure/RULES.Db index 63b06dfed..7ea4defd7 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -443,7 +443,14 @@ $(COMMON_DIR)/%.html: %.pm $(TOOLS)/podToHtml.pl $(COMMON_DIR)/%.html: ../%.pm $(TOOLS)/podToHtml.pl @$(RM) $(notdir $@) - $(PERL) $(TOOLS)/podToHtml.pl -o $(notdir $@) $< + $(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $< + @$(MKDIR) -p $(dir $@) + @$(MV) $(notdir $@) $@ + +$(COMMON_DIR)/%.html: ../%.pl $(TOOLS)/podToHtml.pl + @$(RM) $(notdir $@) + $(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $< + @$(MKDIR) -p $(dir $@) @$(MV) $(notdir $@) $@ .PRECIOUS: $(COMMON_DIR)/%.html %.html diff --git a/src/tools/EPICS/Getopts.pm b/src/tools/EPICS/Getopts.pm index c08db62aa..f448985d0 100644 --- a/src/tools/EPICS/Getopts.pm +++ b/src/tools/EPICS/Getopts.pm @@ -2,6 +2,76 @@ package EPICS::Getopts; require 5.000; require Exporter; +=head1 NAME + +EPICS::Getopts - Process single-character command-line options + +=head1 SYNOPSIS + + use EPICS::Getopts; + + getopts('vo:I@') or die "Bad option\n"; + # -v is a counted flag; -o takes an argument and + # sets $opt_o to its value; -I may be used more than + # once, the values are pushed onto @opt_I + +=head1 DESCRIPTION + +This version of getopts has been modified from the Perl original to add +functionality that was needed by EPICS Perl applications. Some functionality of +the original has also been removed. The main changes were: + +=over 2 + +=item * + +Arguments without any modifiers are now counted, rather than storing a binary +value in the C<$opt_x> variable. This means that multiple copies of the same +option can be detected by the program. + +=item * + +A new B> modifier is supported which collects the option arguments in an +array C<@opt_x>. Multiple copies of this option can be given, which pushes each +argument value onto the end of the array. + +=back + + +=head1 FUNCTIONS + +=over 4 + +=item B> + +The getopts() function processes single-character options. It takes one +argument, a string containing all the options that should be recognized. For +option letters in the string that are followed by a colon B> it sets +C<$opt_x> (where x is the option letter) to the value of that argument. For +option letters followed by an at sign B> it pushes each subsequent argument +onto the array C<@opt_x> (where x is the option letter). For option letters +without any qualifier it adds 1 to the value of C<$opt_x>. Options which take an +argument don't care whether there is a space between the option and the +argument. + +If unspecified switches are found on the command-line, the user will be warned +that an unknown option was given. The getopts() function returns true unless an +invalid option was found. + +Note that, if your code is running under the recommended C +pragma, you will need to declare the necesary package variables with "our" +before the call to getopts: + + our($opt_v, $opt_o, @opt_I); + +To allow programs to process arguments that look like options but aren't, the +function will stop processing options when it sees the argument B>. The +B> will be removed from @ARGV. + +=back + +=cut + @ISA = qw(Exporter); @EXPORT = qw(getopts); diff --git a/src/tools/EPICS/Path.pm b/src/tools/EPICS/Path.pm index 83a91efb3..4ddbbc59e 100644 --- a/src/tools/EPICS/Path.pm +++ b/src/tools/EPICS/Path.pm @@ -7,17 +7,17 @@ # $Revision-Id$ -use Carp; -use Cwd qw(getcwd abs_path); -use File::Spec; +package EPICS::Path; +require 5.000; +require Exporter; -=head1 EPICS::Path +=head1 NAME EPICS::Path - Path-handling utilities for EPICS tools =head1 SYNOPSIS - use lib '@EPICS_BASE@/lib/perl'; + use lib '/path/to/base/lib/perl'; use EPICS::Path; my $dir = UnixPath('C:\Program Files\EPICS'); @@ -26,17 +26,29 @@ EPICS::Path - Path-handling utilities for EPICS tools =head1 DESCRIPTION -C provides functions for processing pathnames that are commonly -needed by EPICS tools. Windows is not the only culprit, some older automount -daemons insert strange prefixes into absolute directory paths that we have to -remove before storing the result for use later. +This module provides functions for processing pathnames that are commonly needed +by EPICS tools. Windows is not the only culprit, some older automount daemons +insert strange prefixes into absolute directory paths that we have to remove +before storing the result for use later. +Note that these functions are only designed to work on operating systems that +recognize a forward slash C as a directory separator; this does include +Windows, but not pre-OS-X versions of MacOS which used a colon C<:> instead. =head1 FUNCTIONS =over 4 -=item UnixPath( I ) +=cut + +use Carp; +use Cwd qw(getcwd abs_path); +use File::Spec; + +@ISA = qw(Exporter); +@EXPORT = qw(UnixPath LocalPath AbsPath); + +=item B> C should be used on any pathnames provided by external tools to convert them into a form that Perl understands. @@ -57,7 +69,7 @@ sub UnixPath { return $newpath; } -=item LocalPath( I ) +=item B> C should be used when generating pathnames for external tools or to put into a file. It converts paths from the Unix form that Perl understands to @@ -65,7 +77,7 @@ any necessary external representation, and also removes automounter prefixes to put the path into its canonical form. Before Leopard, the Mac OS X automounter inserted a verbose prefix, and in case -anyone is still using SunOS it adds its own prefix as well. +anyone is still using SunOS (pre-Solaris) it added its own prefix as well. =cut @@ -81,19 +93,19 @@ sub LocalPath { return $newpath; } -=item AbsPath( I ) +=item B> -=item AbsPath( I, I ) +=item B> -The C function in Perl's C module doesn't like non-existent +The C function in Perl's F module doesn't like non-existent path components other than in the final position, but EPICS tools needs to be able to handle them in paths like F<$(TOP)/lib/$(T_A)> before the F<$(TOP)/lib> directory has been created. -C takes a path I and optionally an absolute path to a directory -that first is relative to; if the second argument is not provided the current -working directory is used. The result returned has been filtered through -C to remove any automounter prefixes. +C takes a path C<$path> and optionally an absolute path to a directory +that the first is relative to; if the second argument is not provided the +current working directory is used instead. The result returned has also been +filtered through C to remove any automounter prefixes. =cut @@ -139,5 +151,4 @@ This software is distributed under the terms of the EPICS Open License. =cut - 1; diff --git a/src/tools/EPICS/Readfile.pm b/src/tools/EPICS/Readfile.pm index 43ee493f4..325efb17b 100644 --- a/src/tools/EPICS/Readfile.pm +++ b/src/tools/EPICS/Readfile.pm @@ -1,5 +1,5 @@ #************************************************************************* -# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. @@ -11,6 +11,28 @@ package EPICS::Readfile; require 5.000; require Exporter; +=head1 NAME + +EPICS::Readfile - DBD and DB-file input for EPICS tools + +=head1 SYNOPSIS + + use lib '/path/to/base/lib/perl'; + use EPICS::macLib; + use EPICS::Readfile; + + my $macros = EPICS::macLib->new('a=1', 'b=2'); + my @path = qw(../dbd /opt/epics/base/dbd); + my $contents = Readfile('input.dbd', $macros, \@path); + printf "Read in %d files", scalar @inputfiles; + +=head1 DESCRIPTION + +This module provides a function for reading DBD and DB files that is commonly +needed by EPICS tools. + +=cut + use EPICS::macLib; @ISA = qw(Exporter); @@ -69,6 +91,57 @@ sub unquote { return $s; } + +=head1 FUNCTIONS + +=over 4 + +=item B> + +This function reads an EPICS DBD or DB file into a string, substitutes any +macros present, then parses the contents for any C, C and +C commands found therein and recursively executes those commands. The +return value from the function is a string comprising the fully expanded +contents of those files. Before executing them any commands will be replaced +with specially formatted comments that allow the original command to be +recovered during later parsing. + +I takes as arguments the input filename, an optional handle to a set +of macro values from L, and a reference to an array +containing the current search path. + +If macro expansion is not required, the second argument may be any boolean False +value such as C<0> or C<()>. See L for more details about +this argument. + +The path argument is a reference to an array of directory paths to be searched, +in order. These paths may be used to locate the original input file and any +include files that it references. The path array will be modified by any +C or C commands found while parsing the input files. + +While processing input filenames (either the original argument or an include +filename) if the filename does not contain any forward-slash C characters the +path will be searched and the first file matching that name will be used. If the +filename contains one or more forward-slash characters it must be either an +absolute path or one that is relative to the current working directory; the +search path is not used in this case. + +=back + +=head1 VARIABLES + +=over 4 + +=item B> + +As new files are processed their names are added to this array which may be +examimed after the I function returns, for example to calculate the +complete set of dependencies for the input file. + +=back + +=cut + sub Readfile { my ($file, $macros, $Rpath) = @_; print "Readfile($file)\n" if $debug; @@ -98,4 +171,13 @@ sub Readfile { return join "\n", @output; } +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut + 1; diff --git a/src/tools/Makefile b/src/tools/Makefile index 54115bc79..00314e824 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -54,6 +54,10 @@ PERL_SCRIPTS += podToHtml.pl PERL_SCRIPTS += registerRecordDeviceDriver.pl HTMLS = style.css +HTMLS += EPICS/Getopts.html +HTMLS += EPICS/Path.html +HTMLS += EPICS/Readfile.html +HTMLS += podToHtml.html # Build Package Config Files diff --git a/src/tools/podToHtml.pl b/src/tools/podToHtml.pl index 2baf9e80f..d5817c797 100644 --- a/src/tools/podToHtml.pl +++ b/src/tools/podToHtml.pl @@ -14,24 +14,72 @@ use warnings; use Getopt::Std; use Pod::Simple::HTML; -our ($opt_o); +=head1 NAME + +podToHtml.pl - convert EPICS .pod files to .html + +=head1 SYNOPSIS + +B [B<-s>] [B<-o> file.html] file.pod + +=head1 DESCRIPTION + +Converts files from Perl's POD format into HTML format. + +The generated HTML output file refers to a CSS style sheet F which +can be located in a parent directory of the final installation directory. The +relative path to that file (i.e. the number of parent directories to traverse) +is calculated based on the number of components in the path to the input file. + +=head1 OPTIONS + +I understands the following options: + +=over 4 + +=item B<-s> + +Indicates that the first component of the input file path is not part of the +final installation path, thus should be removed before calculating the relative +path to the style-sheet file. + +=item B<-o> file.html + +Name of the HTML output file to be created. + +=back + +If no output filename is set, the file created will be named after the input +file, removing any directory components in the path and replacing any file +extension with .html. + +=cut + +our $opt_o; +our $opt_s = 0; $Getopt::Std::OUTPUT_HELP_VERSION = 1; -&HELP_MESSAGE if !getopts('o:') || @ARGV != 1; +&HELP_MESSAGE if !getopts('o:s') || @ARGV != 1; my $infile = shift @ARGV; +my @inpath = split /\//, $infile; +my $file = pop @inpath; + if (!$opt_o) { - ($opt_o = $infile) =~ s/\. \w+ $/.html/x; - $opt_o =~ s/^.*\///; + ($opt_o = $file) =~ s/\. \w+ $/.html/x; } +# Calculate path to style.css file +shift @inpath if $opt_s; # Remove leading .. +my $root = '../' x scalar @inpath; + open my $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; my $podHtml = Pod::Simple::HTML->new(); -$podHtml->html_css('style.css'); +$podHtml->html_css($root . 'style.css'); $podHtml->perldoc_url_prefix(''); $podHtml->perldoc_url_postfix('.html'); $podHtml->set_source($infile); @@ -42,6 +90,15 @@ print $out $html; close $out; sub HELP_MESSAGE { - print STDERR "Usage: podToHtml.pl [-o file.html] file.pod\n"; + print STDERR "Usage: podToHtml.pl [-s] [-o file.html] file.pod\n"; exit 2; } + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2010 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut From e4f336de9401e617fad1913408808ea698bf9465 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 21 Apr 2015 15:09:24 -0500 Subject: [PATCH 080/204] Perl style fixes, document more Perl programs --- src/libCom/env/bldEnvData.pl | 4 +- src/libCom/misc/makeEpicsVersion.pl | 4 +- src/template/base/makeBaseApp.pl | 26 ++++----- src/template/ext/makeBaseExt.pl | 22 ++++---- src/tools/DBD/Output.pm | 14 ++--- src/tools/DBD/Parser.pm | 2 +- src/tools/EPICS/Readfile.pm | 14 ++--- src/tools/EPICS/Release.pm | 12 ++--- src/tools/Makefile | 3 ++ src/tools/convertRelease.pl | 30 ++++++----- src/tools/dbdExpand.pl | 4 +- src/tools/dbdReport.pl | 2 +- src/tools/dbdToHtml.pl | 2 +- src/tools/dbdToMenuH.pl | 2 +- src/tools/dbdToRecordtypeH.pl | 6 +-- src/tools/expandVars.pl | 4 +- src/tools/fullPathName.pl | 56 +++++++++++++++++--- src/tools/munch.pl | 70 +++++++++++++++++++++---- src/tools/podRemove.pl | 60 ++++++++++++++++++--- src/tools/podToHtml.pl | 32 ++++++----- src/tools/registerRecordDeviceDriver.pl | 2 +- 21 files changed, 259 insertions(+), 112 deletions(-) diff --git a/src/libCom/env/bldEnvData.pl b/src/libCom/env/bldEnvData.pl index 993c36cee..08d65467e 100644 --- a/src/libCom/env/bldEnvData.pl +++ b/src/libCom/env/bldEnvData.pl @@ -32,8 +32,8 @@ our $opt_o = 'envData.c'; $Getopt::Std::OUTPUT_HELP_VERSION = 1; $Text::Wrap::columns = 75; -&HELP_MESSAGE unless getopts('ho:q') && @ARGV == 1; -&HELP_MESSAGE if $opt_h; +HELP_MESSAGE() unless getopts('ho:q') && @ARGV == 1; +HELP_MESSAGE() if $opt_h; my $config = AbsPath(shift); my $env_defs = AbsPath('../env/envDefs.h'); diff --git a/src/libCom/misc/makeEpicsVersion.pl b/src/libCom/misc/makeEpicsVersion.pl index 2a720e86d..08f5aa3c0 100644 --- a/src/libCom/misc/makeEpicsVersion.pl +++ b/src/libCom/misc/makeEpicsVersion.pl @@ -20,8 +20,8 @@ our $opt_o = 'epicsVersion.h'; $Getopt::Std::OUTPUT_HELP_VERSION = 1; -&HELP_MESSAGE unless getopts('ho:qv:') && @ARGV == 1; -&HELP_MESSAGE if $opt_h; +HELP_MESSAGE() unless getopts('ho:qv:') && @ARGV == 1; +HELP_MESSAGE() if $opt_h; my ($infile) = @ARGV; diff --git a/src/template/base/makeBaseApp.pl b/src/template/base/makeBaseApp.pl index cf65d290f..65091a0fb 100644 --- a/src/template/base/makeBaseApp.pl +++ b/src/template/base/makeBaseApp.pl @@ -20,10 +20,10 @@ $app_top = cwd(); $bad_ident_chars = '[^0-9A-Za-z_]'; -&readReleaseFiles("configure/RELEASE", \%release, \@apps); -&expandRelease(\%release); -&get_commandline_opts; # Check command-line options -&GetUser; # Ensure we know who's in charge +readReleaseFiles("configure/RELEASE", \%release, \@apps); +expandRelease(\%release); +get_commandline_opts(); # Check command-line options +GetUser(); # Ensure we know who's in charge # # Declare two default callback routines for file copy plus two @@ -86,7 +86,7 @@ sub ReplaceFilename { # (filename) $file =~ s|_APPTYPE_|$apptype|; my $qmtop = quotemeta($top); $file =~ s|$qmtop/||; # Change to the target location - $file = &ReplaceFilenameHook($file); # Call the apptype's hook + $file = ReplaceFilenameHook($file); # Call the apptype's hook return $file; } @@ -104,7 +104,7 @@ sub ReplaceLine { # (line) $line =~ s/_CSAFEAPPNAME_/$csafeappname/g; $line =~ s/_APPTYPE_/$apptype/go; $line =~ s/_ARCH_/$arch/go if ($opt_i); - $line = &ReplaceLineHook($line); # Call the apptype's hook + $line = ReplaceLineHook($line); # Call the apptype's hook return $line; } @@ -202,7 +202,7 @@ sub get_commandline_opts { #no args # Print application type list? if ($opt_l) { - &ListAppTypes; + ListAppTypes(); exit 0; # finished for -l command } @@ -325,7 +325,7 @@ sub ListAppTypes { # no args # sub CopyFile { # (source) $source = $_[0]; - $target = &ReplaceFilename($source); + $target = ReplaceFilename($source); if ($target and !-e $target) { open(INP, "<$source") and open(OUT, ">$target") @@ -333,7 +333,7 @@ sub CopyFile { # (source) print "Copying file $source -> $target\n" if $opt_d; while () { - print OUT &ReplaceLine($_); + print OUT ReplaceLine($_); } close INP; close OUT; } @@ -345,11 +345,11 @@ sub CopyFile { # (source) sub FCopyTree { chdir $app_top; # Sigh if (-d "$File::Find::name" - and ($dir = &ReplaceFilename($File::Find::name))) { + and ($dir = ReplaceFilename($File::Find::name))) { print "Creating directory $dir\n" if $opt_d; - &mkpath($dir) unless (-d "$dir"); + mkpath($dir) unless (-d "$dir"); } else { - &CopyFile($File::Find::name); + CopyFile($File::Find::name); } chdir $File::Find::dir; } @@ -363,7 +363,7 @@ sub Cleanup { # (return-code [ messsage-line1, line 2, ... ]) if (@message) { print join("\n", @message), "\n"; } else { - &Usage; + Usage(); } exit $rtncode; } diff --git a/src/template/ext/makeBaseExt.pl b/src/template/ext/makeBaseExt.pl index 307764823..c4185ad53 100644 --- a/src/template/ext/makeBaseExt.pl +++ b/src/template/ext/makeBaseExt.pl @@ -15,7 +15,7 @@ $eEXTTYPE = $ENV{EPICS_MBE_DEF_EXT_TYPE}; $eTOP = $ENV{EPICS_MBE_TEMPLATE_TOP}; $eBASE = $ENV{EPICS_MBE_BASE}; -&get_commandline_opts; # Read and check options +get_commandline_opts(); # Read and check options $extname = "@ARGV"; @@ -43,7 +43,7 @@ sub ReplaceFilename { # (filename) $file =~ s|_EXTTYPE_|$exttype|; # We don't want the Replace overrides $file =~ s|.*/$extdir/Replace.pl$||; - $file = &ReplaceFilenameHook($file); # Call the user-defineable hook + $file = ReplaceFilenameHook($file); # Call the user-defineable hook return $file; } @@ -58,7 +58,7 @@ sub ReplaceLine { # (line) $line =~ s/_EXTNAME_/$extname/o; $line =~ s/_EXTTYPE_/$exttype/o; $line =~ s/_TEMPLATE_TOP_/$top/o; - $line = &ReplaceLineHook($line); # Call the user-defineable hook + $line = ReplaceLineHook($line); # Call the user-defineable hook return $line; } @@ -73,7 +73,7 @@ if (-r "$top/$exttypename/Replace.pl") { opendir TOPDIR, "$top" or die "Can't open $top: $!"; foreach $f ( grep !/^\.\.?$|^[^\/]*(Ext)/, readdir TOPDIR ) { if (-f "$f") { - &CopyFile("$top/$f") unless (-e "$f"); + CopyFile("$top/$f") unless (-e "$f"); } else { $note = yes if ("$f" eq "src" && -e "$f"); find(\&FCopyTree, "$top/$f") unless (-e "$f"); @@ -154,7 +154,7 @@ sub get_commandline_opts { #no args # Print extension type list? if ($opt_l) { - &ListExtTypes; + ListExtTypes(); exit 0; # finished for -l command } @@ -175,7 +175,7 @@ sub get_commandline_opts { #no args # Valid $exttypename? unless (-r "$top/$exttypename") { print "Template for extension type '$exttype' is unreadable or does not exist.\n"; - &ListExtTypes; + ListExtTypes(); exit 1; } @@ -204,7 +204,7 @@ sub ListExtTypes { # no args # sub CopyFile { # (source) $source = $_[0]; - $target = &ReplaceFilename($source); + $target = ReplaceFilename($source); if ($target) { $target =~ s|$top/||; @@ -213,7 +213,7 @@ sub CopyFile { # (source) print "Copying file $source -> $target\n" if $Debug; while () { - print OUT &ReplaceLine($_); + print OUT ReplaceLine($_); } close INP; close OUT; } @@ -225,12 +225,12 @@ sub CopyFile { # (source) sub FCopyTree { chdir $cwd; # Sigh if (-d $File::Find::name - and ($dir = &ReplaceFilename($File::Find::name))) { + and ($dir = ReplaceFilename($File::Find::name))) { $dir =~ s|$top/||; print "Creating directory $dir\n" if $Debug; - &mkpath($dir); + mkpath($dir); } else { - &CopyFile($File::Find::name); + CopyFile($File::Find::name); } chdir $File::Find::dir; } diff --git a/src/tools/DBD/Output.pm b/src/tools/DBD/Output.pm index b82ff98c9..a338358b3 100644 --- a/src/tools/DBD/Output.pm +++ b/src/tools/DBD/Output.pm @@ -19,13 +19,13 @@ use DBD::Variable; sub OutputDBD { my ($out, $dbd) = @_; - &OutputMenus($out, $dbd->menus); - &OutputRecordtypes($out, $dbd->recordtypes); - &OutputDrivers($out, $dbd->drivers); - &OutputRegistrars($out, $dbd->registrars); - &OutputFunctions($out, $dbd->functions); - &OutputVariables($out, $dbd->variables); - &OutputBreaktables($out, $dbd->breaktables); + OutputMenus($out, $dbd->menus); + OutputRecordtypes($out, $dbd->recordtypes); + OutputDrivers($out, $dbd->drivers); + OutputRegistrars($out, $dbd->registrars); + OutputFunctions($out, $dbd->functions); + OutputVariables($out, $dbd->variables); + OutputBreaktables($out, $dbd->breaktables); } sub OutputMenus { diff --git a/src/tools/DBD/Parser.pm b/src/tools/DBD/Parser.pm index 901420685..3fbf2f99d 100644 --- a/src/tools/DBD/Parser.pm +++ b/src/tools/DBD/Parser.pm @@ -79,7 +79,7 @@ sub parseCommon { # Extract POD if (m/\G ( = [a-zA-Z] .* ) \n/oxgc) { - $obj->add_pod($1, &parsePod); + $obj->add_pod($1, parsePod()); } elsif (m/\G \# /oxgc) { if (m/\G \# ! BEGIN \{ ( [^}]* ) \} ! \# \# \n/oxgc) { diff --git a/src/tools/EPICS/Readfile.pm b/src/tools/EPICS/Readfile.pm index 325efb17b..1b6cab21d 100644 --- a/src/tools/EPICS/Readfile.pm +++ b/src/tools/EPICS/Readfile.pm @@ -145,25 +145,25 @@ complete set of dependencies for the input file. sub Readfile { my ($file, $macros, $Rpath) = @_; print "Readfile($file)\n" if $debug; - my $input = &expandMacros($macros, &slurp($file, $Rpath)); + my $input = expandMacros($macros, slurp($file, $Rpath)); my @input = split /\n/, $input; my @output; foreach (@input) { if (m/^ \s* include \s+ $string /ox) { - $arg = &unquote($1); + $arg = unquote($1); print " include $arg\n" if $debug; push @output, "##! include \"$arg\""; - push @output, &Readfile($arg, $macros, $Rpath); + push @output, Readfile($arg, $macros, $Rpath); } elsif (m/^ \s* addpath \s+ $string /ox) { - $arg = &unquote($1); + $arg = unquote($1); print " addpath $arg\n" if $debug; push @output, "##! addpath \"$arg\""; - push @{$Rpath}, &splitPath($arg); + push @{$Rpath}, splitPath($arg); } elsif (m/^ \s* path \s+ $string /ox) { - $arg = &unquote($1); + $arg = unquote($1); print " path $arg\n" if $debug; push @output, "##! path \"$arg\""; - @{$Rpath} = &splitPath($arg); + @{$Rpath} = splitPath($arg); } else { push @output, $_; } diff --git a/src/tools/EPICS/Release.pm b/src/tools/EPICS/Release.pm index 09381dfc8..577444dc4 100644 --- a/src/tools/EPICS/Release.pm +++ b/src/tools/EPICS/Release.pm @@ -19,22 +19,22 @@ sub readReleaseFiles { $Rmacros->{'EPICS_HOST_ARCH'} = $hostarch if $hostarch; return unless (-e $relfile); - &readRelease($relfile, $Rmacros, $Rapps); + readRelease($relfile, $Rmacros, $Rapps); if ($hostarch) { my $hrelfile = "$relfile.$hostarch"; - &readRelease($hrelfile, $Rmacros, $Rapps) if (-e $hrelfile); + readRelease($hrelfile, $Rmacros, $Rapps) if (-e $hrelfile); $hrelfile .= '.Common'; - &readRelease($hrelfile, $Rmacros, $Rapps) if (-e $hrelfile); + readRelease($hrelfile, $Rmacros, $Rapps) if (-e $hrelfile); } if ($arch) { my $crelfile = "$relfile.Common.$arch"; - &readRelease($crelfile, $Rmacros, $Rapps) if (-e $crelfile); + readRelease($crelfile, $Rmacros, $Rapps) if (-e $crelfile); if ($hostarch) { my $arelfile = "$relfile.$hostarch.$arch"; - &readRelease($arelfile, $Rmacros, $Rapps) if (-e $arelfile); + readRelease($arelfile, $Rmacros, $Rapps) if (-e $arelfile); } } } @@ -71,7 +71,7 @@ sub readRelease { my ($op, $path) = m/^ \s* (-? include) \s+ (.*)/x; $path = expandMacros($path, $Rmacros); if (-e $path) { - &readRelease($path, $Rmacros, $Rapps); + readRelease($path, $Rmacros, $Rapps); } elsif ($op eq "include") { carp "EPICS/Release.pm: Include file '$path' not found\n"; } diff --git a/src/tools/Makefile b/src/tools/Makefile index 4b7162b6e..26da8f550 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -58,7 +58,10 @@ HTMLS = style.css HTMLS += EPICS/Getopts.html HTMLS += EPICS/Path.html HTMLS += EPICS/Readfile.html +HTMLS += fullPathName.html HTMLS += podToHtml.html +HTMLS += podRemove.html +HTMLS += munch.html # Build Package Config Files diff --git a/src/tools/convertRelease.pl b/src/tools/convertRelease.pl index abf63c3ec..9a425c954 100644 --- a/src/tools/convertRelease.pl +++ b/src/tools/convertRelease.pl @@ -14,12 +14,15 @@ # use strict; +use warnings; + +use Cwd qw(cwd); +use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl", $Bin); -use Cwd qw(cwd); -use Getopt::Std; use EPICS::Path; use EPICS::Release; @@ -27,8 +30,7 @@ use vars qw($arch $top $iocroot $root); our ($opt_a, $opt_t, $opt_T); -$Getopt::Std::OUTPUT_HELP_VERSION = 1; -getopts('a:t:T:') or &HELP_MESSAGE; +getopts('a:t:T:') or HELP_MESSAGE(); my $cwd = UnixPath(cwd()); @@ -63,7 +65,7 @@ if ($opt_t) { } } -&HELP_MESSAGE unless @ARGV == 1; +HELP_MESSAGE() unless @ARGV == 1; my $outfile = $ARGV[0]; @@ -80,12 +82,12 @@ expandRelease(\%macros, \@apps); # This is a perl switch statement: for ($outfile) { - m/releaseTops/ and do { &releaseTops; last; }; - m/dllPath\.bat/ and do { &dllPath; last; }; - m/relPaths\.sh/ and do { &relPaths; last; }; - m/cdCommands/ and do { &cdCommands; last; }; - m/envPaths/ and do { &envPaths; last; }; - m/checkRelease/ and do { &checkRelease; last; }; + m/releaseTops/ and do { releaseTops(); last; }; + m/dllPath\.bat/ and do { dllPath(); last; }; + m/relPaths\.sh/ and do { relPaths(); last; }; + m/cdCommands/ and do { cdCommands(); last; }; + m/envPaths/ and do { envPaths(); last; }; + m/checkRelease/ and do { checkRelease(); last; }; die "Output file type \'$outfile\' not supported"; } @@ -158,7 +160,7 @@ sub cdCommands { my $startup = $cwd; $startup =~ s/^$root/$iocroot/o if ($opt_t); - $startup =~ s/([\\"])/\\\1/g; # escape back-slashes and double-quotes + $startup =~ s/([\\"])/\\$1/g; # escape back-slashes and double-quotes print OUT "startup = \"$startup\"\n"; @@ -171,7 +173,7 @@ sub cdCommands { foreach my $app (@includes) { my $iocpath = my $path = $macros{$app}; $iocpath =~ s/^$root/$iocroot/o if ($opt_t); - $iocpath =~ s/([\\"])/\\\1/g; # escape back-slashes and double-quotes + $iocpath =~ s/([\\"])/\\$1/g; # escape back-slashes and double-quotes my $app_lc = lc($app); print OUT "$app_lc = \"$iocpath\"\n" if (-d $path); @@ -203,7 +205,7 @@ sub envPaths { foreach my $app (@includes) { my $iocpath = my $path = $macros{$app}; $iocpath =~ s/^$root/$iocroot/o if ($opt_t); - $iocpath =~ s/([\\"])/\\\1/g; # escape back-slashes and double-quotes + $iocpath =~ s/([\\"])/\\$1/g; # escape back-slashes and double-quotes print OUT "epicsEnvSet(\"$app\",\"$iocpath\")\n" if (-d $path); } close OUT; diff --git a/src/tools/dbdExpand.pl b/src/tools/dbdExpand.pl index 32b38bf1e..96398e703 100755 --- a/src/tools/dbdExpand.pl +++ b/src/tools/dbdExpand.pl @@ -43,7 +43,7 @@ my $errors = 0; while (@ARGV) { my $file = shift @ARGV; eval { - &ParseDBD($dbd, &Readfile($file, $macros, \@opt_I)); + ParseDBD($dbd, Readfile($file, $macros, \@opt_I)); }; if ($@) { warn "dbdExpand.pl: $@"; @@ -72,7 +72,7 @@ if ($opt_o) { $out = STDOUT; } -&OutputDBD($out, $dbd); +OutputDBD($out, $dbd); if ($opt_o) { close $out or die "Closing $opt_o failed: $!\n"; diff --git a/src/tools/dbdReport.pl b/src/tools/dbdReport.pl index 303782879..9d1bd068e 100755 --- a/src/tools/dbdReport.pl +++ b/src/tools/dbdReport.pl @@ -32,7 +32,7 @@ my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my $macros = EPICS::macLib->new(@opt_S); my $dbd = DBD->new(); -&ParseDBD($dbd, &Readfile(shift @ARGV, $macros, \@opt_I)); +ParseDBD($dbd, Readfile(shift @ARGV, $macros, \@opt_I)); $Text::Wrap::columns = 75; diff --git a/src/tools/dbdToHtml.pl b/src/tools/dbdToHtml.pl index bf162fefc..44f03fdbd 100644 --- a/src/tools/dbdToHtml.pl +++ b/src/tools/dbdToHtml.pl @@ -55,7 +55,7 @@ my $infile = shift @ARGV; $infile =~ m/\.dbd.pod$/ or die "$tool: Input file '$infile' must have '.dbd.pod' extension\n"; -&ParseDBD($dbd, &Readfile($infile, 0, \@opt_I)); +ParseDBD($dbd, Readfile($infile, 0, \@opt_I)); if (!$opt_o) { ($opt_o = $infile) =~ s/\.dbd\.pod$/.html/; diff --git a/src/tools/dbdToMenuH.pl b/src/tools/dbdToMenuH.pl index 1e5f35fb2..e1d640c16 100755 --- a/src/tools/dbdToMenuH.pl +++ b/src/tools/dbdToMenuH.pl @@ -49,7 +49,7 @@ my $guard_name = "INC_$outbase"; $guard_name =~ tr/a-zA-Z0-9_/_/cs; $guard_name =~ s/(_[hH])?$/_H/; -&ParseDBD($dbd, &Readfile($infile, 0, \@opt_I)); +ParseDBD($dbd, Readfile($infile, 0, \@opt_I)); if ($opt_D) { my %filecount; diff --git a/src/tools/dbdToRecordtypeH.pl b/src/tools/dbdToRecordtypeH.pl index 06c3b565e..0516a58bf 100755 --- a/src/tools/dbdToRecordtypeH.pl +++ b/src/tools/dbdToRecordtypeH.pl @@ -50,7 +50,7 @@ my $guard_name = "INC_$outbase"; $guard_name =~ tr/a-zA-Z0-9_/_/cs; $guard_name =~ s/(_[hH])?$/_H/; -&ParseDBD($dbd, &Readfile($infile, 0, \@opt_I)); +ParseDBD($dbd, Readfile($infile, 0, \@opt_I)); my $rtypes = $dbd->recordtypes; die "$tool: Input file must contain a single recordtype definition.\n" @@ -99,9 +99,9 @@ if ($opt_D) { # Output dependencies only, to stdout "\n} ${rn}FieldIndex;\n\n"; print OUTFILE "#ifdef GEN_SIZE_OFFSET\n\n"; if ($opt_s) { - &newtables; + newtables(); } else { - &oldtables; + oldtables(); } print OUTFILE "#endif /* GEN_SIZE_OFFSET */\n"; } diff --git a/src/tools/expandVars.pl b/src/tools/expandVars.pl index 76e35e259..c61bfbf6e 100644 --- a/src/tools/expandVars.pl +++ b/src/tools/expandVars.pl @@ -22,10 +22,10 @@ use EPICS::Copy; # Process command line options our ($opt_a, $opt_d, @opt_D, $opt_h, $opt_t); getopts('a:dD@ht:') - or &HELP_MESSAGE; + or HELP_MESSAGE(); # Handle the -h command -&HELP_MESSAGE if $opt_h; +HELP_MESSAGE() if $opt_h; die "Path to TOP not set, use -t option\n" unless $opt_t; diff --git a/src/tools/fullPathName.pl b/src/tools/fullPathName.pl index a553dbd3c..0b1bdc0dd 100644 --- a/src/tools/fullPathName.pl +++ b/src/tools/fullPathName.pl @@ -13,17 +13,56 @@ # might have trailing directory names that don't exist yet. use strict; +use warnings; + +use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl", $Bin); -use Getopt::Std; use EPICS::Path; +use Pod::Usage; + +=head1 NAME + +fullPathName.pl - Convert a pathname to an absolute path + +=head1 SYNOPSIS + +B [B<-h>] /path/to/something + +=head1 DESCRIPTION + +The EPICS build system needs the ability to get the absolute path of a file or +directory that does not exist at the time. The AbsPath() function in the +EPICS::Path module provides the necessary functionality, which this script makes +available to the build system. The string which is returned on the standard +output stream has had any shell special characters escaped with a back-slash +(except on Windows). + +=head1 OPTIONS + +B understands the following options: + +=over 4 + +=item B<-h> + +Help, display this document as text. + +=back + +=cut + our ($opt_h); -$Getopt::Std::OUTPUT_HELP_VERSION = 1; -&HELP_MESSAGE if !getopts('h') || $opt_h || @ARGV != 1; +sub HELP_MESSAGE { + pod2usage(-exitval => 2, -verbose => $opt_h); +} + +HELP_MESSAGE() if !getopts('h') || $opt_h || @ARGV != 1; my $path = AbsPath(shift); @@ -32,8 +71,11 @@ $path =~ s/([!"\$&'\(\)*,:;<=>?\[\\\]^`{|}])/\\$1/g unless $^O eq 'MSWin32'; print "$path\n"; +=head1 COPYRIGHT AND LICENSE -sub HELP_MESSAGE { - print STDERR "Usage: fullPathName.pl [-h] pathname\n"; - exit 2; -} +Copyright (C) 2009 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut diff --git a/src/tools/munch.pl b/src/tools/munch.pl index f6c0d6e08..2aee71344 100644 --- a/src/tools/munch.pl +++ b/src/tools/munch.pl @@ -7,19 +7,65 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* + # $Revision-Id$ -# -# Creates a ctdt.c file of C++ static constructors and destructors, -# as required for all vxWorks binaries containing C++ code. use strict; use warnings; + use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; -our ($opt_o); +use Pod::Usage; -$Getopt::Std::OUTPUT_HELP_VERSION = 1; -&HELP_MESSAGE if !getopts('o:') || @ARGV != 1; +=head1 NAME + +munch.pl - Combine C++ static constructors and destructors for libraries + +=head1 SYNOPSIS + +B [B<-h>] [B<-o> file_ctdt.c] file.nm + +=head1 DESCRIPTION + +Creates a ctdt.c file of C++ static constructors and destructors, as required +for all vxWorks binaries containing C++ code. The VxWorks linking loader and +unloader only call one constructor function, so the code generated by this +script is needed to ensure that all the static constructors and destructors in +the library module will be executed at the appropriate time. + +The file input to this function is generated by running the B program on the +library concerned. The processing algorithm was reverse-engineered from the +B scripts provided with various versions of VxWorks up to 6.9. + +=head1 OPTIONS + +B understands the following options: + +=over 4 + +=item B<-h> + +Help, display this document as text. + +=item B<-o> file_ctdt.c + +Name of the output file to be created. + +=back + +If no output filename is set with a B<-o> option, the generated C code will be +sent to the standard output stream. + +=cut + +our ($opt_o, $opt_h); + +sub HELP_MESSAGE { + pod2usage(-exitval => 2, -verbose => $opt_h); +} + +HELP_MESSAGE() if !getopts('ho:') || $opt_h || @ARGV != 1; # Is exception handler frame info required? my $need_eh_frame = 0; @@ -154,7 +200,11 @@ sub cDecl { return $decl; } -sub HELP_MESSAGE { - print STDERR "Usage: munch.pl [-o file_ctdt.c] file.nm\n"; - exit 2; -} +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2013 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut diff --git a/src/tools/podRemove.pl b/src/tools/podRemove.pl index c44f16a3d..66f09e308 100644 --- a/src/tools/podRemove.pl +++ b/src/tools/podRemove.pl @@ -6,17 +6,57 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -# $Id$ +# $Revision-Id$ use strict; use warnings; use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; -our ($opt_o); +use Pod::Usage; -$Getopt::Std::OUTPUT_HELP_VERSION = 1; -&HELP_MESSAGE if !getopts('o:') || @ARGV != 1; +=head1 NAME + +podRemove.pl - Remove POD directives from files + +=head1 SYNOPSIS + +B [B<-h>] [B<-o> file] file.pod + +=head1 DESCRIPTION + +Removes Perl's POD documentation from a text file + +=head1 OPTIONS + +B understands the following options: + +=over 4 + +=item B<-h> + +Help, display this document as text. + +=item B<-o> file + +Name of the output file to be created. + +=back + +If no output filename is set, the file created will be named after the input +file, removing any directory components in the path and removing any .pod file +extension. + +=cut + +our ($opt_o, $opt_h); + +sub HELP_MESSAGE { + pod2usage(-exitval => 2, -verbose => $opt_h); +} + +HELP_MESSAGE() if !getopts('ho:') || $opt_h || @ARGV != 1; my $infile = shift @ARGV; @@ -43,7 +83,11 @@ while (<$inp>) { close $out; close $inp; -sub HELP_MESSAGE { - print STDERR "Usage: podRemove.pl [-o file] file.pod\n"; - exit 2; -} +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut diff --git a/src/tools/podToHtml.pl b/src/tools/podToHtml.pl index d5817c797..e950f6c6e 100644 --- a/src/tools/podToHtml.pl +++ b/src/tools/podToHtml.pl @@ -6,21 +6,25 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -# $Id$ +# $Revision-Id$ use strict; use warnings; use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; + use Pod::Simple::HTML; +use Pod::Usage; + =head1 NAME -podToHtml.pl - convert EPICS .pod files to .html +podToHtml.pl - Convert EPICS .pod files to .html =head1 SYNOPSIS -B [B<-s>] [B<-o> file.html] file.pod +B [B<-h>] [B<-s>] [B<-o> file.html] file.pod =head1 DESCRIPTION @@ -33,10 +37,14 @@ is calculated based on the number of components in the path to the input file. =head1 OPTIONS -I understands the following options: +B understands the following options: =over 4 +=item B<-h> + +Help, display this document as text. + =item B<-s> Indicates that the first component of the input file path is not part of the @@ -55,11 +63,14 @@ extension with .html. =cut -our $opt_o; +our ($opt_o, $opt_h); our $opt_s = 0; -$Getopt::Std::OUTPUT_HELP_VERSION = 1; -&HELP_MESSAGE if !getopts('o:s') || @ARGV != 1; +sub HELP_MESSAGE { + pod2usage(-exitval => 2, -verbose => $opt_h); +} + +HELP_MESSAGE() if !getopts('ho:s') || $opt_h || @ARGV != 1; my $infile = shift @ARGV; @@ -89,14 +100,9 @@ $podHtml->run; print $out $html; close $out; -sub HELP_MESSAGE { - print STDERR "Usage: podToHtml.pl [-s] [-o file.html] file.pod\n"; - exit 2; -} - =head1 COPYRIGHT AND LICENSE -Copyright (C) 2010 UChicago Argonne LLC, as Operator of Argonne National +Copyright (C) 2013 UChicago Argonne LLC, as Operator of Argonne National Laboratory. This software is distributed under the terms of the EPICS Open License. diff --git a/src/tools/registerRecordDeviceDriver.pl b/src/tools/registerRecordDeviceDriver.pl index 368d7acea..ea93a0d60 100644 --- a/src/tools/registerRecordDeviceDriver.pl +++ b/src/tools/registerRecordDeviceDriver.pl @@ -30,7 +30,7 @@ my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my ($file, $subname, $bldTop) = @ARGV; my $dbd = DBD->new(); -&ParseDBD($dbd, &Readfile($file, "", \@path)); +ParseDBD($dbd, Readfile($file, "", \@path)); if ($opt_D) { # Output dependencies only my %filecount; From 3a7cb332348ad4d285340c82a753f2ac60728af6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 22 Apr 2015 18:01:20 -0500 Subject: [PATCH 081/204] Fixed some build issues in db/test --- src/ioc/db/test/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 71140b316..34534d396 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -93,13 +93,13 @@ dbCaLinkTest_SRCS += dbCaLinkTest.c dbCaLinkTest_SRCS += dbCACTest.cpp dbCaLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbCaLinkTest.c +testHarness_SRCS += dbCACTest.cpp TESTS += dbCaLinkTest TESTFILES += ../dbCaLinkTest1.db ../dbCaLinkTest2.db ../dbCaLinkTest3.db scanIoTest_DBD += menuScan.dbd TESTPROD_HOST += scanIoTest scanIoTest_SRCS += scanIoTest.c -scanIoTest_REGRDDFLAGS = -l scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += scanIoTest.c TESTFILES += ../scanIoTest.db @@ -152,4 +152,6 @@ xRecord$(DEP): $(COMMON_DIR)/xRecord.h arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h devx$(DEP): $(COMMON_DIR)/xRecord.h +scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h +dbCaLinkTest$(DEP): $(COMMON_DIR)/xRecord.h $(COMMON_DIR)/arrRecord.h From 7947807e0907039f58a10944721e6ca497b7748c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 18 May 2015 21:46:01 -0400 Subject: [PATCH 082/204] fix typo --- src/ioc/db/test/dbScanTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbScanTest.c b/src/ioc/db/test/dbScanTest.c index 0815827e3..905827e0c 100644 --- a/src/ioc/db/test/dbScanTest.c +++ b/src/ioc/db/test/dbScanTest.c @@ -37,7 +37,7 @@ static void onceComp(void *junk, dbCommon *prec) static void testOnce(void) { testDiag("check scanOnceCallback() callback"); - waiter = epicsEventMustCreate(epicsEventError); + waiter = epicsEventMustCreate(epicsEventEmpty); testdbPrepare(); From 1240f097973ac5cfa6b83a92c7e2e9240bccc832 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 1 Jun 2015 19:19:24 -0400 Subject: [PATCH 083/204] genVersionHeader: update HG, add SVN and BZR Update the HG case to use --cwd to avoid dependence on shell syntax. --- src/tools/genVersionHeader.pl | 42 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index 19c28edd7..28ae666e6 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -48,13 +48,13 @@ if(!$foundvcs && -d "$opt_t/.hg") { # Mercurial # v1-4-abcdef-dirty # is 4 commits after tag 'v1' with short hash abcdef # with uncommited modifications - $result = `cd "$opt_t" && hg tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`; + $result = `hg --cwd "$opt_t" tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`; chomp($result); if(!$? && length($result)>1) { $opt_V = $result; $foundvcs = 1; # see if working copy has modifications, additions, removals, or missing files - my $hasmod = `cd "$opt_t" && hg status -m -a -r -d`; + my $hasmod = `hg --cwd "$opt_t" status -m -a -r -d`; chomp($hasmod); if(length($hasmod)>0) { $opt_V = "$opt_V-dirty"; @@ -70,6 +70,38 @@ if(!$foundvcs && -d "$opt_t/.git") { $foundvcs = 1; } } +if(!$foundvcs && -d "$opt_t/.svn") { + # 12345 + $result = `cd "$opt_t" && svn info --non-interactive`; + chomp($result); + if(!$? && $result =~ /^Revision:\s*(\d+)/m) { + $opt_V = $1; + $foundvcs = 1; + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `cd "$opt_t" && svn status -q --non-interactive`; + chomp($hasmod); + if(length($hasmod)>0) { + $opt_V = "$opt_V-dirty"; + } + } +} +if(!$foundvcs && -d "$opt_t/.bzr") { + # 12444-anj@aps.anl.gov-20131003210403-icfd8mc37g8vctpf + $result = `cd "$opt_t" && bzr version-info -q --custom --template="{revno}-{revision_id}"`; + chomp($result); + print "BZR $result"; + if(!$? && length($result)>1) { + $opt_V = $result; + $foundvcs = 1; + # see if working copy has modifications, additions, removals, or missing files + # unfortunately "bzr version-info --check-clean ..." doesn't seem to work as documented + my $hasmod = `cd "$opt_t" && bzr status -SV`; + chomp($hasmod); + if(length($hasmod)>0) { + $opt_V = "$opt_V-dirty"; + } + } +} my $output = "#ifndef $opt_N\n# define $opt_N \"$opt_V\"\n#endif\n"; print "== would\n$output" if $opt_v; @@ -80,12 +112,12 @@ if(open($DST, '+<', $outfile)) { print "== have\n$actual" if $opt_v; if($actual eq $output) { - print "Keeping existing VCS version header $outfile\n"; + print "Keeping existing VCS version header $outfile with \"$opt_V\"\n"; exit(0) } - print "Updating VCS version header $outfile\n"; + print "Updating VCS version header $outfile with \"$opt_V\"\n"; } else { - print "Creating VCS version header $outfile\n"; + print "Creating VCS version header $outfile with \"$opt_V\"\n"; open($DST, '>', $outfile) or die "Unable to open or create VCS version header $outfile"; } From 7fd4ac5a67becdb386144e27c4439d99345a80fb Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 1 Jun 2015 19:19:24 -0400 Subject: [PATCH 084/204] update template use waveform as long string use app. name in header and macro name --- src/template/base/Makefile | 2 +- .../top/exampleApp/Db/dbVersionExample.db | 6 +- src/template/base/top/exampleApp/src/Makefile | 8 ++- .../top/exampleApp/src/_APPNAME_Hello.dbd | 2 +- .../base/top/exampleApp/src/devGenVersion.c | 56 +++++++++++++++++++ .../exampleApp/src/devStringInGenVersion.c | 47 ---------------- 6 files changed, 67 insertions(+), 54 deletions(-) create mode 100644 src/template/base/top/exampleApp/src/devGenVersion.c delete mode 100644 src/template/base/top/exampleApp/src/devStringInGenVersion.c diff --git a/src/template/base/Makefile b/src/template/base/Makefile index f2292c4e5..4e7de23d3 100644 --- a/src/template/base/Makefile +++ b/src/template/base/Makefile @@ -32,7 +32,7 @@ TEMPLATES += top/exampleApp/Db/dbVersionExample.db TEMPLATES += top/exampleApp/Db/dbSubExample.db TEMPLATES += top/exampleApp/Db/user.substitutions TEMPLATES += top/exampleApp/src/Makefile -TEMPLATES += top/exampleApp/src/devStringInGenVersion.c +TEMPLATES += top/exampleApp/src/devGenVersion.c TEMPLATES += top/exampleApp/src/xxxRecord.dbd TEMPLATES += top/exampleApp/src/xxxRecord.c TEMPLATES += top/exampleApp/src/devXxxSoft.c diff --git a/src/template/base/top/exampleApp/Db/dbVersionExample.db b/src/template/base/top/exampleApp/Db/dbVersionExample.db index bd1c175df..e86b75dc4 100644 --- a/src/template/base/top/exampleApp/Db/dbVersionExample.db +++ b/src/template/base/top/exampleApp/Db/dbVersionExample.db @@ -1,5 +1,7 @@ -record(stringin, "$(user):version") { - field(DTYP, "My Version") +record(waveform, "$(user):version") { + field(DTYP, "Module Version") field(DESC, "Module version") + field(FTVL, "CHAR") + field(NELM, "$(NELM=200)") field(PINI, "YES") } diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile index a419167b8..0c127999b 100644 --- a/src/template/base/top/exampleApp/src/Makefile +++ b/src/template/base/top/exampleApp/src/Makefile @@ -17,7 +17,7 @@ DBD += xxxSupport.dbd # Compile and add the code to the support library _APPNAME_Support_SRCS += xxxRecord.c _APPNAME_Support_SRCS += devXxxSoft.c -_APPNAME_Support_SRCS += devStringInGenVersion.c +_APPNAME_Support_SRCS += devGenVersion.c # Link locally-provided code into the support library, # rather than directly into the IOC application. @@ -29,7 +29,9 @@ _APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS) # Generate a header which defines a macro with a version string # with the date and time, or a revision id from VCS system if available -GENVERSION = mymodversion.h +GENVERSION = _APPNAME_Version.h +# Macro name +GENVERSIONMACRO = _APPNAME_VERSION #============================= # Build the IOC application @@ -88,4 +90,4 @@ include $(TOP)/configure/RULES # Dependency for the generated header # must be explicit -devStringInGenVersion$(DEP): $(GENVERSION) +devGenVersion$(DEP): $(GENVERSION) diff --git a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd index 5be6cdc04..47f187050 100644 --- a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd +++ b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd @@ -1,2 +1,2 @@ registrar(helloRegister) -device(stringin,INST_IO,devSiMyModVersion,"My Version") +device(waveform,INST_IO,devWfMyModVersion,"Module Version") diff --git a/src/template/base/top/exampleApp/src/devGenVersion.c b/src/template/base/top/exampleApp/src/devGenVersion.c new file mode 100644 index 000000000..297b69138 --- /dev/null +++ b/src/template/base/top/exampleApp/src/devGenVersion.c @@ -0,0 +1,56 @@ +/* devGenVersion.c */ +/* Example device support providing the module version string as a charactor array (long string) */ + +#include +#include +#include +#include + +#include "recGbl.h" +#include "alarm.h" +#include "recSup.h" +#include "devSup.h" +#include "menuFtype.h" +#include "waveformRecord.h" + +#include "_APPNAME_Version.h" + +/* must be last include */ +#include "epicsExport.h" + +static long read_wf(waveformRecord *prec) +{ + size_t N = strlen(_APPNAME_VERSION)+1; + char *buf = prec->bptr; + + if(prec->ftvl!=menuFtypeCHAR) { + (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return 0; + } + + if(N>=prec->nelm) + N = prec->nelm; + prec->nord = N; + + memcpy(buf, _APPNAME_VERSION, N); + buf[prec->nelm-1] = '\0'; + + return 0; +} + +static struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_si; +}devWfMyModVersion={ + 5, + NULL, + NULL, + NULL, + NULL, + read_wf, +}; +epicsExportAddress(dset,devWfMyModVersion); diff --git a/src/template/base/top/exampleApp/src/devStringInGenVersion.c b/src/template/base/top/exampleApp/src/devStringInGenVersion.c deleted file mode 100644 index 21fd09bfc..000000000 --- a/src/template/base/top/exampleApp/src/devStringInGenVersion.c +++ /dev/null @@ -1,47 +0,0 @@ -/* devStringIn.c */ -/* Example device support module for stringinRecord */ - -#include -#include -#include -#include - -#include "recGbl.h" -#include "alarm.h" -#include "recSup.h" -#include "devSup.h" -#include "stringinRecord.h" - -#include "mymodversion.h" - -/* must be last include */ -#include "epicsExport.h" - -static long read_si(stringinRecord *prec) -{ - size_t N = strlen(MODULEVERSION); - if(Nval)) { - strcpy(prec->val, MODULEVERSION); - } else { - /* Not enough space, so signal an alarm */ - (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); - } - return 0; -} - -static struct { - long number; - DEVSUPFUN report; - DEVSUPFUN init; - DEVSUPFUN init_record; - DEVSUPFUN get_ioint_info; - DEVSUPFUN read_si; -}devSiMyModVersion={ - 5, - NULL, - NULL, - NULL, - NULL, - read_si, -}; -epicsExportAddress(dset,devSiMyModVersion); From 53d01f6fb437ff2fc87157f71cb007486d8956ea Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 3 Jun 2015 00:15:39 -0500 Subject: [PATCH 085/204] Show how to make time providers backwards-compatible --- documentation/RELEASE_NOTES.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 48dacb281..5a3f74280 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -29,6 +29,19 @@ it in source code will no longer compile. The identifier epicsTimeOK still exist and has the value 0 as before, so most code that uses these APIs can be changed in a way that is backwards-compatible with the previous return status.

+

Time providers that have to return a status value and still need to be built +with earlier versions of Base can define the necessary status symbols like this:

+ +
+#include "epicsTime.h"
+
+#ifndef M_time
+/* S_time_... status values were not provided before Base 3.16 */
+#define S_time_unsynchronized epicsTimeERROR
+#define S_time_...whatever... epicsTimeERROR
+#endif
+
+

Callback subsystem API

Added a new macro callbackGetPriority(prio, callback) to the From 155017bf0910ec0f7bd15b8e2b0ec63c404c6f80 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 9 Jun 2015 15:36:28 -0500 Subject: [PATCH 086/204] Removed references to VxWorks 5.x --- configure/os/CONFIG.Common.vxWorksCommon | 34 +++++-------------- configure/os/CONFIG.linux-386.Common | 2 +- configure/os/CONFIG.linux-486.Common | 2 +- configure/os/CONFIG.linux-586.Common | 2 +- configure/os/CONFIG.linux-686.Common | 2 +- configure/os/CONFIG.linux-x86.Common | 2 +- configure/os/CONFIG.linux-x86_64.Common | 2 +- configure/os/CONFIG_SITE.Common.vxWorksCommon | 24 ++++--------- documentation/README.1st | 16 ++++----- documentation/README.html | 11 +++--- src/libCom/osi/os/vxWorks/osdStdio.c | 1 + src/libCom/osi/os/vxWorks/osdWireConfig.h | 1 + 12 files changed, 37 insertions(+), 62 deletions(-) diff --git a/configure/os/CONFIG.Common.vxWorksCommon b/configure/os/CONFIG.Common.vxWorksCommon index 56c533400..b5cea700c 100644 --- a/configure/os/CONFIG.Common.vxWorksCommon +++ b/configure/os/CONFIG.Common.vxWorksCommon @@ -1,10 +1,9 @@ # CONFIG.Common.vxWorksCommon # # $Revision-Id$ -# This file is maintained by the build community. # # Definitions for vxWorks target archs -# Sites may override these definitions in CONFIG_SITE.Common.vxWorksCommon +# Override these definitions in CONFIG_SITE.Common.vxWorksCommon # or CONFIG_SITE..vxWorksCommon #------------------------------------------------------- @@ -66,8 +65,6 @@ VXWORKS_MAJOR_VERSION = $(basename $(basename $(VXWORKS_VERSION))) # These are needed for vxWorks 6.x; the GNU toolset version number # is in the path to the compiler tools: -VX_GNU_VERSION_5.4 = 2.95 -VX_GNU_VERSION_5.5 = 2.96 VX_GNU_VERSION_6.0 = 3.3.2 VX_GNU_VERSION_6.1 = 3.3.2 VX_GNU_VERSION_6.2 = 3.3.2 @@ -83,30 +80,21 @@ VX_GNU_VERSION = $(VX_GNU_VERSION_$(VXWORKS_VERSION)) VX_GNU_MAJOR_VERSION = $(basename $(basename $(VX_GNU_VERSION))) #-------------------------------------------------- -# Fix WIND_BASE for vxWorks 6.x on linux -# NB: We know the value of WIND_HOST_TYPE here, but not VXWORKS_VERSION +# Fix old Linux WIND_HOST_TYPE ifeq ($(WIND_HOST_TYPE),x86-linux) - WIND_HOST_TYPE_5 = x86-linux - WIND_HOST_TYPE_6 = x86-linux2 - WIND_HOST_TYPE = $(WIND_HOST_TYPE_$(VXWORKS_MAJOR_VERSION)) + WIND_HOST_TYPE = x86-linux2 endif #-------------------------------------------------- # vxWorks directory definitions -VX_DIR_5 = $(WIND_BASE) -VX_DIR_6 = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION) -VX_DIR = $(VX_DIR_$(VXWORKS_MAJOR_VERSION)) +VX_DIR = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION) -VX_INCLUDE_DIRS_5 = $(VX_DIR)/target/h -VX_INCLUDE_DIRS_6 = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip -GNU_TARGET_INCLUDE_DIR = $(VX_INCLUDE_DIRS_$(VXWORKS_MAJOR_VERSION)) +GNU_TARGET_INCLUDE_DIR = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip #-------------------------------------------------- # vxWorks GNU directories -GNU_DIR_5 = $(WIND_BASE)/host/$(WIND_HOST_TYPE) -GNU_DIR_6 = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE) -GNU_DIR = $(GNU_DIR_$(VXWORKS_MAJOR_VERSION)) +GNU_DIR = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE) #-------------------------------------------------- # Wind River moved nm out of GNU_BIN in some versions @@ -126,9 +114,7 @@ NM = $(NM_DIR)/$(CMPLR_PREFIX)nm$(CMPLR_SUFFIX)$(HOSTEXE) #-------------------------------------------------- # A linker script is essential for munching from vxWorks 6.6 onwards -# (i.e. with versions that use gcc 4.1.2 or later). It can be used -# with any vxWorks 5 or 6 version, but apparently should not be used -# when compiling for 68K (which isn't supported in vxWorks 6 anyway) +# (i.e. with versions that use gcc 4.1.2 or later). MUNCH_LDFLAGS_6 = -T $(VX_DIR)/target/h/tool/gnu/ldscripts/link.OUT MUNCH_LDFLAGS = $(MUNCH_LDFLAGS_$(VXWORKS_MAJOR_VERSION)) @@ -143,11 +129,10 @@ export TOOL_FAMILY = GNU OP_SYS_CPPFLAGS += -DvxWorks=vxWorks OP_SYS_CFLAGS += -fno-builtin -# Fix for vxWorks 5 headers that use macros defined in vxWorks.h but +# Fix for vxWorks headers that use macros defined in vxWorks.h but # which don't actually include vxWorks.h themselves, for example the # target/h/sys/stat.h file which uses ULONG. This also stops dbDefs.h -# from defining the OFFSET macro, which generates lots of warnings in -# both vxWorks 5 and 6. +# from defining the OFFSET macro, which generates lots of warnings. OP_SYS_INCLUDE_CPPFLAGS += -include $(VX_DIR)/target/h/vxWorks.h #-------------------------------------------------- @@ -160,7 +145,6 @@ OPT_CXXFLAGS_YES = -O2 CODE_CFLAGS = # # For vxWorks versions before 6.3 we need this g++ compiler flag -CODE_CXXFLAGS_5 = -fno-implicit-templates CODE_CXXFLAGS_6.0 = -fno-implicit-templates CODE_CXXFLAGS_6.1 = -fno-implicit-templates CODE_CXXFLAGS_6.2 = -fno-implicit-templates diff --git a/configure/os/CONFIG.linux-386.Common b/configure/os/CONFIG.linux-386.Common index 23c1d9403..e37483163 100644 --- a/configure/os/CONFIG.linux-386.Common +++ b/configure/os/CONFIG.linux-386.Common @@ -10,4 +10,4 @@ #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common -WIND_HOST_TYPE = x86-linux +WIND_HOST_TYPE = x86-linux2 diff --git a/configure/os/CONFIG.linux-486.Common b/configure/os/CONFIG.linux-486.Common index 74563656a..f2aa5bd0f 100644 --- a/configure/os/CONFIG.linux-486.Common +++ b/configure/os/CONFIG.linux-486.Common @@ -10,4 +10,4 @@ #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common -WIND_HOST_TYPE = x86-linux +WIND_HOST_TYPE = x86-linux2 diff --git a/configure/os/CONFIG.linux-586.Common b/configure/os/CONFIG.linux-586.Common index 2f1e82527..3c0e5e1a6 100644 --- a/configure/os/CONFIG.linux-586.Common +++ b/configure/os/CONFIG.linux-586.Common @@ -10,4 +10,4 @@ #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common -WIND_HOST_TYPE = x86-linux +WIND_HOST_TYPE = x86-linux2 diff --git a/configure/os/CONFIG.linux-686.Common b/configure/os/CONFIG.linux-686.Common index 22da7d0b0..b17268457 100644 --- a/configure/os/CONFIG.linux-686.Common +++ b/configure/os/CONFIG.linux-686.Common @@ -10,4 +10,4 @@ #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common -WIND_HOST_TYPE = x86-linux +WIND_HOST_TYPE = x86-linux2 diff --git a/configure/os/CONFIG.linux-x86.Common b/configure/os/CONFIG.linux-x86.Common index f6d94061f..16aa54986 100644 --- a/configure/os/CONFIG.linux-x86.Common +++ b/configure/os/CONFIG.linux-x86.Common @@ -10,4 +10,4 @@ #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common -WIND_HOST_TYPE = x86-linux +WIND_HOST_TYPE = x86-linux2 diff --git a/configure/os/CONFIG.linux-x86_64.Common b/configure/os/CONFIG.linux-x86_64.Common index 23c105c46..6973d95bb 100644 --- a/configure/os/CONFIG.linux-x86_64.Common +++ b/configure/os/CONFIG.linux-x86_64.Common @@ -10,4 +10,4 @@ #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common -WIND_HOST_TYPE = x86-linux +WIND_HOST_TYPE = x86-linux2 diff --git a/configure/os/CONFIG_SITE.Common.vxWorksCommon b/configure/os/CONFIG_SITE.Common.vxWorksCommon index 993694350..2259f9689 100644 --- a/configure/os/CONFIG_SITE.Common.vxWorksCommon +++ b/configure/os/CONFIG_SITE.Common.vxWorksCommon @@ -1,25 +1,17 @@ # CONFIG_SITE.Common.vxWorksCommon # # Site specific definitions for vxWorks target builds. -# Only the local epics system manager should modify this file # Compiler options can vary with the vxWorks version number, so we -# need to know that. However don't include any third-level digits -# (e.g. the .2 in 5.5.2) because we don't need them. +# need to know that. Do not include any third-level digits. -# Note: vxWorks 5.4.x (Tornado 2.0.x) is not supported +# Note: vxWorks 5.4.x and 5.5.x (Tornado 2.x) are not supported. +# VxWorks 6.0 through 6.5 use older, untested versions of GCC. -VXWORKS_VERSION = 5.5 -#VXWORKS_VERSION = 6.0 -#VXWORKS_VERSION = 6.1 -#VXWORKS_VERSION = 6.2 -#VXWORKS_VERSION = 6.3 -#VXWORKS_VERSION = 6.4 -#VXWORKS_VERSION = 6.5 #VXWORKS_VERSION = 6.6 #VXWORKS_VERSION = 6.7 #VXWORKS_VERSION = 6.8 -#VXWORKS_VERSION = 6.9 +VXWORKS_VERSION = 6.9 # Sites may override the following path for a particular host @@ -27,10 +19,8 @@ VXWORKS_VERSION = 5.5 # CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon file. # WIND_BASE is where you installed the Wind River software. -# Under vxWorks 6.x this is *not* the same as the old VX_DIR setting -WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS) -#WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION) +WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION) #WIND_BASE = /ade/vxWorks/$(VXWORKS_VERSION) @@ -39,9 +29,9 @@ WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS) #WORKBENCH_VERSION = 2.6 #WORKBENCH_VERSION = 3.0 #WORKBENCH_VERSION = 3.2 -#WORKBENCH_VERSION = 3.3 +WORKBENCH_VERSION = 3.3 # Utilities Version number, required from vxWorks 6.8 and later -#UTILITIES_VERSION = 1.0 +UTILITIES_VERSION = 1.0 diff --git a/documentation/README.1st b/documentation/README.1st index db5fcf867..b74f6ae92 100644 --- a/documentation/README.1st +++ b/documentation/README.1st @@ -85,17 +85,17 @@ as processes on the host platform. vxWorks - You must have vxWorks 5.5.x or 6.x installed if any of your target - systems are vxWorks systems; the C++ compiler for vxWorks 5.4 is now too - old to support. The vxWorks installation provides the cross-compiler and - header files needed to build for these targets. The absolute path to and - the version number of the vxWorks installation must be set in the + You must have vxWorks 6 installed if any of your target systems are + vxWorks systems; the C++ compilers for vxWorks 5.x are now too old to + support. The vxWorks installation provides the cross-compiler and header + files needed to build for these targets. The absolute path to and the + version number of the vxWorks installation must be set in the base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its target-specific overrides. - Consult the vxWorks 5.x or vxWorks 6.x EPICS web pages about and the - vxWorks documentation for information about configuring your vxWorks - operating system for use with EPICS. + Consult the vxWorks 6.x EPICS web pages and the vxWorks documentation + for information about configuring your vxWorks operating system for use + with EPICS. RTEMS For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or diff --git a/documentation/README.html b/documentation/README.html index 08cadff9b..f717c3d47 100644 --- a/documentation/README.html +++ b/documentation/README.html @@ -90,17 +90,16 @@ as processes on the host platform.

vxWorks
- You must have vxWorks 5.5.x or 6.x installed if any of your target systems are - vxWorks systems; the C++ compiler for vxWorks 5.4 is now too old to support. - The vxWorks installation provides the cross-compiler and header files needed to + You must have vxWorks 6 installed if any of your target systems are vxWorks + systems; the C++ compilers for vxWorks 5.x are now too old to support. The + vxWorks installation provides the cross-compiler and header files needed to build for these targets. The absolute path to and the version number of the vxWorks installation must be set in the base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its target-specific overrides.

-

Consult the vxWorks - 5.x or vxWorks - 6.x EPICS web pages about and the vxWorks documentation for information +

Consult the vxWorks + 6.x EPICS web pages and the vxWorks documentation for information about configuring your vxWorks operating system for use with EPICS.

RTEMS
diff --git a/src/libCom/osi/os/vxWorks/osdStdio.c b/src/libCom/osi/os/vxWorks/osdStdio.c index ed15221d9..20664a1a4 100644 --- a/src/libCom/osi/os/vxWorks/osdStdio.c +++ b/src/libCom/osi/os/vxWorks/osdStdio.c @@ -9,6 +9,7 @@ \*************************************************************************/ #include +#include #include #include "epicsStdio.h" #include "dbDefs.h" diff --git a/src/libCom/osi/os/vxWorks/osdWireConfig.h b/src/libCom/osi/os/vxWorks/osdWireConfig.h index b1aa6e232..408ec6758 100644 --- a/src/libCom/osi/os/vxWorks/osdWireConfig.h +++ b/src/libCom/osi/os/vxWorks/osdWireConfig.h @@ -9,6 +9,7 @@ #ifndef osdWireConfig_h #define osdWireConfig_h +#include #include #if _BYTE_ORDER == _LITTLE_ENDIAN From 3a54e97758458f6292a60befcf2451b645d3a4a4 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 29 Jun 2015 17:45:18 -0500 Subject: [PATCH 087/204] libCom: epicsReadline refactoring --- configure/CONFIG_SITE_ENV | 3 + documentation/RELEASE_NOTES.html | 11 + src/libCom/env/envDefs.h | 1 + src/libCom/osi/epicsReadline.c | 149 +++++++ .../osi/{os/default => }/epicsReadline.h | 13 +- src/libCom/osi/os/RTEMS/osdReadline.c | 70 ++++ src/libCom/osi/os/default/epicsReadline.c | 376 ------------------ src/libCom/osi/os/default/gnuReadline.c | 106 +++++ src/libCom/osi/os/vxWorks/osdReadline.c | 101 +++++ 9 files changed, 447 insertions(+), 383 deletions(-) create mode 100644 src/libCom/osi/epicsReadline.c rename src/libCom/osi/{os/default => }/epicsReadline.h (66%) create mode 100644 src/libCom/osi/os/RTEMS/osdReadline.c delete mode 100644 src/libCom/osi/os/default/epicsReadline.c create mode 100644 src/libCom/osi/os/default/gnuReadline.c create mode 100644 src/libCom/osi/os/vxWorks/osdReadline.c diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV index d63f19fc8..efe2560e0 100644 --- a/configure/CONFIG_SITE_ENV +++ b/configure/CONFIG_SITE_ENV @@ -57,8 +57,11 @@ EPICS_TS_NTP_INET= # Prompt string # IOCSH_HISTSIZE # Number of lines of command history to keep. +# IOCSH_HISTEDIT_DISABLE +# Prevents use of readline or equivalent if defined. IOCSH_PS1="epics> " IOCSH_HISTSIZE=50 +IOCSH_HISTEDIT_DISABLE= # Log Server: # EPICS_IOC_LOG_INET diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index b2e5fdc65..2a91b73c0 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,17 @@ --> +

Refactoring of epicsReadline

+ +

The epicsReadline code has been reorganized to allow the commandline history +editor to be disabled at runtime. The EPICS_COMMANDLINE_LIBRARY build setting +still selects the preferred editor, but the new IOCSH_HISTEDIT_DISABLE +environment variable can be set at runtime to disable history editing and make +the IOC or other program use the basic editor instead. This is useful when +starting and controlling an IOC from another program through its stdin and +stdout streams since history editors often insert invisible escape codes into +the stdout stream, making it hard to parse.

+

Callback subsystem API

Added a new macro callbackGetPriority(prio, callback) to the diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h index b2a594f44..c5b947d57 100644 --- a/src/libCom/env/envDefs.h +++ b/src/libCom/env/envDefs.h @@ -69,6 +69,7 @@ epicsShareExtern const ENV_PARAM EPICS_CMD_PROTO_PORT; epicsShareExtern const ENV_PARAM EPICS_AR_PORT; epicsShareExtern const ENV_PARAM IOCSH_PS1; epicsShareExtern const ENV_PARAM IOCSH_HISTSIZE; +epicsShareExtern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; epicsShareExtern const ENV_PARAM *env_param_list[]; diff --git a/src/libCom/osi/epicsReadline.c b/src/libCom/osi/epicsReadline.c new file mode 100644 index 000000000..7cc569014 --- /dev/null +++ b/src/libCom/osi/epicsReadline.c @@ -0,0 +1,149 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "envDefs.h" +#include "epicsReadline.h" + +#define EPICS_COMMANDLINE_LIBRARY_EPICS 0 +#define EPICS_COMMANDLINE_LIBRARY_LIBTECLA 1 +#define EPICS_COMMANDLINE_LIBRARY_READLINE 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_CURSES 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_NCURSES 2 + +#ifndef EPICS_COMMANDLINE_LIBRARY +# define EPICS_COMMANDLINE_LIBRARY EPICS_COMMANDLINE_LIBRARY_EPICS +#endif + +struct osdContext; +struct readlineContext { + FILE *in; + char *line; + struct osdContext *osd; +}; + +static void osdReadlineBegin(struct readlineContext *); +static char * osdReadline(const char *prompt, struct readlineContext *); +static void osdReadlineEnd(struct readlineContext *); + +#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_EPICS + +static void osdReadlineBegin(struct readlineContext * c) {} +static char * osdReadline(const char *prompt, struct readlineContext * c) { return NULL; } +static void osdReadlineEnd(struct readlineContext * c) {} + +#else + +# if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE +# include "gnuReadline.c" +# else +# include "osdReadline.c" +# endif + +#endif + +/* + * Create a command-line context + */ +void * epicsShareAPI +epicsReadlineBegin(FILE *in) +{ + struct readlineContext *readlineContext = malloc(sizeof *readlineContext); + + if (readlineContext) { + readlineContext->in = in; + readlineContext->line = NULL; + if (!envGetConfigParamPtr(&IOCSH_HISTEDIT_DISABLE)) + osdReadlineBegin(readlineContext); + } + return readlineContext; +} + +/* + * Read a line of input + */ +char * epicsShareAPI +epicsReadline (const char *prompt, void *context) +{ + struct readlineContext *readlineContext = context; + FILE *in; + char *line; + int c; /* char is unsigned on some archs, EOF is -ve */ + int linelen = 0; + int linesize = 50; + + if (readlineContext->osd) + return osdReadline(prompt, readlineContext); + + free(readlineContext->line); + readlineContext->line = NULL; + if ((in = readlineContext->in) == NULL) { + in = stdin; + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + } + line = (char *)malloc(linesize); + if (line == NULL) { + printf("Out of memory!\n"); + return NULL; + } + while ((c = getc(in)) != '\n') { + if (c == EOF) { + if (ferror(in)) { + if ((errno == EINTR) || (errno == EPIPE)) { + clearerr(in); + continue; + } + } + free (line); + return NULL; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc(line, linesize); + if (cp == NULL) { + printf("Out of memory!\n"); + free(line); + return NULL; + } + line = cp; + } + line[linelen++] = c; + } + line[linelen] = '\0'; + readlineContext->line = line; + return line; +} + +/* + * Destroy a command-line context + */ +void epicsShareAPI +epicsReadlineEnd (void *context) +{ + if (context) { + struct readlineContext *readlineContext = context; + + if (readlineContext->osd) + osdReadlineEnd(readlineContext); + else + free(readlineContext->line); + free(readlineContext); + } +} + diff --git a/src/libCom/osi/os/default/epicsReadline.h b/src/libCom/osi/epicsReadline.h similarity index 66% rename from src/libCom/osi/os/default/epicsReadline.h rename to src/libCom/osi/epicsReadline.h index f6ddf2791..1d0e8b761 100644 --- a/src/libCom/osi/os/default/epicsReadline.h +++ b/src/libCom/osi/epicsReadline.h @@ -1,14 +1,13 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -#ifndef _EPICS_READLINE_H -#define _EPICS_READLINE_H +#ifndef INC_epicsReadline_H +#define INC_epicsReadline_H #ifdef __cplusplus extern "C" { @@ -25,4 +24,4 @@ epicsShareFunc void epicsShareAPI epicsReadlineEnd (void *context); } #endif -#endif /* _EPICS_READLINE_H */ +#endif /* INC_epicsReadline_H */ diff --git a/src/libCom/osi/os/RTEMS/osdReadline.c b/src/libCom/osi/os/RTEMS/osdReadline.c new file mode 100644 index 000000000..6f1e6732c --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdReadline.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +struct osdContext {}; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin (struct readlineContext *context) +{ + GetLine *gl; + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) + i = 0; + + gl = new_GetLine(200, i * 40); + if (gl) { + context->osd = (struct osdContext *) gl; + if (context->in) + gl_change_terminal(gl, context->in, stdout, NULL); + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + GetLine *gl = (GetLine *) context->osd; + char *line; + + line = gl_get_line(gl, prompt ? prompt : "", NULL, -1); + if (line) { + char *nl = strchr(line, '\n'); + + if (nl) + *nl = '\0'; + return line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd(struct readlineContext *context) +{ + GetLine *gl = (GetLine *) context->osd; + + del_GetLine(gl); +} + diff --git a/src/libCom/osi/os/default/epicsReadline.c b/src/libCom/osi/os/default/epicsReadline.c deleted file mode 100644 index 6c442e376..000000000 --- a/src/libCom/osi/os/default/epicsReadline.c +++ /dev/null @@ -1,376 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Saskatchewan -* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne -* National Laboratory. -* EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* $Revision-Id$ */ -/* Author: Eric Norum Date: 12DEC2001 */ - -#include -#include -#include - -#define epicsExportSharedSymbols -#include "envDefs.h" -#include "epicsReadline.h" - -#define EPICS_COMMANDLINE_LIBRARY_EPICS 0 -#define EPICS_COMMANDLINE_LIBRARY_LIBTECLA 1 -#define EPICS_COMMANDLINE_LIBRARY_READLINE 2 -#define EPICS_COMMANDLINE_LIBRARY_READLINE_CURSES 2 -#define EPICS_COMMANDLINE_LIBRARY_READLINE_NCURSES 2 - -#ifndef EPICS_COMMANDLINE_LIBRARY -#define EPICS_COMMANDLINE_LIBRARY EPICS_COMMANDLINE_LIBRARY_EPICS -#endif - - - -#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_LIBTECLA -#include -#include - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin (FILE *in) -{ - GetLine *gl; - long i = 50; - - envGetLongConfigParam(&IOCSH_HISTSIZE, &i); - if (i < 0) i = 0; - gl = new_GetLine(200, i * 40); - if ((gl != NULL) && (in != NULL)) - gl_change_terminal(gl, in, stdout, NULL); - return gl; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - char *line; - char *nl; - - line = gl_get_line(context, prompt ? prompt : "", NULL, -1); - if ((line != NULL) && ((nl = strchr(line, '\n')) != NULL)) - *nl = '\0'; - return line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd(void *context) -{ - del_GetLine(context); -} - - -#elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE - -#include -#include - -struct readlineContext { - FILE *in; - char *line; -}; - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin(FILE *in) -{ - struct readlineContext *readlineContext; - - readlineContext = malloc(sizeof *readlineContext); - if (readlineContext != NULL) { - readlineContext->in = in; - readlineContext->line = NULL; - if (in == NULL) { - long i = 50; - - envGetLongConfigParam(&IOCSH_HISTSIZE, &i); - if (i < 0) i = 0; - stifle_history (i); - rl_bind_key ('\t', rl_insert); - } - } - return readlineContext; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - struct readlineContext *readlineContext = context; - - int c; /* char is unsigned on some archs, EOF is -ve */ - char *line = NULL; - int linelen = 0; - int linesize = 50; - - free (readlineContext->line); - readlineContext->line = NULL; - if (readlineContext->in == NULL) { - line = readline (prompt); - } - else { - line = (char *)malloc (linesize * sizeof *line); - if (line == NULL) { - printf ("Out of memory!\n"); - return NULL; - } - if (prompt) { - fputs (prompt, stdout); - fflush (stdout); - } - while ((c = getc (readlineContext->in)) != '\n') { - if (c == EOF) { - free (line); - line = NULL; - break; - } - if ((linelen + 1) >= linesize) { - char *cp; - - linesize += 50; - cp = (char *)realloc (line, linesize * sizeof *line); - if (cp == NULL) { - printf ("Out of memory!\n"); - free (line); - line = NULL; - break; - } - line = cp; - } - line[linelen++] = c; - } - if (line) - line[linelen] = '\0'; - } - readlineContext->line = line; - if (line && line[0] != '\0') - add_history (line); - return line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd (void *context) -{ - struct readlineContext *readlineContext = context; - - if (readlineContext) { - free(readlineContext->line); - free(readlineContext); - } -} - - -#elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_EPICS - -#if defined(vxWorks) - -#include -#include -#define LEDLIB_LINESIZE 1000 - -#ifndef _WRS_VXWORKS_MAJOR -typedef int LED_ID; -#endif - -struct readlineContext { - LED_ID ledId; - char line[LEDLIB_LINESIZE]; - FILE *in; -}; - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin(FILE *in) -{ - struct readlineContext *readlineContext; - - readlineContext = malloc(sizeof *readlineContext); - if (readlineContext != NULL) { - readlineContext->ledId = (LED_ID) ERROR; - readlineContext->in = in; - if (in == NULL) { - long i = 50; - - envGetLongConfigParam(&IOCSH_HISTSIZE, &i); - if (i < 1) i = 1; - readlineContext->ledId = ledOpen(fileno(stdin), fileno(stdout), i); - if (readlineContext->ledId == (LED_ID) ERROR) { - readlineContext->in = stdin; - printf("Warning -- Unabled to allocate space for command-line history.\n"); - printf("Warning -- Command-line editting disabled.\n"); - } - } - } - return readlineContext; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - struct readlineContext *readlineContext = context; - int i; - - if (prompt) { - fputs(prompt, stdout); - fflush(stdout); - } - if (readlineContext->ledId != (LED_ID) ERROR) { - i = ledRead(readlineContext->ledId, readlineContext->line, LEDLIB_LINESIZE-1); - if (i < 0) - return NULL; - } - else { - if (fgets(readlineContext->line, LEDLIB_LINESIZE, readlineContext->in) == NULL) - return NULL; - i = strlen(readlineContext->line); - } - if ((i >= 1) && (readlineContext->line[i-1] == '\n')) - readlineContext->line[i-1] = '\0'; - else - readlineContext->line[i] = '\0'; - return readlineContext->line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd (void *context) -{ - struct readlineContext *readlineContext = context; - - if (readlineContext) { - if (readlineContext->ledId != (LED_ID) ERROR) - ledClose(readlineContext->ledId); - free(readlineContext); - } -} - -#else /* !vxWorks */ - -struct readlineContext { - FILE *in; - char *line; -}; - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin(FILE *in) -{ - struct readlineContext *readlineContext; - - readlineContext = malloc(sizeof *readlineContext); - if (readlineContext != NULL) { - readlineContext->in = in; - readlineContext->line = NULL; - } - return readlineContext; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - struct readlineContext *readlineContext = context; - - int c; /* char is unsigned on some archs, EOF is -ve */ - char *line = NULL; - int linelen = 0; - int linesize = 50; - FILE *in; - - free (readlineContext->line); - readlineContext->line = NULL; - if ((in = readlineContext->in) == NULL) { - in = stdin; - if (prompt != NULL) { - fputs (prompt, stdout); - fflush (stdout); - } - } - line = (char *)malloc (linesize * sizeof *line); - if (line == NULL) { - printf ("Out of memory!\n"); - return NULL; - } - while ((c = getc (in)) != '\n') { - if (c == EOF) { - if (ferror(in)) { - if ((errno == EINTR) || (errno == EPIPE)) { - clearerr(in); - continue; - } - } - free (line); - return NULL; - } - if ((linelen + 1) >= linesize) { - char *cp; - - linesize += 50; - cp = (char *)realloc (line, linesize * sizeof *line); - if (cp == NULL) { - printf ("Out of memory!\n"); - free (line); - return NULL; - } - line = cp; - } - line[linelen++] = c; - } - line[linelen] = '\0'; - readlineContext->line = line; - return line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd (void *context) -{ - struct readlineContext *readlineContext = context; - - if (readlineContext) { - free(readlineContext->line); - free(readlineContext); - } -} - -#endif /* !vxWorks */ - -#else - -# error "Unsupported EPICS_COMMANDLINE_LIBRARY" - -#endif diff --git a/src/libCom/osi/os/default/gnuReadline.c b/src/libCom/osi/os/default/gnuReadline.c new file mode 100644 index 000000000..d24589e77 --- /dev/null +++ b/src/libCom/osi/os/default/gnuReadline.c @@ -0,0 +1,106 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +struct osdContext {} present; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin(struct readlineContext *context) +{ + context->osd = &present; + if (context->in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) + i = 0; + stifle_history(i); + rl_bind_key('\t', rl_insert); + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + char *line; + + free(context->line); + context->line = NULL; + if (context->in == NULL) { + line = readline(prompt); + } + else { + int c; /* char is unsigned on some archs; EOF is -ve */ + int linelen = 0; + int linesize = 50; + + line = malloc(linesize); + if (line == NULL) { + printf("Out of memory!\n"); + return NULL; + } + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + while ((c = getc(context->in)) != '\n') { + if (c == EOF) { + free(line); + line = NULL; + break; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc(line, linesize); + if (cp == NULL) { + printf ("Out of memory!\n"); + free(line); + line = NULL; + break; + } + line = cp; + } + line[linelen++] = c; + } + if (line) + line[linelen] = '\0'; + } + context->line = line; + if (line && *line) + add_history(line); + return line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd (struct readlineContext *context) +{ + if (context->osd) { + free(context->line); + } +} + diff --git a/src/libCom/osi/os/vxWorks/osdReadline.c b/src/libCom/osi/os/vxWorks/osdReadline.c new file mode 100644 index 000000000..e0208893e --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdReadline.c @@ -0,0 +1,101 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +/* FIXME: Remove line-lenth limitation */ +#define LEDLIB_LINESIZE 1000 + +#ifndef _WRS_VXWORKS_MAJOR +typedef int LED_ID; +#endif + +struct osdContext { + LED_ID ledId; + char line[LEDLIB_LINESIZE]; +}; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin(struct readlineContext *context) +{ + struct osdContext osd = malloc(sizeof *osd); + + if (osd != NULL) { + osd->ledId = (LED_ID) ERROR; + if (context->in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 1) + i = 1; + + osd->ledId = ledOpen(fileno(stdin), fileno(stdout), i); + if (osd->ledId == (LED_ID) ERROR) { + context->in = stdin; + printf("Warning -- Unabled to allocate space for command-line history.\n"); + printf("Warning -- Command-line editting disabled.\n"); + } + } + context->osd = osd; + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + struct osdContext *osd = context->osd; + int i; + + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + if (osd->ledId != (LED_ID) ERROR) { + i = ledRead(osd->ledId, osd->line, LEDLIB_LINESIZE-1); + if (i < 0) + return NULL; + } + else { + if (fgets(osd->line, LEDLIB_LINESIZE, context->in) == NULL) + return NULL; + i = strlen(osd->line); + } + if ((i >= 1) && (osd->line[i-1] == '\n')) + osd->line[i-1] = '\0'; + else + osd->line[i] = '\0'; + return osd->line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd (struct readlineContext *context) +{ + LED_ID ledId = context->osd->ledId; + + if (ledId != (LED_ID) ERROR) + ledClose(ledId); + free(context->osd); +} + From f6cdbe2693d4cb3ac4335fd7fbb017409980eb0b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 6 Jul 2015 11:26:35 -0500 Subject: [PATCH 088/204] tools: Support for DB files, added dbExpand.pl --- src/tools/DBD.pm | 17 +++- src/tools/DBD/Base.pm | 31 +++---- src/tools/DBD/Breaktable.pm | 12 ++- src/tools/DBD/Device.pm | 2 - src/tools/DBD/Menu.pm | 4 +- src/tools/DBD/Output.pm | 34 +++++++- src/tools/DBD/Parser.pm | 159 +++++++++++++++++++++++++--------- src/tools/DBD/Recfield.pm | 2 - src/tools/DBD/Record.pm | 123 ++++++++++++++++++++++++++ src/tools/DBD/Variable.pm | 6 +- src/tools/EPICS/macLib.pm | 3 +- src/tools/Makefile | 2 + src/tools/dbExpand.pl | 88 +++++++++++++++++++ src/tools/dbdExpand.pl | 8 +- src/tools/dbdReport.pl | 6 +- src/tools/dbdToMenuH.pl | 0 src/tools/dbdToRecordtypeH.pl | 0 src/tools/test/Device.plt | 4 +- src/tools/test/Menu.plt | 4 +- 19 files changed, 420 insertions(+), 85 deletions(-) create mode 100644 src/tools/DBD/Record.pm create mode 100644 src/tools/dbExpand.pl mode change 100755 => 100644 src/tools/dbdExpand.pl mode change 100755 => 100644 src/tools/dbdReport.pl mode change 100755 => 100644 src/tools/dbdToMenuH.pl mode change 100755 => 100644 src/tools/dbdToRecordtypeH.pl diff --git a/src/tools/DBD.pm b/src/tools/DBD.pm index 3189588c3..3a2a85ee4 100644 --- a/src/tools/DBD.pm +++ b/src/tools/DBD.pm @@ -1,11 +1,15 @@ package DBD; +use strict; +use warnings; + use DBD::Base; use DBD::Breaktable; use DBD::Driver; use DBD::Menu; use DBD::Recordtype; use DBD::Recfield; +use DBD::Record; use DBD::Registrar; use DBD::Function; use DBD::Variable; @@ -20,6 +24,7 @@ sub new { 'DBD::Function' => {}, 'DBD::Menu' => {}, 'DBD::Recordtype' => {}, + 'DBD::Record' => {}, 'DBD::Registrar' => {}, 'DBD::Variable' => {}, 'COMMENTS' => [], @@ -30,12 +35,12 @@ sub new { } sub add { - my ($this, $obj) = @_; + my ($this, $obj, $obj_name) = @_; my $obj_class = ref $obj; confess "DBD::add: Unknown DBD object type '$obj_class'" unless $obj_class =~ m/^DBD::/ and exists $this->{$obj_class}; - my $obj_name = $obj->name; + $obj_name = $obj->name unless defined $obj_name; if (exists $this->{$obj_class}->{$obj_name}) { return if $obj->equals($this->{$obj_class}->{$obj_name}); dieContext("A different $obj->{WHAT} named '$obj_name' already exists"); @@ -95,6 +100,14 @@ sub recordtype { return $this->{'DBD::Recordtype'}->{$rtyp_name}; } +sub records { + return shift->{'DBD::Record'}; +} +sub record { + my ($this, $record_name) = @_; + return $this->{'DBD::Record'}->{$record_name}; +} + sub registrars { return shift->{'DBD::Registrar'}; } diff --git a/src/tools/DBD/Base.pm b/src/tools/DBD/Base.pm index b0388d725..e258e650a 100644 --- a/src/tools/DBD/Base.pm +++ b/src/tools/DBD/Base.pm @@ -2,17 +2,21 @@ package DBD::Base; +use strict; +use warnings; + use Carp; require Exporter; -@ISA = qw(Exporter); -@EXPORT = qw(&pushContext &popContext &dieContext &warnContext &is_reserved - &identifier &unquote &escapeCcomment &escapeCstring $RXident $RXname - $RXuint $RXint $RXhex $RXoct $RXuintx $RXintx $RXnum $RXdqs $RXsqs $RXstr); +our @ISA = qw(Exporter); + +our @EXPORT = qw(&pushContext &popContext &dieContext &warnContext &is_reserved + &escapeCcomment &escapeCstring $RXident $RXname $RXuint $RXint $RXhex $RXoct + $RXuintx $RXintx $RXnum $RXdqs $RXsqs $RXstr); our $RXident = qr/ [a-zA-Z] [a-zA-Z0-9_]* /x; -our $RXname = qr/ [a-zA-Z0-9_\-:.<>;]+ /x; +our $RXname = qr/ [a-zA-Z0-9_\-:.\[\]<>;]+ /x; our $RXhex = qr/ (?: 0 [xX] [0-9A-Fa-f]+ ) /x; our $RXoct = qr/ 0 [0-7]* /x; our $RXuint = qr/ \d+ /x; @@ -20,8 +24,8 @@ our $RXint = qr/ -? $RXuint /ox; our $RXuintx = qr/ ( $RXhex | $RXoct | $RXuint ) /ox; our $RXintx = qr/ ( $RXhex | $RXoct | $RXint ) /ox; our $RXnum = qr/ -? (?: \d+ | \d* \. \d+ ) (?: [eE] [-+]? \d+ )? /x; -our $RXdqs = qr/" (?: [^"] | \\" )* " /x; -our $RXsqs = qr/' (?: [^'] | \\' )* ' /x; +our $RXdqs = qr/ " (?: [^"] | \\" )* " /x; +our $RXsqs = qr/ ' (?: [^'] | \\' )* ' /x; our $RXstr = qr/ ( $RXname | $RXnum | $RXdqs | $RXsqs ) /ox; our @context; @@ -51,14 +55,6 @@ sub warnContext { } -# Input checking - -sub unquote (\$) { - my ($s) = @_; - $$s =~ s/^"(.*)"$/$1/o; - return $$s; -} - # Reserved words from C++ and the DB/DBD file parser my %reserved = map { $_ => undef } qw(and and_eq asm auto bitand bitor bool break case catch char class compl const const_cast continue default delete @@ -75,8 +71,7 @@ sub is_reserved { } sub identifier { - my ($id, $what) = @_; - unquote $id; + my ($this, $id, $what) = @_; confess "DBD::Base::identifier: $what undefined!" unless defined $id; $id =~ m/^$RXident$/o or dieContext("Illegal $what '$id'", @@ -115,7 +110,7 @@ sub new { sub init { my ($this, $name, $what) = @_; - $this->{NAME} = identifier($name, "$what name"); + $this->{NAME} = $this->identifier($name, "$what name"); $this->{WHAT} = $what; return $this; } diff --git a/src/tools/DBD/Breaktable.pm b/src/tools/DBD/Breaktable.pm index eb7bea6c9..c14ab8d65 100644 --- a/src/tools/DBD/Breaktable.pm +++ b/src/tools/DBD/Breaktable.pm @@ -9,6 +9,7 @@ sub init { $this->SUPER::init($name, "breakpoint table"); $this->{POINT_LIST} = []; $this->{COMMENTS} = []; + $this->{POD} = []; return $this; } @@ -18,8 +19,6 @@ sub add_point { unless defined $raw; confess "DBD::Breaktable::add_point: Engineering value undefined!" unless defined $eng; - unquote $raw; - unquote $eng; push @{$this->{POINT_LIST}}, [$raw, $eng]; } @@ -41,6 +40,15 @@ sub comments { return @{shift->{COMMENTS}}; } +sub add_pod { + my $this = shift; + push @{$this->{POD}}, @_; +} + +sub pod { + return @{shift->{POD}}; +} + sub equals { my ($a, $b) = @_; return $a->SUPER::equals($b) diff --git a/src/tools/DBD/Device.pm b/src/tools/DBD/Device.pm index 72072e545..5d13a9655 100644 --- a/src/tools/DBD/Device.pm +++ b/src/tools/DBD/Device.pm @@ -18,7 +18,6 @@ my %link_types = ( sub init { my ($this, $link_type, $dset, $choice) = @_; - unquote $choice; dieContext("Unknown link type '$link_type', valid types are:", sort keys %link_types) unless exists $link_types{$link_type}; $this->SUPER::init($dset, "device support (dset)"); @@ -38,7 +37,6 @@ sub choice { sub legal_addr { my ($this, $addr) = @_; my $rx = $link_types{$this->{LINK_TYPE}}; - unquote $addr; return $addr =~ m/^ $rx $/x; } diff --git a/src/tools/DBD/Menu.pm b/src/tools/DBD/Menu.pm index 65bae1526..244234f20 100644 --- a/src/tools/DBD/Menu.pm +++ b/src/tools/DBD/Menu.pm @@ -13,8 +13,7 @@ sub init { sub add_choice { my ($this, $name, $value) = @_; - $name = identifier($name, "Choice name"); - unquote $value; + $name = $this->identifier($name, "Choice name"); foreach $pair ($this->choices) { dieContext("Duplicate menu choice name '$name'") if ($pair->[0] eq $name); @@ -36,7 +35,6 @@ sub choice { sub legal_choice { my ($this, $value) = @_; - unquote $value; return exists $this->{CHOICE_INDEX}->{$value}; } diff --git a/src/tools/DBD/Output.pm b/src/tools/DBD/Output.pm index a338358b3..6e9d67b3c 100644 --- a/src/tools/DBD/Output.pm +++ b/src/tools/DBD/Output.pm @@ -1,9 +1,12 @@ package DBD::Output; +use strict; +use warnings; + require Exporter; -@ISA = qw(Exporter); -@EXPORT = qw(&OutputDBD); +our @ISA = qw(Exporter); +our @EXPORT = qw(&OutputDBD &OutputDB); use DBD; use DBD::Base; @@ -13,6 +16,7 @@ use DBD::Driver; use DBD::Menu; use DBD::Recordtype; use DBD::Recfield; +use DBD::Record; use DBD::Registrar; use DBD::Function; use DBD::Variable; @@ -28,6 +32,11 @@ sub OutputDBD { OutputBreaktables($out, $dbd->breaktables); } +sub OutputDB { + my ($out, $dbd) = @_; + OutputRecords($out, $dbd->records); +} + sub OutputMenus { my ($out, $menus) = @_; while (my ($name, $menu) = each %{$menus}) { @@ -44,7 +53,7 @@ sub OutputRecordtypes { printf $out "recordtype(%s) {\n", $name; print $out " %$_\n" foreach $recordtype->cdefs; - foreach $field ($recordtype->fields) { + foreach my $field ($recordtype->fields) { printf $out " field(%s, %s) {\n", $field->name, $field->dbf_type; while (my ($attr, $val) = each %{$field->attributes}) { @@ -98,4 +107,23 @@ sub OutputBreaktables { } } +sub OutputRecords { + my ($out, $records) = @_; + while (my ($name, $rec) = each %{$records}) { + next if $name ne $rec->name; # Alias + printf $out "record(%s, \"%s\") {\n", $rec->recordtype->name, $name; + printf $out " alias(\"%s\")\n", $_ + foreach $rec->aliases; + foreach my $recfield ($rec->recfields) { + my $field_name = $recfield->name; + my $value = $rec->get_field($field_name); + printf $out " field(%s, \"%s\")\n", $field_name, $value + if defined $value; + } + printf $out " info(\"%s\", \"%s\")\n", $_, $rec->info_value($_) + foreach $rec->info_names; + print $out "}\n"; + } +} + 1; diff --git a/src/tools/DBD/Parser.pm b/src/tools/DBD/Parser.pm index 3fbf2f99d..fa33bf26e 100644 --- a/src/tools/DBD/Parser.pm +++ b/src/tools/DBD/Parser.pm @@ -1,8 +1,12 @@ package DBD::Parser; + +use strict; +use warnings; + require Exporter; -@ISA = qw(Exporter); -@EXPORT = qw(&ParseDBD); +our @ISA = qw(Exporter); +our @EXPORT = qw(&ParseDBD); use DBD; use DBD::Base; @@ -12,6 +16,7 @@ use DBD::Driver; use DBD::Menu; use DBD::Recordtype; use DBD::Recfield; +use DBD::Record; use DBD::Registrar; use DBD::Function; use DBD::Variable; @@ -24,46 +29,72 @@ sub ParseDBD { parseCommon($dbd); if (m/\G menu \s* \( \s* $RXstr \s* \) \s* \{/oxgc) { print "Menu: $1\n" if $debug; - parse_menu($dbd, $1); + my ($menu_name) = unquote($1); + parse_menu($dbd, $menu_name); } elsif (m/\G driver \s* \( \s* $RXstr \s* \)/oxgc) { print "Driver: $1\n" if $debug; - $dbd->add(DBD::Driver->new($1)); + my ($driver_name) = unquote($1); + $dbd->add(DBD::Driver->new($driver_name)); } elsif (m/\G registrar \s* \( \s* $RXstr \s* \)/oxgc) { print "Registrar: $1\n" if $debug; - $dbd->add(DBD::Registrar->new($1)); + my ($registrar_name) = unquote($1); + $dbd->add(DBD::Registrar->new($registrar_name)); } elsif (m/\G function \s* \( \s* $RXstr \s* \)/oxgc) { print "Function: $1\n" if $debug; - $dbd->add(DBD::Function->new($1)); + my ($function_name) = unquote($1); + $dbd->add(DBD::Function->new($function_name)); } elsif (m/\G breaktable \s* \( \s* $RXstr \s* \) \s* \{/oxgc) { print "Breaktable: $1\n" if $debug; - parse_breaktable($dbd, $1); + my ($breaktable_name) = unquote($1); + parse_breaktable($dbd, $breaktable_name); } elsif (m/\G recordtype \s* \( \s* $RXstr \s* \) \s* \{/oxgc) { print "Recordtype: $1\n" if $debug; - parse_recordtype($dbd, $1); + my ($recordtype_name) = unquote($1); + parse_recordtype($dbd, $recordtype_name); + } + elsif (m/\G g?record \s* \( \s* $RXstr \s*, \s* $RXstr \s* \) \s* \{/oxgc) { + print "Record: $1, $2\n" if $debug; + my ($record_type, $record_name) = unquote($1, $2); + parse_record($dbd, $record_type, $record_name); + } + elsif (m/\G alias \s* \( \s* $RXstr \s*, \s* $RXstr \s* \)/oxgc) { + print "Alias: $1, $2\n" if $debug; + my ($record_name, $alias) = unquote($1, $2); + my $rec = $dbd->record($record_name); + dieContext("Alias '$alias' refers to unknown record '$record_name'") + unless defined $rec; + dieContext("Can't create alias '$alias', name already used") + if defined $dbd->record($alias); + $rec->add_alias($alias); + $dbd->add($rec, $alias); } elsif (m/\G variable \s* \( \s* $RXstr \s* \)/oxgc) { print "Variable: $1\n" if $debug; - $dbd->add(DBD::Variable->new($1)); + my ($variable_name) = unquote($1); + $dbd->add(DBD::Variable->new($variable_name)); } elsif (m/\G variable \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/oxgc) { print "Variable: $1, $2\n" if $debug; - $dbd->add(DBD::Variable->new($1, $2)); + my ($variable_name, $variable_type) = unquote($1, $2); + $dbd->add(DBD::Variable->new($variable_name, $variable_type)); } elsif (m/\G device \s* \( \s* $RXstr \s* , \s* $RXstr \s* , \s* $RXstr \s* , \s*$RXstr \s* \)/oxgc) { print "Device: $1, $2, $3, $4\n" if $debug; - my $rtyp = $dbd->recordtype($1); + my ($record_type, $link_type, $dset, $choice) = + unquote($1, $2, $3, $4); + my $rtyp = $dbd->recordtype($record_type); if (!defined $rtyp) { - $rtyp = DBD::Recordtype->new($1); - warn "Device using undefined record type '$1', place-holder created\n"; + $rtyp = DBD::Recordtype->new($record_type); + warn "Device using undefined record type '$record_type', place-holder created\n"; $dbd->add($rtyp); } - $rtyp->add_device(DBD::Device->new($2, $3, $4)); + $rtyp->add_device(DBD::Device->new($link_type, $dset, $choice)); } else { last unless m/\G (.*) $/moxgc; dieContext("Syntax error in '$1'"); @@ -101,6 +132,10 @@ sub parseCommon { } } +sub unquote { + return map { m/^ ("?) (.*) \1 $/ox; $2 } @_; +} + sub parsePod { pushContext("Pod markup"); my @pod; @@ -119,19 +154,20 @@ sub parsePod { } sub parse_menu { - my ($dbd, $name) = @_; - pushContext("menu($name)"); - my $menu = DBD::Menu->new($name); + my ($dbd, $menu_name) = @_; + pushContext("menu($menu_name)"); + my $menu = DBD::Menu->new($menu_name); while(1) { parseCommon($menu); if (m/\G choice \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/oxgc) { print " Menu-Choice: $1, $2\n" if $debug; - $menu->add_choice($1, $2); + my ($choice_name, $value) = unquote($1, $2); + $menu->add_choice($choice_name, $value); } elsif (m/\G \}/oxgc) { print " Menu-End:\n" if $debug; $dbd->add($menu); - popContext("menu($name)"); + popContext("menu($menu_name)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); @@ -141,23 +177,25 @@ sub parse_menu { } sub parse_breaktable { - my ($dbd, $name) = @_; - pushContext("breaktable($name)"); - my $bt = DBD::Breaktable->new($name); + my ($dbd, $breaktable_name) = @_; + pushContext("breaktable($breaktable_name)"); + my $bt = DBD::Breaktable->new($breaktable_name); while(1) { parseCommon($bt); if (m/\G point\s* \(\s* $RXstr \s* , \s* $RXstr \s* \)/oxgc) { print " Breaktable-Point: $1, $2\n" if $debug; - $bt->add_point($1, $2); + my ($raw, $eng) = unquote($1, $2); + $bt->add_point($raw, $eng); } elsif (m/\G $RXstr \s* (?: , \s*)? $RXstr (?: \s* ,)?/oxgc) { print " Breaktable-Data: $1, $2\n" if $debug; - $bt->add_point($1, $2); + my ($raw, $eng) = unquote($1, $2); + $bt->add_point($raw, $eng); } elsif (m/\G \}/oxgc) { print " Breaktable-End:\n" if $debug; $dbd->add($bt); - popContext("breaktable($name)"); + popContext("breaktable($breaktable_name)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); @@ -167,24 +205,64 @@ sub parse_breaktable { } sub parse_recordtype { - my ($dbd, $name) = @_; - pushContext("recordtype($name)"); - my $rtyp = DBD::Recordtype->new($name); + my ($dbd, $record_type) = @_; + pushContext("recordtype($record_type)"); + my $rtyp = DBD::Recordtype->new($record_type); while(1) { parseCommon($rtyp); if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \) \s* \{/oxgc) { print " Recordtype-Field: $1, $2\n" if $debug; - parse_field($rtyp, $1, $2); - } - elsif (m/\G \}/oxgc) { - print " Recordtype-End:\n" if $debug; - $dbd->add($rtyp); - popContext("recordtype($name)"); - return; + my ($field_name, $field_type) = unquote($1, $2); + parse_field($rtyp, $field_name, $field_type); } elsif (m/\G % (.*) \n/oxgc) { print " Recordtype-Cdef: $1\n" if $debug; $rtyp->add_cdef($1); + } + elsif (m/\G \}/oxgc) { + print " Recordtype-End:\n" if $debug; + $dbd->add($rtyp); + popContext("recordtype($record_type)"); + return; + } else { + m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); + dieContext("Syntax error in '$1'"); + } + } +} + +sub parse_record { + my ($dbd, $record_type, $record_name) = @_; + pushContext("record($record_type, $record_name)"); + my $rtyp = $dbd->recordtype($record_type); + dieContext("No recordtype named '$record_type'") + unless defined $rtyp; + my $rec = DBD::Record->new($rtyp, $record_name); # FIXME: Merge duplicates + while(1) { + parseCommon($rec); + if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/oxgc) { + print " Record-Field: $1, $2\n" if $debug; + my ($field_name, $value) = unquote($1, $2); + $rec->put_field($field_name, $value); + } + elsif (m/\G info \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/oxgc) { + print " Record-Info: $1, $2\n" if $debug; + my ($info_name, $value) = unquote($1, $2); + $rec->add_info($info_name, $value); + } + elsif (m/\G alias \s* \( \s* $RXstr \s* \)/oxgc) { + print " Record-Alias: $1\n" if $debug; + my ($alias) = unquote($1); + dieContext("Can't create alias '$alias', name in use") + if defined $dbd->record($1); + $rec->add_alias($alias); + $dbd->add($rec, $alias); + } + elsif (m/\G \}/oxgc) { + print " Record-End:\n" if $debug; + $dbd->add($rec); + popContext("record($record_type, $record_name)"); + return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); dieContext("Syntax error in '$1'"); @@ -193,19 +271,20 @@ sub parse_recordtype { } sub parse_field { - my ($rtyp, $name, $field_type) = @_; - my $fld = DBD::Recfield->new($name, $field_type); - pushContext("field($name, $field_type)"); + my ($rtyp, $field_name, $field_type) = @_; + my $fld = DBD::Recfield->new($field_name, $field_type); + pushContext("field($field_name, $field_type)"); while(1) { parseCommon($fld); if (m/\G (\w+) \s* \( \s* $RXstr \s* \)/oxgc) { print " Field-Attribute: $1, $2\n" if $debug; - $fld->add_attribute($1, $2); + my ($attr, $value) = unquote($1, $2); + $fld->add_attribute($attr, $value); } elsif (m/\G \}/oxgc) { print " Field-End:\n" if $debug; $rtyp->add_field($fld); - popContext("field($name, $field_type)"); + popContext("field($field_name, $field_type)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); diff --git a/src/tools/DBD/Recfield.pm b/src/tools/DBD/Recfield.pm index 6dbf1696f..d38a081fa 100644 --- a/src/tools/DBD/Recfield.pm +++ b/src/tools/DBD/Recfield.pm @@ -50,7 +50,6 @@ sub new { sub init { my ($this, $name, $type) = @_; - unquote $type; $this->SUPER::init($name, "record field"); dieContext("Illegal field type '$type', valid field types are:", sort keys %field_types) unless exists $field_types{$type}; @@ -75,7 +74,6 @@ sub number { sub add_attribute { my ($this, $attr, $value) = @_; - unquote $value; my $match = $field_attrs{$attr}; if (defined $match) { dieContext("Bad value '$value' for field attribute '$attr'") diff --git a/src/tools/DBD/Record.pm b/src/tools/DBD/Record.pm new file mode 100644 index 000000000..1b7980c2d --- /dev/null +++ b/src/tools/DBD/Record.pm @@ -0,0 +1,123 @@ +package DBD::Record; + +use strict; +use warnings; + +use DBD::Base; + +our @ISA = qw(DBD::Base); + +use Carp; + +our ($macrosOk); +my $warned; + +sub init { + my ($this, $type, $name) = @_; + confess "DBD::Record::init: Not a DBD::Recordtype" + unless $type->isa('DBD::Recordtype'); + $this->SUPER::init($name, "record"); + $this->{RECORD_TYPE} = $type; + $this->{ALIASES} = []; + $this->{RECFIELD_LIST} = []; + $this->{FIELD_INDEX} = {}; + $this->{INFO_LIST} = []; + $this->{INFO_ITEMS} = {}; + $this->{COMMENTS} = []; + $this->{POD} = []; + return $this; +} + +# Override, record names are not as strict as recordtype and menu names +sub identifier { + my ($this, $id, $what) = @_; + confess "DBD::Record::identifier: $what undefined!" + unless defined $id; + if ($macrosOk) { + # FIXME - Check name with macro + } + elsif ($id !~ m/^$RXname$/o) { + my @message; + push @message, "A $what should contain only letters, digits and these", + "special characters: _ - : . [ ] < > ;" unless $warned++; + warnContext("Deprecated $what '$id'", @message); + } + return $id; +} + +sub recordtype { + return shift->{RECORD_TYPE}; +} + +sub add_alias { + my ($this, $alias) = @_; + push @{$this->{ALIASES}}, $this->identifier($alias, "alias name"); +} + +sub aliases { + return @{shift->{ALIASES}}; +} + +sub put_field { + my ($this, $field_name, $value) = @_; + my $recfield = $this->{RECORD_TYPE}->field($field_name); + dieContext("No field named '$field_name'") + unless defined $recfield; + dieContext("Can't set $field_name to '$value'") + unless $recfield->legal_value($value); + push @{$this->{RECFIELD_LIST}}, $recfield + unless exists $this->{FIELD_INDEX}->{$field_name}; + $this->{FIELD_INDEX}->{$field_name} = $value; +} + +sub recfields { + return @{shift->{RECFIELD_LIST}}; +} + +sub field_names { # In their original order... + return map {$_->name} @{shift->{RECFIELD_LIST}}; +} + +sub get_field { + my ($this, $field_name) = @_; + return $this->{FIELD_INDEX}->{$field_name} + if exists $this->{FIELD_INDEX}->{$field_name}; + my $recfield = $this->{RECORD_TYPE}->field($field_name); + return $recfield->attribute("initial"); +} + +sub add_info { + my ($this, $info_name, $value) = @_; + push @{$this->{INFO_LIST}}, $info_name + unless exists $this->{INFO_ITEMS}->{$info_name}; + $this->{INFO_ITEMS}->{$info_name} = $value; +} + +sub info_names { + return @{shift->{INFO_LIST}}; +} + +sub info_value { + my ($this, $info_name) = @_; + return $this->{INFO_ITEMS}->{$info_name}; +} + +sub add_comment { + my ($this, $comment) = @_; + push @{$this->{COMMENTS}}, $comment; +} + +sub comments { + return @{shift->{COMMENTS}}; +} + +sub add_pod { + my $this = shift; + push @{$this->{POD}}, @_; +} + +sub pod { + return @{shift->{POD}}; +} + +1; diff --git a/src/tools/DBD/Variable.pm b/src/tools/DBD/Variable.pm index 87ea6d47a..cd1b0a334 100644 --- a/src/tools/DBD/Variable.pm +++ b/src/tools/DBD/Variable.pm @@ -10,11 +10,7 @@ my %valid_types = ( sub init { my ($this, $name, $type) = @_; - if (defined $type) { - unquote $type; - } else { - $type = "int"; - } + $type = "int" unless defined $type; exists $valid_types{$type} or dieContext("Unknown variable type '$type', valid types are:", sort keys %valid_types); diff --git a/src/tools/EPICS/macLib.pm b/src/tools/EPICS/macLib.pm index 332ceca67..9ae40314e 100644 --- a/src/tools/EPICS/macLib.pm +++ b/src/tools/EPICS/macLib.pm @@ -122,7 +122,8 @@ sub suppressWarning($$) { sub expandString($$) { my ($this, $src) = @_; $this->_expand; - my $entry = EPICS::macLib::entry->new($src, 'string'); + (my $name = $src) =~ s/^ (.{20}) .* $/$1.../xs; + my $entry = EPICS::macLib::entry->new($name, 'string'); my $result = $this->_translate($entry, 0, $src); return $result unless $entry->{error}; return $this->{noWarn} ? $result : undef; diff --git a/src/tools/Makefile b/src/tools/Makefile index 26da8f550..580e32cb6 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -29,6 +29,7 @@ PERL_MODULES += DBD/Output.pm PERL_MODULES += DBD/Parser.pm PERL_MODULES += DBD/Recfield.pm PERL_MODULES += DBD/Recordtype.pm +PERL_MODULES += DBD/Record.pm PERL_MODULES += DBD/Registrar.pm PERL_MODULES += DBD/Variable.pm @@ -49,6 +50,7 @@ PERL_SCRIPTS += useManifestTool.pl PERL_SCRIPTS += dbdToMenuH.pl PERL_SCRIPTS += dbdToRecordtypeH.pl PERL_SCRIPTS += dbdExpand.pl +PERL_SCRIPTS += dbExpand.pl PERL_SCRIPTS += dbdToHtml.pl PERL_SCRIPTS += podToHtml.pl PERL_SCRIPTS += podRemove.pl diff --git a/src/tools/dbExpand.pl b/src/tools/dbExpand.pl new file mode 100644 index 000000000..abfea833b --- /dev/null +++ b/src/tools/dbExpand.pl @@ -0,0 +1,88 @@ +#!/usr/bin/env perl + +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# $Id$ + +use strict; + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + +use DBD; +use DBD::Parser; +use DBD::Output; +use EPICS::Getopts; +use EPICS::Readfile; +use EPICS::macLib; + +our ($opt_D, @opt_I, @opt_S, $opt_o, $opt_V); + +getopts('DI@S@o:V') or + die "Usage: dbExpand [-D] [-I dir] [-S macro=val] [-o out.db] in.dbd in.db ..."; + +my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? +my $macros = EPICS::macLib->new(@opt_S); +my $dbd = DBD->new(); + +$macros->suppressWarning(!$opt_V); +$DBD::Record::macrosOk = !$opt_V; + +# Calculate filename for the dependency warning message below +my $dep = $opt_o; +my $dot_d = ''; +if ($opt_D) { + $dep =~ s{\.\./O\.Common/(.*)}{$1\$\(DEP\)}; + $dot_d = '.d'; +} else { + $dep = "\$(COMMON_DIR)/$dep"; +} + +die "dbExpand.pl: No input files for $opt_o\n" if !@ARGV; + +my $errors = 0; + +while (@ARGV) { + my $file = shift @ARGV; + eval { + &ParseDBD($dbd, &Readfile($file, $macros, \@opt_I)); + }; + if ($@) { + warn "dbExpand.pl: $@"; + my $outfile = $opt_o ? " to create '$opt_o$dot_d'" : ''; + warn " while reading '$file'$outfile\n"; + warn " Your Makefile may need this dependency rule:\n", + " $dep: \$(COMMON_DIR)/$file\n" + if $@ =~ m/Can't find file '$file'/; + ++$errors; + } +} + +if ($opt_D) { # Output dependencies only, ignore errors + my %filecount; + my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles; + print "$opt_o: ", join(" \\\n ", @uniqfiles), "\n\n"; + print map { "$_:\n" } @uniqfiles; + exit 0; +} + +die "dbExpand.pl: Exiting due to errors\n" if $errors; + +my $out; +if ($opt_o) { + open $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; +} else { + $out = *STDOUT; +} + +&OutputDB($out, $dbd); + +if ($opt_o) { + close $out or die "Closing $opt_o failed: $!\n"; +} +exit 0; diff --git a/src/tools/dbdExpand.pl b/src/tools/dbdExpand.pl old mode 100755 new mode 100644 index 96398e703..f111bd98d --- a/src/tools/dbdExpand.pl +++ b/src/tools/dbdExpand.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne @@ -9,6 +9,8 @@ # $Id$ +use strict; + use FindBin qw($Bin); use lib "$Bin/../../lib/perl"; @@ -19,6 +21,8 @@ use EPICS::Getopts; use EPICS::Readfile; use EPICS::macLib; +our ($opt_D, @opt_I, @opt_S, $opt_o); + getopts('DI@S@o:') or die "Usage: dbdExpand [-D] [-I dir] [-S macro=val] [-o out.dbd] in.dbd ..."; @@ -69,7 +73,7 @@ my $out; if ($opt_o) { open $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; } else { - $out = STDOUT; + $out = *STDOUT; } OutputDBD($out, $dbd); diff --git a/src/tools/dbdReport.pl b/src/tools/dbdReport.pl old mode 100755 new mode 100644 index 9d1bd068e..a90a9c9c0 --- a/src/tools/dbdReport.pl +++ b/src/tools/dbdReport.pl @@ -32,7 +32,7 @@ my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my $macros = EPICS::macLib->new(@opt_S); my $dbd = DBD->new(); -ParseDBD($dbd, Readfile(shift @ARGV, $macros, \@opt_I)); +ParseDBD($dbd, Readfile(shift @ARGV, $macros, \@opt_I)) while @ARGV; $Text::Wrap::columns = 75; @@ -62,3 +62,7 @@ if (%recordtypes) { if @devices; } } +my @records = sort keys %{$dbd->records}; +print wrap("Records: ", "\t", join(', ', @records)), "\n" + if @records; + diff --git a/src/tools/dbdToMenuH.pl b/src/tools/dbdToMenuH.pl old mode 100755 new mode 100644 diff --git a/src/tools/dbdToRecordtypeH.pl b/src/tools/dbdToRecordtypeH.pl old mode 100755 new mode 100644 diff --git a/src/tools/test/Device.plt b/src/tools/test/Device.plt index d362054c2..79f96bfd0 100644 --- a/src/tools/test/Device.plt +++ b/src/tools/test/Device.plt @@ -7,7 +7,7 @@ use Test::More tests => 16; use DBD::Device; -my $dev = DBD::Device->new('VME_IO', 'test', '"Device"'); +my $dev = DBD::Device->new('VME_IO', 'test', 'Device'); isa_ok $dev, 'DBD::Device'; is $dev->name, 'test', 'Device name'; is $dev->link_type, 'VME_IO', 'Link type'; @@ -27,7 +27,7 @@ my %dev_addrs = ( INST_IO => '@Anything' ); while (my ($link, $addr) = each(%dev_addrs)) { - $dev->init($link, 'test', '"Device"'); + $dev->init($link, 'test', 'Device'); ok $dev->legal_addr($addr), "$link address"; } diff --git a/src/tools/test/Menu.plt b/src/tools/test/Menu.plt index f8da94b97..97305b8a9 100644 --- a/src/tools/test/Menu.plt +++ b/src/tools/test/Menu.plt @@ -11,11 +11,11 @@ my $menu = DBD::Menu->new('test'); isa_ok $menu, 'DBD::Menu'; is $menu->name, 'test', 'Menu name'; is $menu->choices, 0, 'Choices == zero'; -$menu->add_choice('ch1', '"Choice 1"'); +$menu->add_choice('ch1', 'Choice 1'); is $menu->choices, 1, 'First choice added'; ok $menu->legal_choice('Choice 1'), 'First choice legal'; is_deeply $menu->choice(0), ['ch1', 'Choice 1'], 'First choice found'; -$menu->add_choice('ch2', '"Choice 2"'); +$menu->add_choice('ch2', 'Choice 2'); is $menu->choices, 2, 'Second choice added'; ok $menu->legal_choice('Choice 1'), 'First choice still legal'; is_deeply $menu->choice(0), ['ch1', 'Choice 1'], 'First choice still found'; From 9f0d34656a04602a99f3b1446a267c9fefaf4283 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 6 Jul 2015 11:27:03 -0500 Subject: [PATCH 089/204] Remove old libCom HTML files --- src/libCom/freeList/Makefile | 3 +- src/libCom/freeList/freeList.html | 85 ------------------ src/libCom/gpHash/Makefile | 3 +- src/libCom/gpHash/gpHash.html | 139 ------------------------------ 4 files changed, 4 insertions(+), 226 deletions(-) delete mode 100644 src/libCom/freeList/freeList.html delete mode 100644 src/libCom/gpHash/gpHash.html diff --git a/src/libCom/freeList/Makefile b/src/libCom/freeList/Makefile index f5afa3de5..3a1e25182 100644 --- a/src/libCom/freeList/Makefile +++ b/src/libCom/freeList/Makefile @@ -8,6 +8,7 @@ # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/freeList + INC += freeList.h + Com_SRCS += freeListLib.c -HTMLS += freeList/freeList.html diff --git a/src/libCom/freeList/freeList.html b/src/libCom/freeList/freeList.html deleted file mode 100644 index 853cf9d35..000000000 --- a/src/libCom/freeList/freeList.html +++ /dev/null @@ -1,85 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ - - -

-
-
-
-

VERSION $Id: freeList.3,v 1.2 1997/04/10 19:47:35

-
-
-

NAME

-     freeList.c - General Purpose memory free list library
-
-
-
-

SYNOPSIS

-     freeListInitPvt     - Initialize a free list
-     freeListCalloc - Allocate and initialize to zero a new element
-     freeListMalloc - Allocate a new element
-     freeListFree   - Free an element,i.e. put on free list
-
-
-     void freeListInitPvt(void **ppvt,int size,int nmalloc);
-     void *freeListCalloc(void *pvt);
-     void *freeListMalloc(void *pvt);
-     size_t freeListItemsAvail(void *pvt);
-     void freeListFree(void *pvt,void*pmem);
-
-     where :
-
-     pvt  - For private use by library. Caller must provide a "void *pvt"
-     size - Size in butes of each element. Note that all elements must be same size
-     nmalloc   - Number of elements top allocate when regular malloc must be called.
-
-
-
-
-

DESCRIPTION

-     This library can be used to allocate  and  free  fixed  size
-     memory  elements.   Free  elements  are maintained on a free
-     list rather then being returned to the  heap  via  calls  to
-     free.  When  it  is  necessary to call malloc, memory can be
-     allocated in multiples of the element size.
-
-
-
-

RETURNS

-     freeListCalloc and freeListMalloc return address of element allocated
-     or NULL if no more memory could be obtained via call to malloc
-
-
-
-

INCLUDES

-     freeLib.h
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Man(1) output converted with -man2html -
- - diff --git a/src/libCom/gpHash/Makefile b/src/libCom/gpHash/Makefile index cc6b6a5f4..6fcd3f8a7 100644 --- a/src/libCom/gpHash/Makefile +++ b/src/libCom/gpHash/Makefile @@ -8,6 +8,7 @@ # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/gpHash + INC += gpHash.h + Com_SRCS += gpHashLib.c -HTMLS += gpHash/gpHash.html diff --git a/src/libCom/gpHash/gpHash.html b/src/libCom/gpHash/gpHash.html deleted file mode 100644 index 658e47f47..000000000 --- a/src/libCom/gpHash/gpHash.html +++ /dev/null @@ -1,139 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ - - -
-
-
-
-

VERSION $Id: gpHash.3,v 1.2 1999/09/13 19:18:04

-
-
-

NAME

-     gpHash.c - General Purpose Hash Library
-
-
-
-

SYNOPSIS

-     gphInitPvt     - Initialize
-     gphFind        - Find an element taht has been hashed
-     gphAdd         - Add a new entry
-     gphDelete - Delete an entry
-     gphFreeMem     - Free all memory allocated by gpHash
-     gphDump        - Dump current members
-
-
-     typedef struct{
-         ELLNODE     node;
-         char        *name;          /*address of name placed in directory*/
-         void        *pvtid;         /*private name for subsystem user*/
-         void        *userPvt;       /*private for user*/
-     } GPHENTRY;
-
-     void    gphInitPvt(void **ppvt);
-     GPHENTRY *gphFind(void *pvt,const char *name,void *pvtid);
-     GPHENTRY *gphAdd(void *pvt,const char *name,void *pvtid);
-     void gphDelete(void *pvt,const char *name,void *pvtid);
-     void gphFreeMem(void *pvt);
-     void gphDump(void *pvt);
-
-
-     where :
-
-     pvt  - For private use by library. Caller must provide a "void *pvt"
-     name - The character string that will be hashed and added to table
-     pvtid     - The name plus value of this pointer constitute unique entry
-
-
-
-
-

DESCRIPTION

-     This library provides a general purpose directory  of  names
-     that  is  accessed via a hash table. The hash table contains
-     256 entries. Each entry is a list of members  that  hash  to
-     the  same  value. The user can maintain seperate directories
-     via the same table by having  a  different  pvtid  for  each
-     directory.
-
-
-
-

RETURNS

-     gphFind returns the address of the GPHENTRY describing the entry or NULL if name was not found.
-     gphAdd returns the address of the new GPHENTRY describing the entry or NULL if name was already
-     present.
-
-
-
-

INCLUDES

-     gpHash.h
-
-
-
-
-

REFERENCE

-     Fast Hashing of Variable Length Text Strings, Peter K. Pear-
-     son, Communications of the ACM, June 1990
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Man(1) output converted with -man2html -
- - From 60823bd2fb2915301354ed3bec193095b260c9df Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 8 Jul 2015 16:46:34 -0500 Subject: [PATCH 090/204] Some edits to the Perl script --- src/tools/genVersionHeader.pl | 121 ++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 51 deletions(-) diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index 28ae666e6..43bec8d80 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -1,128 +1,147 @@ #!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2014 Brookhaven National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* # # Generate a C header file which # defines a macro with a string # describing the VCS revision # +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + use EPICS::Getopts; use POSIX qw(strftime); use strict; -our($opt_v,$opt_t,$opt_N,$opt_V); +# RFC 8601 date+time w/ zone (eg "2014-08-29T09:42:47-0700") +my $tfmt = '%Y-%m-%dT%H:%M:%S'; +$tfmt .= '%z' unless $^O eq 'MSWin32'; # %z returns zone name on Windows + +our ($opt_h, $opt_v); +our $opt_t = '.'; +our $opt_N = 'VCSVERSION'; +our $opt_V = strftime($tfmt, localtime); -$opt_N = "MODVERSION"; -$opt_t = "."; my $foundvcs = 0; my $result; -getopts("vt:N:V:") or - die "Usage: genVersionHeader.pl [-t top] [-N NAME] [-V VERSION] output.h"; +getopts('hvt:N:V:') && @ARGV == 1 + or HELP_MESSAGE(); my ($outfile) = @ARGV; -chomp($opt_V); -if(!$opt_V) { - # RFC 8601 date+time w/ zone (eg "2014-08-29T09:42:47-0700") - $opt_V = strftime "%Y-%m-%dT%H:%M:%S%z", localtime; -} - -if(!$foundvcs && -d "$opt_t/_darcs") { # Darcs +if (!$foundvcs && -d "$opt_t/_darcs") { # Darcs # v1-4-dirty # is tag 'v1' plus 4 patches # with uncommited modifications $result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`; - chomp($result); - if(!$? && length($result)>1) { + chomp $result; + if (!$? && length($result) > 1) { $opt_V = $result; $foundvcs = 1; # see if working copy has modifications, additions, removals, or missing files my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`; - if(!$?) { - $opt_V = "$opt_V-dirty"; - } + $opt_V .= '-dirty' unless $?; } } -if(!$foundvcs && -d "$opt_t/.hg") { # Mercurial +if (!$foundvcs && -d "$opt_t/.hg") { # Mercurial # v1-4-abcdef-dirty # is 4 commits after tag 'v1' with short hash abcdef # with uncommited modifications $result = `hg --cwd "$opt_t" tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`; - chomp($result); - if(!$? && length($result)>1) { + chomp $result; + if (!$? && length($result)>1) { $opt_V = $result; $foundvcs = 1; # see if working copy has modifications, additions, removals, or missing files my $hasmod = `hg --cwd "$opt_t" status -m -a -r -d`; - chomp($hasmod); - if(length($hasmod)>0) { - $opt_V = "$opt_V-dirty"; - } + chomp $hasmod; + $opt_V .= '-dirty' if length($hasmod) > 0; } } -if(!$foundvcs && -d "$opt_t/.git") { +if (!$foundvcs && -d "$opt_t/.git") { # same format as Mercurial $result = `git --git-dir="$opt_t/.git" describe --tags --dirty`; - chomp($result); - if(!$? && length($result)>1) { + chomp $result; + if (!$? && length($result) > 1) { $opt_V = $result; $foundvcs = 1; } } -if(!$foundvcs && -d "$opt_t/.svn") { +if (!$foundvcs && -d "$opt_t/.svn") { # 12345 $result = `cd "$opt_t" && svn info --non-interactive`; - chomp($result); - if(!$? && $result =~ /^Revision:\s*(\d+)/m) { + chomp $result; + if (!$? && $result =~ /^Revision:\s*(\d+)/m) { $opt_V = $1; $foundvcs = 1; # see if working copy has modifications, additions, removals, or missing files my $hasmod = `cd "$opt_t" && svn status -q --non-interactive`; - chomp($hasmod); - if(length($hasmod)>0) { - $opt_V = "$opt_V-dirty"; - } + chomp $hasmod; + $opt_V .= '-dirty' if length($hasmod) > 0; } } -if(!$foundvcs && -d "$opt_t/.bzr") { +if (!$foundvcs && -d "$opt_t/.bzr") { # 12444-anj@aps.anl.gov-20131003210403-icfd8mc37g8vctpf $result = `cd "$opt_t" && bzr version-info -q --custom --template="{revno}-{revision_id}"`; - chomp($result); - print "BZR $result"; - if(!$? && length($result)>1) { + chomp $result; + if (!$? && length($result)>1) { $opt_V = $result; $foundvcs = 1; # see if working copy has modifications, additions, removals, or missing files # unfortunately "bzr version-info --check-clean ..." doesn't seem to work as documented my $hasmod = `cd "$opt_t" && bzr status -SV`; - chomp($hasmod); - if(length($hasmod)>0) { - $opt_V = "$opt_V-dirty"; - } + chomp $hasmod; + $opt_V .= '-dirty' if length($hasmod) > 0; } } -my $output = "#ifndef $opt_N\n# define $opt_N \"$opt_V\"\n#endif\n"; +my $output = << "__END"; +/* Generated file, do not edit! */ + +#ifndef $opt_N +# define $opt_N \"$opt_V\" +#endif +__END print "== would\n$output" if $opt_v; my $DST; -if(open($DST, '+<', $outfile)) { - my $actual = join("", <$DST>); +if (open($DST, '+<', $outfile)) { + my $actual = join('', <$DST>); print "== have\n$actual" if $opt_v; - if($actual eq $output) { + if ($actual eq $output) { print "Keeping existing VCS version header $outfile with \"$opt_V\"\n"; - exit(0) + exit 0; } print "Updating VCS version header $outfile with \"$opt_V\"\n"; } else { print "Creating VCS version header $outfile with \"$opt_V\"\n"; - open($DST, '>', $outfile) or die "Unable to open or create VCS version header $outfile"; + open($DST, '>', $outfile) + or die "Unable to open or create VCS version header $outfile"; } -seek($DST,0,0); -truncate($DST,0); +seek $DST, 0, 0; +truncate $DST, 0; print $DST $output; +close $DST; + +sub HELP_MESSAGE { + print STDERR < Date: Thu, 9 Jul 2015 13:34:37 -0700 Subject: [PATCH 091/204] fill zeros into buffer when BALG is changed to LIFO from FIFO. --- src/std/rec/compressRecord.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/std/rec/compressRecord.c b/src/std/rec/compressRecord.c index 84a93f82f..c0feaacfb 100644 --- a/src/std/rec/compressRecord.c +++ b/src/std/rec/compressRecord.c @@ -92,6 +92,8 @@ static void reset(compressRecord *prec) if (prec->alg == compressALG_Average && prec->sptr == 0){ prec->sptr = calloc(prec->nsam, sizeof(double)); } + + if(prec->bptr && prec->nsam) memset(prec->bptr, 0, prec->nsam * sizeof(double)); } static void monitor(compressRecord *prec) From 8f3fcc2787c0418d191c28c31c0477b8d8c74aac Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 9 Jul 2015 17:07:49 -0400 Subject: [PATCH 092/204] dbLock: fix initialization of self links initPVLinks() doesn't correctly handle case where a record links to itself. This results it being added twice to lockRecordList, which corrupts the list count, and the lockset ref. counter. The error appears in dbLockCleanupRecords() when not all locksets are free'd. --- src/ioc/db/dbLock.c | 48 +++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index df2924a70..200fdf280 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -526,6 +526,11 @@ static int initPVLinks(void* junk, DBENTRY* pdbentry) dbCommon *prec = pdbentry->precnode->precord; lockSet *A=prec->lset->plockSet; + if(!A) { + A = prec->lset->plockSet = makeSet(); + ellAdd(&A->lockRecordList, &prec->lset->node); + } + /* for each link originating from this record */ for(i=0; ino_links; i++) { DBADDR *paddr; @@ -544,51 +549,31 @@ static int initPVLinks(void* junk, DBENTRY* pdbentry) paddr = (DBADDR*)plink->value.pv_link.pvt; B = paddr->precord->lset->plockSet; + if(A==B) { + /* these records are already in the same lockset */ + } else if(B) { + assert(A!=B); + dbLockSetMerge(NULL, prec, paddr->precord); + assert(prec->lset->plockSet==paddr->precord->lset->plockSet); - /* Initial population of lockSets happens here. */ - if(!A && !B) { /* neither side has a lockSet */ - A = prec->lset->plockSet = paddr->precord->lset->plockSet = makeSet(); - dbLockIncRef(A); /* ref for psecond */ - ellAdd(&A->lockRecordList, &prec->lset->node); - ellAdd(&A->lockRecordList, &paddr->precord->lset->node); - - } else if(!B) { /* fast merge paddr->precord into A */ + } else { + /* fast merge paddr->precord into A */ paddr->precord->lset->plockSet = A; dbLockIncRef(A); ellAdd(&A->lockRecordList, &paddr->precord->lset->node); - - } else if(!A) { /* fast merge prec into B */ - A = prec->lset->plockSet = B; - dbLockIncRef(B); - ellAdd(&B->lockRecordList, &prec->lset->node); } } return 0; } -static int initSingleSets(void* junk, DBENTRY* pdbentry) -{ - dbCommon *prec = pdbentry->precnode->precord; - lockSet *ls; - - if(!prec->lset->plockSet) { - ls = prec->lset->plockSet = makeSet(); - ellAdd(&ls->lockRecordList, &prec->lset->node); - } - - return 0; -} - void dbLockInitRecords(dbBase *pdbbase) { epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL); /* create all lockRecords */ forEachRecord(NULL, pdbbase, &createLockRecord); - /* create lockSets for pairs of records with DB_LINKs */ + /* create lockSets */ forEachRecord(NULL, pdbbase, &initPVLinks); - /* create lockSets for all records with no DB_LINKs */ - forEachRecord(NULL, pdbbase, &initSingleSets); } static int freeLockRecord(void* junk, DBENTRY* pdbentry) @@ -598,6 +583,7 @@ static int freeLockRecord(void* junk, DBENTRY* pdbentry) lockSet *ls = lr->plockSet; prec->lset = NULL; + lr->precord = NULL; assert(ls->refcount>0); assert(ellCount(&ls->lockRecordList)>0); @@ -639,6 +625,8 @@ void dbLockCleanupRecords(dbBase *pdbbase) /* Caller must lock both pfirst and psecond. * Assumes that pfirst has been modified * to link to psecond. + * + * Note: locker may be NULL (during iocInit) */ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) { @@ -660,7 +648,7 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) cantProceed(NULL); } #endif - if(A->ownerlocker!=locker || B->ownerlocker!=locker) { + if(locker && (A->ownerlocker!=locker || B->ownerlocker!=locker)) { errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n", locker, pfirst->name, psecond->name, A->ownerlocker, B->ownerlocker, locker); From 9198428619451d160ff17628f3d31f1121ee5e61 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 9 Jul 2015 17:16:39 -0400 Subject: [PATCH 093/204] dbLock: minor must match following condition --- src/ioc/db/dbLock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 200fdf280..b248cba46 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -641,7 +641,7 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) assert(A && B); #ifdef LOCKSET_DEBUG - if(A->owner!=myself || B->owner!=myself) { + if(locker && (A->owner!=myself || B->owner!=myself)) { errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n", locker, pfirst->name, psecond->name, A->owner, B->owner, myself); From 837111296e8e328d378636ba9219c454b02493bb Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 10 Jul 2015 12:09:37 -0500 Subject: [PATCH 094/204] Updates to build rules and genVersionHeader.pl script Added -q (quiet) flag, renamed INSTALL_QUIETLY build variable. Moved GENVERSION variables into normal locations. Changed from :: rule to : rule with FORCE dependency. Removed use of --git-dir, only works when CWD is TOP. Simplified some parts, more perlish. Added VCS used to generated output file. Expanded on the -v (verbose) output. --- configure/CONFIG_BASE | 4 +- configure/CONFIG_COMMON | 9 ++ configure/RULES_BUILD | 13 +- src/template/base/top/exampleApp/src/Makefile | 5 +- src/tools/genVersionHeader.pl | 118 +++++++++++------- 5 files changed, 87 insertions(+), 62 deletions(-) diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index e416f99db..6eac3b2e0 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -63,11 +63,11 @@ DBTOMENUH = $(PERL) $(TOOLS)/dbdToMenuH.pl REGISTERRECORDDEVICEDRIVER = $(PERL) $(TOOLS)/registerRecordDeviceDriver.pl CONVERTRELEASE = $(PERL) $(TOOLS)/convertRelease.pl FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl +GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) #------------------------------------------------------- # tools for installing libraries and products -INSTALL_QUIETLY := $(if $(findstring s,$(MAKEFLAGS)),-q,) -INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(INSTALL_QUIETLY) +INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG) INSTALL_PRODUCT = $(INSTALL) INSTALL_LIBRARY = $(INSTALL) diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 584175784..9e82b13b0 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -85,6 +85,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) # Make echo output - suppress echoing if make's '-s' flag is set NOP = : ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) +QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,) #------------------------------------------------------- ifdef T_A @@ -327,6 +328,14 @@ COMPILE.cpp = $(CCC) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) # C preprocessor command PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(INCLUDES) $< > $@ +#-------------------------------------------------- +# genVersion header defaults + +# C macro name +GENVERSIONMACRO = VCSVERSION +# C macro default value (empty to use date+time) +GENVERSIONDEFAULT = + #-------------------------------------------------- # Header dependency file generation diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 2b836ce2c..7ec79d569 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -363,15 +363,8 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES) # Generate header with version number from VCS ifneq ($(GENVERSION),) -GENERATEVERSIONHEADERCMD ?= $(PERL) $(TOOLS)/genVersionHeader.pl -# C macro name -GENVERSIONMACRO ?= MODULEVERSION -# C macro default (empty uses date+time) -#GENVERSIONDEFAULT = defaultversionnumber - -$(GENVERSION):: - $(GENERATEVERSIONHEADERCMD) -t "$(TOP)" -V "$(GENVERSIONDEFAULT)" -N "$(GENVERSIONMACRO)" $@ - +$(COMMON_DIR)/$(GENVERSION): FORCE + $(GENVERSIONHEADER) -t $(TOP) -N $(GENVERSIONMACRO) -V "$(GENVERSIONDEFAULT)" $@ endif #--------------------------------------------------------------- @@ -503,7 +496,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: % .PRECIOUS: $(COMMON_INC) .PHONY: all host inc build install clean rebuild buildInstall build_clean -.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease +.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease FORCE endif # BASE_RULES_BUILD # EOF RULES_BUILD diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile index 0c127999b..2a301d872 100644 --- a/src/template/base/top/exampleApp/src/Makefile +++ b/src/template/base/top/exampleApp/src/Makefile @@ -88,6 +88,5 @@ include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE -# Dependency for the generated header -# must be explicit -devGenVersion$(DEP): $(GENVERSION) +# Explicit dependency needed for generated header file +dev_APPNAME_Version$(DEP): $(COMMON_DIR)/$(GENVERSION) diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index 43bec8d80..f078ad4f6 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -21,109 +21,131 @@ use strict; # RFC 8601 date+time w/ zone (eg "2014-08-29T09:42:47-0700") my $tfmt = '%Y-%m-%dT%H:%M:%S'; $tfmt .= '%z' unless $^O eq 'MSWin32'; # %z returns zone name on Windows +my $now = strftime($tfmt, localtime); -our ($opt_h, $opt_v); +our ($opt_h, $opt_v, $opt_q); our $opt_t = '.'; our $opt_N = 'VCSVERSION'; -our $opt_V = strftime($tfmt, localtime); +our $opt_V = $now; -my $foundvcs = 0; -my $result; +my $vcs; -getopts('hvt:N:V:') && @ARGV == 1 +getopts('hvqt:N:V:') && @ARGV == 1 or HELP_MESSAGE(); my ($outfile) = @ARGV; -if (!$foundvcs && -d "$opt_t/_darcs") { # Darcs +if (!$vcs && -d "$opt_t/_darcs") { # Darcs + print "== Found /_darcs directory\n" if $opt_v; # v1-4-dirty # is tag 'v1' plus 4 patches # with uncommited modifications - $result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`; + my $result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`; chomp $result; - if (!$? && length($result) > 1) { + print "== darcs show tags, changes:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { $opt_V = $result; - $foundvcs = 1; + $vcs = 'Darcs'; # see if working copy has modifications, additions, removals, or missing files my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`; $opt_V .= '-dirty' unless $?; } } -if (!$foundvcs && -d "$opt_t/.hg") { # Mercurial +if (!$vcs && -d "$opt_t/.hg") { # Mercurial + print "== Found /.hg directory\n" if $opt_v; # v1-4-abcdef-dirty # is 4 commits after tag 'v1' with short hash abcdef # with uncommited modifications - $result = `hg --cwd "$opt_t" tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`; - chomp $result; - if (!$? && length($result)>1) { + my $result = `hg tip --template '{latesttag}-{latesttagdistance}-{node|short}'`; + print "== hg tip:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { $opt_V = $result; - $foundvcs = 1; + $vcs = 'Mercurial'; # see if working copy has modifications, additions, removals, or missing files - my $hasmod = `hg --cwd "$opt_t" status -m -a -r -d`; + my $hasmod = `hg status -m -a -r -d`; chomp $hasmod; - $opt_V .= '-dirty' if length($hasmod) > 0; + $opt_V .= '-dirty' if $hasmod ne ''; } } -if (!$foundvcs && -d "$opt_t/.git") { - # same format as Mercurial - $result = `git --git-dir="$opt_t/.git" describe --tags --dirty`; +if (!$vcs && -d "$opt_t/.git") { # Git + print "== Found /.git directory\n" if $opt_v; + # v1-4-abcdef-dirty + # is 4 commits after tag 'v1' with short hash abcdef + # with uncommited modifications + my $result = `git describe --tags --dirty`; chomp $result; - if (!$? && length($result) > 1) { + print "== git describe:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { $opt_V = $result; - $foundvcs = 1; + $vcs = 'Git'; } } -if (!$foundvcs && -d "$opt_t/.svn") { - # 12345 - $result = `cd "$opt_t" && svn info --non-interactive`; +if (!$vcs && -d "$opt_t/.svn") { # Subversion + print "== Found /.svn directory\n" if $opt_v; + # 12345-dirty + my $result = `cd "$opt_t" && svn info --non-interactive`; chomp $result; + print "== svn info:\n$result\n==\n" if $opt_v; if (!$? && $result =~ /^Revision:\s*(\d+)/m) { $opt_V = $1; - $foundvcs = 1; + $vcs = 'Subversion'; # see if working copy has modifications, additions, removals, or missing files - my $hasmod = `cd "$opt_t" && svn status -q --non-interactive`; + my $hasmod = `cd "$opt_t" && svn status --non-interactive`; chomp $hasmod; - $opt_V .= '-dirty' if length($hasmod) > 0; + $opt_V .= '-dirty' if $hasmod ne ''; } } -if (!$foundvcs && -d "$opt_t/.bzr") { - # 12444-anj@aps.anl.gov-20131003210403-icfd8mc37g8vctpf - $result = `cd "$opt_t" && bzr version-info -q --custom --template="{revno}-{revision_id}"`; - chomp $result; - if (!$? && length($result)>1) { +if (!$vcs && -d "$opt_t/.bzr") { # Bazaar + print "== Found /.bzr directory\n" if $opt_v; + # 12444-anj@aps.anl.gov-20131003210403-icfd8mc37g8vctpf-dirty + my $result = `bzr version-info -q --custom --template="{revno}-{revision_id}-{clean}"`; + print "== bzr version-info:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { + $result =~ s/-([01])$/$1 ? '' : '-dirty'/e; $opt_V = $result; - $foundvcs = 1; - # see if working copy has modifications, additions, removals, or missing files - # unfortunately "bzr version-info --check-clean ..." doesn't seem to work as documented - my $hasmod = `cd "$opt_t" && bzr status -SV`; - chomp $hasmod; - $opt_V .= '-dirty' if length($hasmod) > 0; + $vcs = 'Bazaar'; + } +} +if (!$vcs) { + print "== No VCS directories\n" if $opt_v; + if ($opt_V eq '') { + $vcs = 'build date/time'; + $opt_V = $now; + } + else { + $vcs = 'Makefile'; } } my $output = << "__END"; /* Generated file, do not edit! */ +/* Version determined from $vcs */ + #ifndef $opt_N -# define $opt_N \"$opt_V\" + #define $opt_N \"$opt_V\" #endif __END -print "== would\n$output" if $opt_v; + +print "== Want:\n$output==\n" if $opt_v; my $DST; if (open($DST, '+<', $outfile)) { my $actual = join('', <$DST>); - print "== have\n$actual" if $opt_v; + print "== Current:\n$actual==\n" if $opt_v; if ($actual eq $output) { - print "Keeping existing VCS version header $outfile with \"$opt_V\"\n"; + print "Keeping VCS header $outfile\n $opt_N = \"$opt_V\"\n" + unless $opt_q; exit 0; } - print "Updating VCS version header $outfile with \"$opt_V\"\n"; + print "Updating VCS header $outfile\n $opt_N = \"$opt_V\"\n" + unless $opt_q; } else { - print "Creating VCS version header $outfile with \"$opt_V\"\n"; + print "Creating VCS header $outfile\n $opt_N = \"$opt_V\"\n" + unless $opt_q; open($DST, '>', $outfile) - or die "Unable to open or create VCS version header $outfile"; + or die "Can't create $outfile: $!\n"; } seek $DST, 0, 0; @@ -136,11 +158,13 @@ sub HELP_MESSAGE { Usage: genVersionHeader.pl -h Display this Usage message - genVersionHeader.pl [-v] [-t top] [-N NAME] [-V version] output.h"; + genVersionHeader.pl [-v] [-q] [-t top] [-N NAME] [-V version] output.h"; Generate or update the header file output.h + -v - Verbose (debugging messages) + -q - Quiet -t top - Path to the module's top (default '$opt_t') -N NAME - Macro name to be defined (default '$opt_N') - -v version - Version number if no VCS (default '$opt_V') + -V version - Version if no VCS (e.g. '$opt_V') EOF exit $opt_h ? 0 : 1; } From ed4bcd831f378a6c4f868c99ce3e4d14b0d80c5e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 10 Jul 2015 12:28:49 -0500 Subject: [PATCH 095/204] Update example template, release notes Changed device support to use the long string input rectype. Moved the device registration into its own file. Used _APPNAME_ more, trying to prevent future name clashes. --- documentation/RELEASE_NOTES.html | 12 ++-- src/template/base/Makefile | 5 +- src/template/base/top/exampleApp/Db/Makefile | 2 +- .../top/exampleApp/Db/_APPNAME_Version.db | 6 ++ .../top/exampleApp/Db/dbVersionExample.db | 7 --- src/template/base/top/exampleApp/src/Makefile | 12 ++-- .../top/exampleApp/src/_APPNAME_Hello.dbd | 1 - .../base/top/exampleApp/src/devGenVersion.c | 56 ------------------- .../top/exampleApp/src/dev_APPNAME_Version.c | 38 +++++++++++++ .../exampleApp/src/dev_APPNAME_Version.dbd | 1 + .../base/top/exampleBoot/ioc/st.cmd@Common | 2 +- .../base/top/exampleBoot/ioc/st.cmd@RTEMS | 2 +- .../base/top/exampleBoot/ioc/st.cmd@vxWorks | 2 +- 13 files changed, 65 insertions(+), 81 deletions(-) create mode 100644 src/template/base/top/exampleApp/Db/_APPNAME_Version.db delete mode 100644 src/template/base/top/exampleApp/Db/dbVersionExample.db delete mode 100644 src/template/base/top/exampleApp/src/devGenVersion.c create mode 100644 src/template/base/top/exampleApp/src/dev_APPNAME_Version.c create mode 100644 src/template/base/top/exampleApp/src/dev_APPNAME_Version.dbd diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 99a32458e..88becb3cc 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -17,13 +17,13 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.

Generate Version Header

-

A Perl script and Makefile rules are added to allow modules -to generate a C header file with a macro defined with an automatically updated identifier. -This is a VCS revision ID (Darcs, Git, and Mercurial supported) or -the time of the build if not VCS system is in use.

+

A Perl script and Makefile rules have been added to allow modules to generate +a C header file with a macro defined with an automatically updated identifier. +This is a VCS revision ID (Darcs, Git, Mercurial Subversion and Bazaar are all +supported) or the date/time of the build if no VCS system is in use.

-

The example template is updated with a demonstration which makes this -identifier visible with a stringin record.

+

The makeBaseApp example template has been updated with a new device support +which makes this identifier visible via a lsi (long string input) record.

Implement EPICS_CAS_INTF_ADDR_LIST in rsrv

diff --git a/src/template/base/Makefile b/src/template/base/Makefile index 4e7de23d3..c8c1eed8b 100644 --- a/src/template/base/Makefile +++ b/src/template/base/Makefile @@ -28,11 +28,12 @@ TEMPLATES += top/exampleApp/Makefile TEMPLATES += top/exampleApp/Db/Makefile TEMPLATES += top/exampleApp/Db/dbExample1.db TEMPLATES += top/exampleApp/Db/dbExample2.db -TEMPLATES += top/exampleApp/Db/dbVersionExample.db +TEMPLATES += top/exampleApp/Db/_APPNAME_Version.db TEMPLATES += top/exampleApp/Db/dbSubExample.db TEMPLATES += top/exampleApp/Db/user.substitutions TEMPLATES += top/exampleApp/src/Makefile -TEMPLATES += top/exampleApp/src/devGenVersion.c +TEMPLATES += top/exampleApp/src/dev_APPNAME_Version.c +TEMPLATES += top/exampleApp/src/dev_APPNAME_Version.dbd TEMPLATES += top/exampleApp/src/xxxRecord.dbd TEMPLATES += top/exampleApp/src/xxxRecord.c TEMPLATES += top/exampleApp/src/devXxxSoft.c diff --git a/src/template/base/top/exampleApp/Db/Makefile b/src/template/base/top/exampleApp/Db/Makefile index 86bc0d620..89b452264 100644 --- a/src/template/base/top/exampleApp/Db/Makefile +++ b/src/template/base/top/exampleApp/Db/Makefile @@ -12,7 +12,7 @@ include $(TOP)/configure/CONFIG # databases, templates, substitutions like this DB += dbExample1.db DB += dbExample2.db -DB += dbVersionExample.db +DB += _APPNAME_Version.db DB += dbSubExample.db DB += user.substitutions diff --git a/src/template/base/top/exampleApp/Db/_APPNAME_Version.db b/src/template/base/top/exampleApp/Db/_APPNAME_Version.db new file mode 100644 index 000000000..1b03f08ba --- /dev/null +++ b/src/template/base/top/exampleApp/Db/_APPNAME_Version.db @@ -0,0 +1,6 @@ +record(lsi, "$(user):_APPNAME_:version") { + field(DTYP, "_APPNAME_ version") + field(DESC, "Version string") + field(SIZV, "$(SIZV=200)") + field(PINI, "YES") +} diff --git a/src/template/base/top/exampleApp/Db/dbVersionExample.db b/src/template/base/top/exampleApp/Db/dbVersionExample.db deleted file mode 100644 index e86b75dc4..000000000 --- a/src/template/base/top/exampleApp/Db/dbVersionExample.db +++ /dev/null @@ -1,7 +0,0 @@ -record(waveform, "$(user):version") { - field(DTYP, "Module Version") - field(DESC, "Module version") - field(FTVL, "CHAR") - field(NELM, "$(NELM=200)") - field(PINI, "YES") -} diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile index 2a301d872..fa2a16ce7 100644 --- a/src/template/base/top/exampleApp/src/Makefile +++ b/src/template/base/top/exampleApp/src/Makefile @@ -14,21 +14,22 @@ DBDINC += xxxRecord # Install devXxxSoft.dbd into /dbd DBD += xxxSupport.dbd -# Compile and add the code to the support library +# Compile and add code to the support library _APPNAME_Support_SRCS += xxxRecord.c _APPNAME_Support_SRCS += devXxxSoft.c -_APPNAME_Support_SRCS += devGenVersion.c # Link locally-provided code into the support library, -# rather than directly into the IOC application. +# rather than directly into the IOC application, that +# causes problems on Windows DLL builds _APPNAME_Support_SRCS += dbSubExample.c +_APPNAME_Support_SRCS += dev_APPNAME_Version.c _APPNAME_Support_SRCS += _APPNAME_Hello.c _APPNAME_Support_SRCS += initTrace.c _APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS) -# Generate a header which defines a macro with a version string -# with the date and time, or a revision id from VCS system if available +# Auto-generate a header file containing a version string. +# Version comes from the VCS if available, else date+time. GENVERSION = _APPNAME_Version.h # Macro name GENVERSIONMACRO = _APPNAME_VERSION @@ -44,6 +45,7 @@ DBD += _APPNAME_.dbd _APPNAME__DBD += base.dbd _APPNAME__DBD += xxxSupport.dbd _APPNAME__DBD += dbSubExample.dbd +_APPNAME__DBD += dev_APPNAME_Version.dbd _APPNAME__DBD += _APPNAME_Hello.dbd _APPNAME__DBD += initTrace.dbd diff --git a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd index 47f187050..64eb0389a 100644 --- a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd +++ b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd @@ -1,2 +1 @@ registrar(helloRegister) -device(waveform,INST_IO,devWfMyModVersion,"Module Version") diff --git a/src/template/base/top/exampleApp/src/devGenVersion.c b/src/template/base/top/exampleApp/src/devGenVersion.c deleted file mode 100644 index 297b69138..000000000 --- a/src/template/base/top/exampleApp/src/devGenVersion.c +++ /dev/null @@ -1,56 +0,0 @@ -/* devGenVersion.c */ -/* Example device support providing the module version string as a charactor array (long string) */ - -#include -#include -#include -#include - -#include "recGbl.h" -#include "alarm.h" -#include "recSup.h" -#include "devSup.h" -#include "menuFtype.h" -#include "waveformRecord.h" - -#include "_APPNAME_Version.h" - -/* must be last include */ -#include "epicsExport.h" - -static long read_wf(waveformRecord *prec) -{ - size_t N = strlen(_APPNAME_VERSION)+1; - char *buf = prec->bptr; - - if(prec->ftvl!=menuFtypeCHAR) { - (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); - return 0; - } - - if(N>=prec->nelm) - N = prec->nelm; - prec->nord = N; - - memcpy(buf, _APPNAME_VERSION, N); - buf[prec->nelm-1] = '\0'; - - return 0; -} - -static struct { - long number; - DEVSUPFUN report; - DEVSUPFUN init; - DEVSUPFUN init_record; - DEVSUPFUN get_ioint_info; - DEVSUPFUN read_si; -}devWfMyModVersion={ - 5, - NULL, - NULL, - NULL, - NULL, - read_wf, -}; -epicsExportAddress(dset,devWfMyModVersion); diff --git a/src/template/base/top/exampleApp/src/dev_APPNAME_Version.c b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.c new file mode 100644 index 000000000..4f2c28f67 --- /dev/null +++ b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.c @@ -0,0 +1,38 @@ +/* dev_APPNAME_Version.c */ +/* Example device support for the lsi (long string input) record + * providing the module version string as the value + */ + +#include +#include +#include + +#include "devSup.h" +#include "lsiRecord.h" + +#include "_APPNAME_Version.h" + +/* must be last include */ +#include "epicsExport.h" + +const char const version[] = _APPNAME_VERSION; + +static long read_string(lsiRecord *prec) +{ + size_t N = sizeof version; + char *buf = prec->val; + + if (N > prec->sizv) + N = prec->sizv; + prec->len = N; + + memcpy(buf, version, N); + buf[N - 1] = '\0'; + + return 0; +} + +static lsidset dev_CSAFEAPPNAME_Version = { + 5, NULL, NULL, NULL, NULL, read_string +}; +epicsExportAddress(dset,dev_CSAFEAPPNAME_Version); diff --git a/src/template/base/top/exampleApp/src/dev_APPNAME_Version.dbd b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.dbd new file mode 100644 index 000000000..67295f3f0 --- /dev/null +++ b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.dbd @@ -0,0 +1 @@ +device(lsi,INST_IO,dev_CSAFEAPPNAME_Version,"_APPNAME_ version") diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@Common b/src/template/base/top/exampleBoot/ioc/st.cmd@Common index c4eb081ba..a2d018e3a 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@Common +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@Common @@ -13,7 +13,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" -dbLoadRecords "db/dbVersionExample.db", "user=_USER_" +dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" ## Set this to see messages from mySub diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS index 7b6319608..cc96f84ab 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS @@ -11,7 +11,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase) ## Load record instances dbLoadTemplate("db/user.substitutions") -dbLoadRecords("db/dbVersionExample.db", "user=_USER_") +dbLoadRecords("db/_APPNAME_Version.db", "user=_USER_") dbLoadRecords("db/dbSubExample.db", "user=_USER_") ## Set this to see messages from mySub diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks index d8a2077ce..44a9afc67 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks @@ -20,7 +20,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" -dbLoadRecords "db/dbVersionExample.db", "user=_USER_" +dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" ## Set this to see messages from mySub From 8b3d37d3925fcec0abc0b8539724381c9e98c1c3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 10 Jul 2015 14:19:44 -0400 Subject: [PATCH 096/204] genVersionHeader: work with git repo w/o tags --always is necessary for (new) repositories w/o any tags. --abbrev=20 to get more than the default 7 characters of the hash --- src/tools/genVersionHeader.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index f078ad4f6..5851ea831 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -72,7 +72,7 @@ if (!$vcs && -d "$opt_t/.git") { # Git # v1-4-abcdef-dirty # is 4 commits after tag 'v1' with short hash abcdef # with uncommited modifications - my $result = `git describe --tags --dirty`; + my $result = `git describe --always --tags --dirty --abbrev=20`; chomp $result; print "== git describe:\n$result\n==\n" if $opt_v; if (!$? && $result ne '') { From 58602cfa8022c620ececa079141674862974565a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 10 Jul 2015 16:22:31 -0500 Subject: [PATCH 097/204] Clean up in src/ioc/db/test/scanIoTest.c --- src/ioc/db/test/scanIoTest.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/ioc/db/test/scanIoTest.c b/src/ioc/db/test/scanIoTest.c index 904f24d6e..5d02bf15d 100644 --- a/src/ioc/db/test/scanIoTest.c +++ b/src/ioc/db/test/scanIoTest.c @@ -42,16 +42,6 @@ #include "devx.h" #include "xRecord.h" -#define ONE_THREAD_LOOPS 101 -#define PAR_THREAD_LOOPS 53 -#define CB_THREAD_LOOPS 13 - -#define NO_OF_THREADS 7 -#define NO_OF_MEMBERS 5 -#define NO_OF_GROUPS 11 - -#define NO_OF_MID_THREADS 3 - STATIC_ASSERT(NUM_CALLBACK_PRIORITIES==3); void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); @@ -307,7 +297,7 @@ static void testMultiThreading(void) MAIN(scanIoTest) { - testPlan(0); + testPlan(148); testSingleThreading(); testDiag("run a second time to verify shutdown and restart works"); testSingleThreading(); From dd6edb103c175d4d870c46a783fa199784fb3385 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 13 Jul 2015 17:51:46 -0400 Subject: [PATCH 098/204] scanIoTest: set # of tests --- src/ioc/db/test/scanIoTest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/test/scanIoTest.c b/src/ioc/db/test/scanIoTest.c index 5d02bf15d..ee6d8b462 100644 --- a/src/ioc/db/test/scanIoTest.c +++ b/src/ioc/db/test/scanIoTest.c @@ -257,9 +257,9 @@ static void testMultiThreading(void) eltc(1); testDiag("Scan first list"); - scanIoRequest(drvs[0]->scan); + testOk1(scanIoRequest(drvs[0]->scan)==0x7); testDiag("Scan second list"); - scanIoRequest(drvs[1]->scan); + testOk1(scanIoRequest(drvs[1]->scan)==0x7); testDiag("Wait for everything to start"); for(i=0; i Date: Mon, 13 Jul 2015 17:51:46 -0400 Subject: [PATCH 099/204] iocInit: dbCa shutdown order --- src/ioc/misc/iocInit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 14dc756ca..11f0e1f75 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -692,11 +692,13 @@ int iocShutdown(void) iterateRecords(doFreeRecord, NULL); dbLockCleanupRecords(pdbbase); asShutdown(); + } + dbCaShutdown(); /* must be before dbChannelExit */ + if (iocBuildMode==buildIsolated) { dbChannelExit(); dbProcessNotifyExit(); iocshFree(); } - dbCaShutdown(); iocState = iocStopped; iocBuildMode = buildRSRV; return 0; From 7395d1d88d457f63935eeff3ebff6cace2e7ac48 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 24 Jul 2015 14:06:45 -0500 Subject: [PATCH 100/204] Make dbCa routines macros to dbLink wrappers --- src/ioc/db/dbCa.c | 41 ++++++++++++------------ src/ioc/db/dbCa.h | 75 +++++++++++++++++++++++--------------------- src/ioc/db/dbCaPvt.h | 13 +++----- src/ioc/db/dbLink.c | 29 +++++++++-------- src/ioc/db/dbLink.h | 14 ++++----- 5 files changed, 86 insertions(+), 86 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 7fa04db79..c35515801 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -6,8 +6,7 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ - -/* $Revision-Id$ +/* dbCa.c * * Original Authors: Bob Dalesio and Marty Kraimer * Date: 26MAR96 @@ -95,9 +94,9 @@ static int dbca_chan_count; * All dbCa* functions operating on a single link may only be called when * the record containing the DBLINK is locked. Including: * dbCaGet*() - * dbCaIsLinkConnected() + * isConnected() * dbCaPutLink() - * dbCaScanFwdLink() + * scanForward() * dbCaAddLinkCallback() * dbCaRemoveLink() * @@ -472,7 +471,7 @@ long dbCaPutLink(struct link *plink, short dbrType, return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest, 0, NULL); } -int dbCaIsLinkConnected(const struct link *plink) +static int isConnected(const struct link *plink) { caLink *pca; @@ -482,7 +481,7 @@ int dbCaIsLinkConnected(const struct link *plink) return pca->isConnected; } -void dbCaScanFwdLink(struct link *plink) { +static void scanForward(struct link *plink) { short fwdLinkValue = 1; if (plink->value.pv_link.pvlMask & pvlOptFWD) @@ -501,7 +500,7 @@ void dbCaScanFwdLink(struct link *plink) { return -1; \ } -long dbCaGetNelements(const struct link *plink, long *nelements) +static long getElements(const struct link *plink, long *nelements) { caLink *pca; @@ -511,7 +510,7 @@ long dbCaGetNelements(const struct link *plink, long *nelements) return 0; } -long dbCaGetAlarm(const struct link *plink, +static long getAlarm(const struct link *plink, epicsEnum16 *pstat, epicsEnum16 *psevr) { caLink *pca; @@ -523,7 +522,7 @@ long dbCaGetAlarm(const struct link *plink, return 0; } -long dbCaGetTimeStamp(const struct link *plink, +static long getTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) { caLink *pca; @@ -534,7 +533,7 @@ long dbCaGetTimeStamp(const struct link *plink, return 0; } -int dbCaGetLinkDBFtype(const struct link *plink) +static int getDBFtype(const struct link *plink) { caLink *pca; int type; @@ -565,7 +564,7 @@ long dbCaGetAttributes(const struct link *plink, return 0; } -long dbCaGetControlLimits(const struct link *plink, +static long getControlLimits(const struct link *plink, double *low, double *high) { caLink *pca; @@ -581,7 +580,7 @@ long dbCaGetControlLimits(const struct link *plink, return gotAttributes ? 0 : -1; } -long dbCaGetGraphicLimits(const struct link *plink, +static long getGraphicLimits(const struct link *plink, double *low, double *high) { caLink *pca; @@ -597,7 +596,7 @@ long dbCaGetGraphicLimits(const struct link *plink, return gotAttributes ? 0 : -1; } -long dbCaGetAlarmLimits(const struct link *plink, +static long getAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { caLink *pca; @@ -615,7 +614,7 @@ long dbCaGetAlarmLimits(const struct link *plink, return gotAttributes ? 0 : -1; } -long dbCaGetPrecision(const struct link *plink, short *precision) +static long getPrecision(const struct link *plink, short *precision) { caLink *pca; int gotAttributes; @@ -627,7 +626,7 @@ long dbCaGetPrecision(const struct link *plink, short *precision) return gotAttributes ? 0 : -1; } -long dbCaGetUnits(const struct link *plink, +static long getUnits(const struct link *plink, char *units, int unitsSize) { caLink *pca; @@ -676,14 +675,14 @@ static void scanLinkOnce(dbCommon *prec, caLink *pca) { static lset dbCa_lset = { dbCaRemoveLink, - dbCaIsLinkConnected, - dbCaGetLinkDBFtype, dbCaGetNelements, + isConnected, + getDBFtype, getElements, dbCaGetLink, - dbCaGetControlLimits, dbCaGetGraphicLimits, dbCaGetAlarmLimits, - dbCaGetPrecision, dbCaGetUnits, - dbCaGetAlarm, dbCaGetTimeStamp, + getControlLimits, getGraphicLimits, getAlarmLimits, + getPrecision, getUnits, + getAlarm, getTimeStamp, dbCaPutLink, - dbCaScanFwdLink + scanForward }; static void connectionCallback(struct connection_handler_args arg) diff --git a/src/ioc/db/dbCa.h b/src/ioc/db/dbCa.h index 21bedb684..841b48f16 100644 --- a/src/ioc/db/dbCa.h +++ b/src/ioc/db/dbCa.h @@ -1,20 +1,17 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2015 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbCa.h */ +/* dbCa.h */ #ifndef INCdbCah #define INCdbCah -#include "shareLib.h" -#include "epicsTime.h" -#include "link.h" +#include "dbLink.h" #ifdef __cplusplus extern "C" { @@ -34,44 +31,52 @@ epicsShareFunc void dbCaAddLinkCallback(struct link *plink, dbCaCallback connect, dbCaCallback monitor, void *userPvt); epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType); epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink); + epicsShareFunc long dbCaGetLink(struct link *plink, short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr, long *nRequest); + +epicsShareFunc long dbCaGetAttributes(const struct link *plink, + dbCaCallback callback, void *userPvt); + epicsShareFunc long dbCaPutLinkCallback(struct link *plink, short dbrType, const void *pbuffer,long nRequest, dbCaCallback callback, void *userPvt); epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType, const void *pbuffer,long nRequest); -epicsShareFunc int dbCaIsLinkConnected(const struct link *plink); -epicsShareFunc void dbCaScanFwdLink(struct link *plink); - -/* The following are available after the link is connected*/ -epicsShareFunc long dbCaGetNelements(const struct link *plink, - long *nelements); -#define dbCaGetSevr(plink, severity) \ - dbCaGetAlarm((plink), NULL, (severity)) -epicsShareFunc long dbCaGetAlarm(const struct link *plink, - epicsEnum16 *status, epicsEnum16 *severity); -epicsShareFunc long dbCaGetTimeStamp(const struct link *plink, - epicsTimeStamp *pstamp); -epicsShareFunc int dbCaGetLinkDBFtype(const struct link *plink); - -/*The following are available after attribute request is complete*/ -epicsShareFunc long dbCaGetAttributes(const struct link *plink, - dbCaCallback callback, void *userPvt); -epicsShareFunc long dbCaGetControlLimits(const struct link *plink, - double *low, double *high); -epicsShareFunc long dbCaGetGraphicLimits(const struct link *plink, - double *low, double *high); -epicsShareFunc long dbCaGetAlarmLimits(const struct link *plink, - double *lolo, double *low, double *high, double *hihi); -epicsShareFunc long dbCaGetPrecision(const struct link *plink, - short *precision); -epicsShareFunc long dbCaGetUnits(const struct link *plink, - char *units, int unitsSize); extern struct ca_client_context * dbCaClientContext; + +/* These macros are for backwards compatibility */ + +#define dbCaIsLinkConnected(link) \ + dbIsLinkConnected(link) + +#define dbCaGetLinkDBFtype(link) \ + dbGetLinkDBFtype(link) +#define dbCaGetNelements(link, nelements) \ + dbGetNelements(link, nelements) +#define dbCaGetSevr(link, sevr) \ + dbGetAlarm(link, NULL, sevr) +#define dbCaGetAlarm(link, stat, sevr) \ + dbGetAlarm(link, stat, sevr) +#define dbCaGetTimeStamp(link, pstamp) \ + dbGetTimeStamp(link, pstamp) +#define dbCaGetControlLimits(link, low, high) \ + dbGetControlLimits(link, low, high) +#define dbCaGetGraphicLimits(link, low, high) \ + dbGetGraphicLimits(link, low, high) +#define dbCaGetAlarmLimits(link, lolo, low, high, hihi) \ + dbGetAlarmLimits(link, lolo, low, high, hihi) +#define dbCaGetPrecision(link, prec) \ + dbGetPrecision(link, prec) +#define dbCaGetUnits(link, units, unitSize) \ + dbGetUnits(link, units, unitSize) + +#define dbCaScanFwdLink(link) \ + dbScanFwdLink(link) + #ifdef __cplusplus } #endif diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index d5274c577..553d48481 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -6,14 +6,11 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbCaPvt.h */ -/**************************************************************** -* -* Current Author: Bob Dalesio -* Contributing Author: Marty Kraimer -* Date: 08APR96 -* -****************************************************************/ +/* dbCaPvt.h + * + * Original Authors: Bob Dalesio, Marty Kraimer + * + */ #ifndef INC_dbCaPvt_H #define INC_dbCaPvt_H diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index b97d2a4ea..a224eac0f 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -1,14 +1,13 @@ /*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne - * National Laboratory. - * Copyright (c) 2002 The Regents of the University of California, as - * Operator of Los Alamos National Laboratory. - * EPICS BASE is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - \*************************************************************************/ -/* dbLink.c */ -/* $Id$ */ -/* +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* dbLink.c + * * Original Authors: Bob Dalesio, Marty Kraimer * Current Author: Andrew Johnson */ @@ -205,7 +204,7 @@ static void dbDbRemoveLink(dbLocker *locker, struct link *plink) free(pdbAddr); } -static int dbDbIsLinkConnected(const struct link *plink) +static int dbDbIsConnected(const struct link *plink) { return TRUE; } @@ -433,7 +432,7 @@ static void dbDbScanFwdLink(struct link *plink) static lset dbDb_lset = { dbDbRemoveLink, - dbDbIsLinkConnected, + dbDbIsConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, @@ -542,10 +541,10 @@ int dbIsLinkConnected(const struct link *plink) { lset *plset = plink->lset; - if (!plset || !plset->isLinkConnected) + if (!plset || !plset->isConnected) return FALSE; - return plset->isLinkConnected(plink); + return plset->isConnected(plink); } int dbGetLinkDBFtype(const struct link *plink) @@ -687,8 +686,8 @@ void dbScanFwdLink(struct link *plink) { lset *plset = plink->lset; - if (plset && plset->scanFwdLink) - plset->scanFwdLink(plink); + if (plset && plset->scanForward) + plset->scanForward(plink); } /* Helper functions for long string support */ diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index fd9b2c250..a2e9175df 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -6,8 +6,7 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* - * dbLink.h +/* dbLink.h * * Created on: Mar 21, 2010 * Author: Andrew Johnson @@ -30,11 +29,11 @@ struct dbLocker; typedef struct lset { void (*removeLink)(struct dbLocker *locker, struct link *plink); - int (*isLinkConnected)(const struct link *plink); + int (*isConnected)(const struct link *plink); int (*getDBFtype)(const struct link *plink); long (*getElements)(const struct link *plink, long *nelements); long (*getValue)(struct link *plink, short dbrType, void *pbuffer, - epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); long (*getControlLimits)(const struct link *plink, double *lo, double *hi); long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, @@ -46,11 +45,11 @@ typedef struct lset { long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); long (*putValue)(struct link *plink, short dbrType, const void *pbuffer, long nRequest); - void (*scanFwdLink)(struct link *plink); + void (*scanForward)(struct link *plink); } lset; -#define dbGetSevr(PLINK, PSEVERITY) \ - dbGetAlarm((PLINK), NULL, (PSEVERITY)) +#define dbGetSevr(link, sevr) \ + dbGetAlarm(link, NULL, sevr) epicsShareFunc void dbInitLink(struct link *plink, short dbfType); epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, @@ -58,6 +57,7 @@ epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, void *pbuffer); epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink); + epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements); epicsShareFunc int dbIsLinkConnected(const struct link *plink); epicsShareFunc int dbGetLinkDBFtype(const struct link *plink); From 877a409de161bf93be9376b946c434e52c6808eb Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 24 Jul 2015 15:59:38 -0500 Subject: [PATCH 101/204] Fix podToHtml build issue on Windows --- configure/RULES.Db | 3 +-- configure/RULES_BUILD | 2 +- configure/os/CONFIG.UnixCommon.Common | 2 +- src/libCom/env/RULES | 2 +- src/libCom/misc/RULES | 2 +- src/template/ext/top/configure/RULES_JAVA | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/configure/RULES.Db b/configure/RULES.Db index 4732e623e..620677407 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -445,13 +445,12 @@ $(COMMON_DIR)/%.html: %.pm $(TOOLS)/podToHtml.pl $(COMMON_DIR)/%.html: ../%.pm $(TOOLS)/podToHtml.pl @$(RM) $(notdir $@) $(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $< - @$(MKDIR) -p $(dir $@) + @$(MKDIR) $(dir $@) @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.html: ../%.pl $(TOOLS)/podToHtml.pl @$(RM) $(notdir $@) $(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $< - @$(MKDIR) -p $(dir $@) @$(MV) $(notdir $@) $@ .PRECIOUS: $(COMMON_DIR)/%.html %.html diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 3d3fc28c8..e7b8d8d27 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -168,7 +168,7 @@ ifdef RES endif $(DIRECTORY_TARGETS) : - $(MKDIR) -p $@ + $(MKDIR) $@ $(PRODNAME): $(INSTALL_LIB_INSTALLS) diff --git a/configure/os/CONFIG.UnixCommon.Common b/configure/os/CONFIG.UnixCommon.Common index 6f3e81bc1..4f02604ee 100644 --- a/configure/os/CONFIG.UnixCommon.Common +++ b/configure/os/CONFIG.UnixCommon.Common @@ -11,7 +11,7 @@ CP = cp MV = mv RM = rm -f -MKDIR = mkdir +MKDIR = mkdir -p RMDIR = rm -rf CAT = cat diff --git a/src/libCom/env/RULES b/src/libCom/env/RULES index f63475d25..88d29be17 100644 --- a/src/libCom/env/RULES +++ b/src/libCom/env/RULES @@ -9,4 +9,4 @@ envData.c: $(LIBCOM)/env/envDefs.h $(LIBCOM)/env/bldEnvData.pl \ $(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV - $(PERL) $(LIBCOM)/env/bldEnvData.pl $(INSTALL_QUIETLY) $(CONFIG) + $(PERL) $(LIBCOM)/env/bldEnvData.pl $(QUIET_FLAG) $(CONFIG) diff --git a/src/libCom/misc/RULES b/src/libCom/misc/RULES index ddcce46a2..2ba358675 100644 --- a/src/libCom/misc/RULES +++ b/src/libCom/misc/RULES @@ -8,7 +8,7 @@ # This is a Makefile fragment, see src/libCom/Makefile. MAKEVERSION = $(LIBCOM)/misc/makeEpicsVersion.pl -MAKEVERSION_FLAGS = -o $(notdir $@) $(INSTALL_QUIETLY) +MAKEVERSION_FLAGS = -o $(notdir $@) $(QUIET_FLAG) MAKEVERSION_FLAGS += $(if $(EPICS_SITE_VERSION),-v "$(EPICS_SITE_VERSION)") $(COMMON_DIR)/epicsVersion.h: $(CONFIG)/CONFIG_BASE_VERSION \ diff --git a/src/template/ext/top/configure/RULES_JAVA b/src/template/ext/top/configure/RULES_JAVA index 1f06e6206..0285c7cd5 100644 --- a/src/template/ext/top/configure/RULES_JAVA +++ b/src/template/ext/top/configure/RULES_JAVA @@ -113,7 +113,7 @@ buildclean: ifdef JAVA_DIRECTORY_TARGETS $(JAVA_DIRECTORY_TARGETS): - $(MKDIR) -p $@ + $(MKDIR) $@ endif $(COMMON_JAVAINC):$(JAVAINC_CLASSFILES) From 02ac91aa2a2fc79d4e9d6c848e354fac7cd2999d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 12 Aug 2015 10:17:31 -0500 Subject: [PATCH 102/204] epicsReadline: ensure readline context is zero'd Otherwise ->osd != NULL for the default RTEMS readline impl --- src/libCom/osi/epicsReadline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/osi/epicsReadline.c b/src/libCom/osi/epicsReadline.c index 7cc569014..d410ac91e 100644 --- a/src/libCom/osi/epicsReadline.c +++ b/src/libCom/osi/epicsReadline.c @@ -59,7 +59,7 @@ static void osdReadlineEnd(struct readlineContext * c) {} void * epicsShareAPI epicsReadlineBegin(FILE *in) { - struct readlineContext *readlineContext = malloc(sizeof *readlineContext); + struct readlineContext *readlineContext = calloc(1, sizeof(*readlineContext)); if (readlineContext) { readlineContext->in = in; From 46853d6862457f81c469d9198cb52e093b4ef263 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 16 Aug 2015 17:43:10 -0500 Subject: [PATCH 103/204] Clean up compiler warnings --- src/ioc/db/test/dbCaLinkTest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index 1f20028f1..c0b5f7b9d 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -303,7 +303,7 @@ static void checkArray(const char *msg, match &= (*b)==x; for(;i Date: Tue, 18 Aug 2015 09:07:18 -0400 Subject: [PATCH 104/204] ioc/db/test: wrong variable for -I --- src/ioc/db/test/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 34534d396..4bab6707f 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -10,8 +10,8 @@ TOP=../../../.. include $(TOP)/configure/CONFIG -# Find private headers -USR_CFLAGS += -I../.. +# Allow access to private headers in db/ +USR_CPPFLAGS = -I ../.. TESTLIBRARY = dbTestIoc From f99fbe1964c329a14cdb4386aa2c53223ce507cf Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 09:07:18 -0400 Subject: [PATCH 105/204] move dbCaShutdown earlier shutdown dbCa thread before free'ing records --- src/ioc/misc/iocInit.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index ceec436a3..d2d369f7b 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -693,7 +693,7 @@ int iocShutdown(void) scanStop(); callbackStop(); } - dbCaShutdown(); + dbCaShutdown(); /* must be before dbFreeRecord and dbChannelExit */ if (iocBuildMode==buildIsolated) { /* free resources */ scanCleanup(); @@ -701,9 +701,6 @@ int iocShutdown(void) iterateRecords(doFreeRecord, NULL); dbLockCleanupRecords(pdbbase); asShutdown(); - } - dbCaShutdown(); /* must be before dbChannelExit */ - if (iocBuildMode==buildIsolated) { dbChannelExit(); dbProcessNotifyExit(); iocshFree(); From 1e39224836ceed346404eb79d56f65e42aa2f1bc Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 09:07:18 -0400 Subject: [PATCH 106/204] dbCa: simplify shutdown Don't need partial shutdown case anymore. Also avoid race when destorying startStopEvent --- src/ioc/db/dbCa.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index bf06d995a..caf0addbb 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -64,7 +64,7 @@ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/ static int removesOutstanding = 0; #define removesOutstandingWarning 10000 -static volatile enum { +static volatile enum dbCaCtl_t { ctlInit, ctlRun, ctlPause, ctlExit } dbCaCtl; static epicsEventId startStopEvent; @@ -203,24 +203,11 @@ void dbCaCallbackProcess(void *userPvt) void dbCaShutdown(void) { - if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) { - dbCaCtl = ctlExit; - epicsEventSignal(workListEvent); - epicsEventMustWait(startStopEvent); - epicsEventDestroy(startStopEvent); - } else { - /* manually cleanup queue since dbCa thread isn't running - * which only happens in unit tests - */ - caLink *pca; - epicsMutexMustLock(workListLock); - while((pca=(caLink*)ellGet(&workList))!=NULL) { - if(pca->link_action&CA_CLEAR_CHANNEL) { - caLinkDec(pca); - } - } - epicsMutexUnlock(workListLock); - } + enum dbCaCtl_t cur = dbCaCtl; + assert(cur == ctlRun || cur == ctlPause); + dbCaCtl = ctlExit; + epicsEventSignal(workListEvent); + epicsEventMustWait(startStopEvent); } static void dbCaLinkInitImpl(int isolate) @@ -233,7 +220,8 @@ static void dbCaLinkInitImpl(int isolate) if (!workListEvent) workListEvent = epicsEventMustCreate(epicsEventEmpty); - startStopEvent = epicsEventMustCreate(epicsEventEmpty); + if(!startStopEvent) + startStopEvent = epicsEventMustCreate(epicsEventEmpty); dbCaCtl = ctlPause; epicsThreadCreate("dbCaLink", epicsThreadPriorityMedium, From 4ab6aa79e355e0b6f2c3765602eb9483c10ce550 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 09:07:18 -0400 Subject: [PATCH 107/204] ioc/db/test: fixup arrShorthandTest to use dbUnitTest --- src/ioc/db/test/arrShorthandTest.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c index afb37e577..8f130ab57 100644 --- a/src/ioc/db/test/arrShorthandTest.c +++ b/src/ioc/db/test/arrShorthandTest.c @@ -22,7 +22,9 @@ #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "registry.h" -#include "epicsUnitTest.h" +#include "errlog.h" +#include "epicsExit.h" +#include "dbUnitTest.h" #include "testMain.h" #include "osiFileName.h" @@ -85,22 +87,20 @@ MAIN(arrShorthandTest) testPlan(26); - db_init_events(); - dbChannelInit(); + testdbPrepare(); - if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd", - "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR - "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Database description 'dbTestIoc.dbd' not found"); + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "xRecord.db", - "." OSI_PATH_LIST_SEPARATOR "..", NULL)) - testAbort("Test database 'xRecord.db' not found"); + testdbReadDatabase("xRecord.db", NULL, NULL); testHead("Register plugin"); testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin"); + eltc(0); + testIocInitOk(); + eltc(1); + #define TESTBAD(Title, Expr) \ testDiag(Title); \ testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \ @@ -129,9 +129,8 @@ MAIN(arrShorthandTest) TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4); TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4); - dbFreeBase(pdbbase); - registryFree(); - pdbbase = NULL; + testIocShutdownOk(); + testdbCleanup(); return testDone(); } From 050b9f9a48ade57eb13f6f2414cb83f98969ee12 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 09:07:18 -0400 Subject: [PATCH 108/204] ioc/db/test: dbChannelTest use dbUnitTest.h --- src/ioc/db/test/dbChannelTest.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index a509e74c9..156c7e9bd 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -18,7 +18,7 @@ #include "dbAccessDefs.h" #include "registry.h" #include "recSup.h" -#include "epicsUnitTest.h" +#include "dbUnitTest.h" #include "testMain.h" #include "osiFileName.h" #include "errlog.h" @@ -158,17 +158,14 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */ testPlan(76); - if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd", - "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR - "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Database description 'dbTestIoc.dbd' not found"); + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "xRecord.db", - "." OSI_PATH_LIST_SEPARATOR "..", NULL)) - testAbort("Test database 'xRecord.db' not found"); + testdbReadDatabase("xRecord.db", NULL, NULL); - dbChannelInit(); + eltc(0); + testIocInitOk(); + eltc(1); r = e = 0; /* dbChannelTest() checks record and field names */ @@ -267,9 +264,8 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */ e = e_start | e_start_map | e_abort; testOk1(!dbChannelCreate("x.{\"scalar\":{}}")); - dbFreeBase(pdbbase); - registryFree(); - pdbbase = NULL; + testIocShutdownOk(); + testdbCleanup(); return testDone(); } From b2716f0a196f0db0d63185f35d1146c942779abf Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 10:46:20 -0400 Subject: [PATCH 109/204] dbCa: subscribe to variable length arrays --- src/ioc/db/dbCa.c | 29 ++++++++++++++++++++--------- src/ioc/db/dbCaPvt.h | 6 ++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index caf0addbb..5e7a609fe 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -346,16 +346,19 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, assert(pca->pgetNative); status = fConvert(pca->pgetNative, pdest, 0); } else { - long ntoget = *nelements; + unsigned long ntoreport = *nelements, ntoget; struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off); aConvert = dbGetConvertRoutine[newType][dbrType]; assert(pca->pgetNative); - if (ntoget > pca->nelements) - ntoget = pca->nelements; - *nelements = ntoget; + if (ntoreport > pca->nelements) + ntoreport = pca->nelements; + ntoget = ntoreport; + if (ntoget > pca->usedelements) + ntoget = pca->usedelements; + *nelements = ntoreport; memset((void *)&dbAddr, 0, sizeof(dbAddr)); dbAddr.pfield = pca->pgetNative; @@ -363,6 +366,12 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, dbAddr.field_size = MAX_STRING_SIZE; /*Ignore error return*/ aConvert(&dbAddr, pdest, ntoget, ntoget, 0); + if(ntogetelementSize+(char*)pca->pgetNative, + 0, + (ntoreport-ntoget)*pca->elementSize); + } } done: if (pstat) *pstat = pca->stat; @@ -705,6 +714,7 @@ static void connectionCallback(struct connection_handler_args arg) } pca->gotFirstConnection = TRUE; pca->nelements = ca_element_count(arg.chid); + pca->usedelements = 0; pca->dbrType = ca_field_type(arg.chid); if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) { link_action |= CA_MONITOR_NATIVE; @@ -773,6 +783,7 @@ static void eventCallback(struct event_handler_args arg) case DBR_TIME_DOUBLE: assert(pca->pgetNative); memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size); + pca->usedelements = arg.count; pca->gotInNative = TRUE; break; default: @@ -1031,15 +1042,15 @@ static void dbCaTask(void *arg) } } if (link_action & CA_MONITOR_NATIVE) { - size_t element_size; - - element_size = dbr_value_size[ca_field_type(pca->chid)]; + epicsMutexMustLock(pca->lock); - pca->pgetNative = dbCalloc(pca->nelements, element_size); + pca->elementSize = dbr_value_size[ca_field_type(pca->chid)]; + pca->pgetNative = dbCalloc(pca->nelements, pca->elementSize); epicsMutexUnlock(pca->lock); + status = ca_add_array_event( ca_field_type(pca->chid)+DBR_TIME_STRING, - ca_element_count(pca->chid), + 0, /* dynamic size */ pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index d5274c577..b50867eda 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -35,7 +35,7 @@ /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 - + typedef struct caLink { ELLNODE node; @@ -51,7 +51,9 @@ typedef struct caLink epicsTimeStamp timeStamp; /* The following have values after connection*/ short dbrType; - long nelements; + size_t elementSize; /* size of one element in pgetNative */ + unsigned long nelements; /* PVs max array size */ + unsigned long usedelements; /* currently used in pgetNative */ char hasReadAccess; char hasWriteAccess; char isConnected; From af1daea3e78bb20b691f398f4f309e1f3ee3fa67 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 10:46:20 -0400 Subject: [PATCH 110/204] dbContext: local CA variable length --- src/ioc/db/dbContext.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/dbContext.cpp b/src/ioc/db/dbContext.cpp index 124a836cf..c0f57b759 100644 --- a/src/ioc/db/dbContext.cpp +++ b/src/ioc/db/dbContext.cpp @@ -154,7 +154,8 @@ void dbContext::callStateNotify ( struct dbChannel * dbch, const struct db_field_log * pfl, cacStateNotify & notifyIn ) { - unsigned long size = dbr_size_n ( type, count ); + long realcount = (count==0)?dbChannelElements(dbch):count; + unsigned long size = dbr_size_n ( type, realcount ); if ( type > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); @@ -181,8 +182,13 @@ void dbContext::callStateNotify ( struct dbChannel * dbch, this->stateNotifyCacheSize = size; } void *pvfl = (void *) pfl; - int status = dbChannel_get ( dbch, static_cast ( type ), - this->pStateNotifyCache, static_cast ( count ), pvfl ); + int status; + if(count==0) /* fetch actual number of elements (dynamic array) */ + status = dbChannel_get_count( dbch, static_cast ( type ), + this->pStateNotifyCache, &realcount, pvfl ); + else /* fetch requested number of elements, truncated or zero padded */ + status = dbChannel_get( dbch, static_cast ( type ), + this->pStateNotifyCache, realcount, pvfl ); if ( status ) { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.exception ( guard, ECA_GETFAIL, @@ -190,7 +196,7 @@ void dbContext::callStateNotify ( struct dbChannel * dbch, } else { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.current ( guard, type, count, this->pStateNotifyCache ); + notifyIn.current ( guard, type, realcount, this->pStateNotifyCache ); } } From 44980a1dac7f104f56b5a02c99907914d2a84931 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 10:46:20 -0400 Subject: [PATCH 111/204] dbContextReadNotifyCache: variable length for CAC gets --- src/ioc/db/dbContextReadNotifyCache.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbContextReadNotifyCache.cpp b/src/ioc/db/dbContextReadNotifyCache.cpp index 700a07f7e..b172068be 100644 --- a/src/ioc/db/dbContextReadNotifyCache.cpp +++ b/src/ioc/db/dbContextReadNotifyCache.cpp @@ -61,27 +61,32 @@ void dbContextReadNotifyCache::callReadNotify ( return; } - if ( dbChannelElements(dbch) < 0 ) { + const long maxcount = dbChannelElements(dbch); + + if ( maxcount < 0 ) { notify.exception ( guard, ECA_BADCOUNT, "database has negetive element count", type, count); return; - } - if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) { + } else if ( count > (unsigned long)maxcount ) { notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; } - unsigned long size = dbr_size_n ( type, count ); + long realcount = (count==0)?maxcount:count; + unsigned long size = dbr_size_n ( type, realcount ); + privateAutoDestroyPtr ptr ( _allocator, size ); int status; { epicsGuardRelease < epicsMutex > unguard ( guard ); - status = dbChannel_get ( dbch, static_cast ( type ), - ptr.get (), static_cast ( count ), 0 ); + if ( count==0 ) + status = dbChannel_get_count ( dbch, (int)type, ptr.get(), &realcount, 0); + else + status = dbChannel_get ( dbch, (int)type, ptr.get (), realcount, 0 ); } if ( status ) { notify.exception ( guard, ECA_GETFAIL, @@ -90,7 +95,7 @@ void dbContextReadNotifyCache::callReadNotify ( } else { notify.completion ( - guard, type, count, ptr.get () ); + guard, type, realcount, ptr.get () ); } } From d5832354e8abc129ce4f3c24614dc2cbf94cc5e7 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 11:09:35 -0400 Subject: [PATCH 112/204] iocInit: Don't free LSET until scan tasks have stopped --- src/ioc/misc/iocInit.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 3bede8547..cce2c7938 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -637,7 +637,6 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, free(plink->value.pv_link.pvt); plink->type = PV_LINK; } - dbFreeLinkContents(plink); } if (precord->dset && @@ -660,6 +659,16 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { + int j; + + for (j = 0; j < pdbRecordType->no_links; j++) { + dbFldDes *pdbFldDes = + pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + + dbFreeLinkContents(plink); + } + epicsMutexDestroy(precord->mlok); } From 180f40c1f76b3c02bd38ff73b549f7c309612fe6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 11:09:35 -0400 Subject: [PATCH 113/204] dbLock: fix unlock w/o lock during iocInit --- src/ioc/db/dbLock.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index b248cba46..56f347abd 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -622,11 +622,13 @@ void dbLockCleanupRecords(dbBase *pdbbase) #endif } -/* Caller must lock both pfirst and psecond. +/* Called in two modes. + * During dbLockInitRecords w/ locker==NULL, then no mutex are locked. + * After dbLockInitRecords w/ locker!=NULL, then + * the caller must lock both pfirst and psecond. + * * Assumes that pfirst has been modified * to link to psecond. - * - * Note: locker may be NULL (during iocInit) */ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) { @@ -702,9 +704,9 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) ellDelete(&locker->locked, &B->lockernode); B->ownerlocker = NULL; epicsAtomicDecrIntT(&B->refcount); - } - epicsMutexUnlock(B->lock); + epicsMutexUnlock(B->lock); + } dbLockDecRef(B); /* last ref we hold */ From b6fbea9610f98fd32939e6c5dee53bc6f99f78b6 Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Tue, 18 Aug 2015 14:24:10 -0400 Subject: [PATCH 114/204] repeaterSubscribeTimer.patch from lp:1479316 --- src/ca/client/repeaterSubscribeTimer.cpp | 7 ++++++- src/ca/client/repeaterSubscribeTimer.h | 3 ++- src/ca/client/udpiiu.cpp | 4 ++-- src/ca/client/udpiiu.h | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ca/client/repeaterSubscribeTimer.cpp b/src/ca/client/repeaterSubscribeTimer.cpp index 69362da5f..66788c839 100644 --- a/src/ca/client/repeaterSubscribeTimer.cpp +++ b/src/ca/client/repeaterSubscribeTimer.cpp @@ -66,6 +66,8 @@ void repeaterSubscribeTimer::shutdown ( epicsTimerNotify::expireStatus repeaterSubscribeTimer:: expire ( const epicsTime & /* currentTime */ ) { + epicsGuard < epicsMutex > guard ( this->stateMutex ); + static const unsigned nTriesToMsg = 50; if ( this->attempts > nTriesToMsg && ! this->once ) { callbackManager mgr ( this->ctxNotify, this->cbMutex ); @@ -90,14 +92,17 @@ epicsTimerNotify::expireStatus repeaterSubscribeTimer:: } } -void repeaterSubscribeTimer::show ( unsigned /* level */ ) const +void repeaterSubscribeTimer::show ( unsigned /* level */ ) { + epicsGuard < epicsMutex > guard ( this->stateMutex ); + ::printf ( "repeater subscribe timer: attempts=%u registered=%u once=%u\n", this->attempts, this->registered, this->once ); } void repeaterSubscribeTimer::confirmNotify () { + epicsGuard < epicsMutex > guard ( this->stateMutex ); this->registered = true; } diff --git a/src/ca/client/repeaterSubscribeTimer.h b/src/ca/client/repeaterSubscribeTimer.h index 52f146d6e..d0f7a3fdb 100644 --- a/src/ca/client/repeaterSubscribeTimer.h +++ b/src/ca/client/repeaterSubscribeTimer.h @@ -64,12 +64,13 @@ public: epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void confirmNotify (); - void show ( unsigned level ) const; + void show ( unsigned level ); private: epicsTimer & timer; repeaterTimerNotify & iiu; epicsMutex & cbMutex; cacContextNotify & ctxNotify; + epicsMutex stateMutex; unsigned attempts; bool registered; bool once; diff --git a/src/ca/client/udpiiu.cpp b/src/ca/client/udpiiu.cpp index e4e8bd31f..f3f8a5552 100644 --- a/src/ca/client/udpiiu.cpp +++ b/src/ca/client/udpiiu.cpp @@ -1084,7 +1084,7 @@ bool udpiiu :: datagramFlush ( return true; } -void udpiiu :: show ( unsigned level ) const +void udpiiu :: show ( unsigned level ) { epicsGuard < epicsMutex > guard ( this->cacMutex ); @@ -1095,7 +1095,7 @@ void udpiiu :: show ( unsigned level ) const ::printf ( "Search Destination List with %u items\n", _searchDestList.count () ); if ( level > 2u ) { - tsDLIterConst < SearchDest > iter ( + tsDLIter < SearchDest > iter ( _searchDestList.firstIter () ); while ( iter.valid () ) { diff --git a/src/ca/client/udpiiu.h b/src/ca/client/udpiiu.h index 103547919..b3431cb11 100644 --- a/src/ca/client/udpiiu.h +++ b/src/ca/client/udpiiu.h @@ -108,7 +108,7 @@ public: epicsGuard < epicsMutex > & guard ); void shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); - void show ( unsigned level ) const; + void show ( unsigned level ); // exceptions class noSocket {}; From 9d00978176ecb5e09d6715fe4aab494a88c08324 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 14:33:44 -0400 Subject: [PATCH 115/204] src/ca/client: no need to remove const --- src/ca/client/repeaterSubscribeTimer.cpp | 2 +- src/ca/client/repeaterSubscribeTimer.h | 4 ++-- src/ca/client/udpiiu.cpp | 4 ++-- src/ca/client/udpiiu.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ca/client/repeaterSubscribeTimer.cpp b/src/ca/client/repeaterSubscribeTimer.cpp index 66788c839..948ccebd5 100644 --- a/src/ca/client/repeaterSubscribeTimer.cpp +++ b/src/ca/client/repeaterSubscribeTimer.cpp @@ -92,7 +92,7 @@ epicsTimerNotify::expireStatus repeaterSubscribeTimer:: } } -void repeaterSubscribeTimer::show ( unsigned /* level */ ) +void repeaterSubscribeTimer::show ( unsigned /* level */ ) const { epicsGuard < epicsMutex > guard ( this->stateMutex ); diff --git a/src/ca/client/repeaterSubscribeTimer.h b/src/ca/client/repeaterSubscribeTimer.h index d0f7a3fdb..fa4768499 100644 --- a/src/ca/client/repeaterSubscribeTimer.h +++ b/src/ca/client/repeaterSubscribeTimer.h @@ -64,13 +64,13 @@ public: epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void confirmNotify (); - void show ( unsigned level ); + void show ( unsigned level ) const; private: epicsTimer & timer; repeaterTimerNotify & iiu; epicsMutex & cbMutex; cacContextNotify & ctxNotify; - epicsMutex stateMutex; + mutable epicsMutex stateMutex; unsigned attempts; bool registered; bool once; diff --git a/src/ca/client/udpiiu.cpp b/src/ca/client/udpiiu.cpp index f3f8a5552..e4e8bd31f 100644 --- a/src/ca/client/udpiiu.cpp +++ b/src/ca/client/udpiiu.cpp @@ -1084,7 +1084,7 @@ bool udpiiu :: datagramFlush ( return true; } -void udpiiu :: show ( unsigned level ) +void udpiiu :: show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->cacMutex ); @@ -1095,7 +1095,7 @@ void udpiiu :: show ( unsigned level ) ::printf ( "Search Destination List with %u items\n", _searchDestList.count () ); if ( level > 2u ) { - tsDLIter < SearchDest > iter ( + tsDLIterConst < SearchDest > iter ( _searchDestList.firstIter () ); while ( iter.valid () ) { diff --git a/src/ca/client/udpiiu.h b/src/ca/client/udpiiu.h index b3431cb11..103547919 100644 --- a/src/ca/client/udpiiu.h +++ b/src/ca/client/udpiiu.h @@ -108,7 +108,7 @@ public: epicsGuard < epicsMutex > & guard ); void shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); - void show ( unsigned level ); + void show ( unsigned level ) const; // exceptions class noSocket {}; From b6aea68304e4b129777b8a996f24b44f376d9183 Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Tue, 18 Aug 2015 14:38:18 -0400 Subject: [PATCH 116/204] epicsSingletonMutex.patch from lp:1479316 --- src/libCom/cxxTemplates/epicsSingletonMutex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/cxxTemplates/epicsSingletonMutex.cpp b/src/libCom/cxxTemplates/epicsSingletonMutex.cpp index 9643a1dde..02d5d63c1 100644 --- a/src/libCom/cxxTemplates/epicsSingletonMutex.cpp +++ b/src/libCom/cxxTemplates/epicsSingletonMutex.cpp @@ -52,9 +52,9 @@ void SingletonUntyped :: incrRefCount ( PBuild pBuild ) void SingletonUntyped :: decrRefCount ( PDestroy pDestroy ) { - assert ( _refCount > 0 ); epicsGuard < epicsMutex > guard ( *pEPICSSigletonMutex ); + assert ( _refCount > 0 ); _refCount--; if ( _refCount == 0 ) { ( *pDestroy ) ( _pInstance ); From 6862ef6580b38d1ad2b3612bdef5a33521ae6aec Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Tue, 18 Aug 2015 14:39:27 -0400 Subject: [PATCH 117/204] timerQueueActive.patch from lp:1479316 --- src/libCom/timer/epicsTimer.cpp | 1 + src/libCom/timer/timerPrivate.h | 1 + src/libCom/timer/timerQueueActive.cpp | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/src/libCom/timer/epicsTimer.cpp b/src/libCom/timer/epicsTimer.cpp index 0fb8bacba..3cf334043 100644 --- a/src/libCom/timer/epicsTimer.cpp +++ b/src/libCom/timer/epicsTimer.cpp @@ -70,6 +70,7 @@ epicsTimerQueueActiveForC :: bool okToShare, unsigned priority ) : timerQueueActive ( refMgr, okToShare, priority ) { + timerQueueActive::start(); } epicsTimerQueueActiveForC::~epicsTimerQueueActiveForC () diff --git a/src/libCom/timer/timerPrivate.h b/src/libCom/timer/timerPrivate.h index 5533647e3..96f9b9eb7 100644 --- a/src/libCom/timer/timerPrivate.h +++ b/src/libCom/timer/timerPrivate.h @@ -134,6 +134,7 @@ class timerQueueActive : public epicsTimerQueueActive, public: typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr; timerQueueActive ( RefMgr &, bool okToShare, unsigned priority ); + void start (); epicsTimer & createTimer (); epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); void show ( unsigned int level ) const; diff --git a/src/libCom/timer/timerQueueActive.cpp b/src/libCom/timer/timerQueueActive.cpp index a1c25f4a0..961b6b050 100644 --- a/src/libCom/timer/timerQueueActive.cpp +++ b/src/libCom/timer/timerQueueActive.cpp @@ -49,6 +49,10 @@ timerQueueActive :: epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ), sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ), exitFlag ( false ), terminateFlag ( false ) +{ +} + +void timerQueueActive::start () { this->thread.start (); } From 869410d6f6248b67249025f9bb15b38424919917 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 15:46:22 -0400 Subject: [PATCH 118/204] libCom/osi: osiLocalAddr() avoid race on initialization --- src/libCom/osi/os/WIN32/osdNetIntf.c | 53 ++++++++++++++++---------- src/libCom/osi/os/default/osdNetIntf.c | 50 +++++++++++++++--------- 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/src/libCom/osi/os/WIN32/osdNetIntf.c b/src/libCom/osi/os/WIN32/osdNetIntf.c index 74025237f..bb50f97b0 100644 --- a/src/libCom/osi/os/WIN32/osdNetIntf.c +++ b/src/libCom/osi/os/WIN32/osdNetIntf.c @@ -40,49 +40,48 @@ #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" +#include "epicsThread.h" #include "epicsVersion.h" +static osiSockAddr osiLocalAddrResult; +static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT; + /* * osiLocalAddr () */ -epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr ( SOCKET socket ) +static void osiLocalAddrOnce ( void *raw ) { - static osiSockAddr addr; - static char init; + SOCKET *psocket = raw; + osiSockAddr addr; int status; INTERFACE_INFO *pIfinfo; - INTERFACE_INFO *pIfinfoList; + INTERFACE_INFO *pIfinfoList = NULL; unsigned nelem; DWORD numifs; DWORD cbBytesReturned; - if (init) { - return addr; - } - - init = 1; + memset ( (void *) &addr, '\0', sizeof ( addr ) ); addr.sa.sa_family = AF_UNSPEC; /* only valid for winsock 2 and above */ if ( wsaMajorVersion() < 2 ) { - return addr; + goto fail; } nelem = 10; pIfinfoList = (INTERFACE_INFO *) calloc ( nelem, sizeof (INTERFACE_INFO) ); if (!pIfinfoList) { - errlogPrintf ("calloc failed\n"); - return addr; + errlogPrintf ("calloc failed\n"); + goto fail; } - status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, NULL, 0, + status = WSAIoctl (*psocket, SIO_GET_INTERFACE_LIST, NULL, 0, (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), &cbBytesReturned, NULL, NULL); if (status != 0 || cbBytesReturned == 0) { - errlogPrintf ("WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); - free (pIfinfoList); - return addr; + errlogPrintf ("WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); + goto fail; } numifs = cbBytesReturned / sizeof(INTERFACE_INFO); @@ -109,12 +108,26 @@ epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr ( SOCKET socket ) addr.sa.sa_family = AF_INET; } - free (pIfinfoList); - return addr; + osiLocalAddrResult = addr; + return; } - free (pIfinfoList); - return addr; + errlogPrintf ( + "osiLocalAddr(): only loopback found\n"); +fail: + /* fallback to loopback */ + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + osiLocalAddrResult = addr; + + free ( pIfinfoList ); +} + +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) +{ + epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, (void*)&socket); + return osiLocalAddrResult; } /* diff --git a/src/libCom/osi/os/default/osdNetIntf.c b/src/libCom/osi/os/default/osdNetIntf.c index ab36fdf10..e3eb6ac66 100644 --- a/src/libCom/osi/os/default/osdNetIntf.c +++ b/src/libCom/osi/os/default/osdNetIntf.c @@ -23,13 +23,17 @@ #include "osiSock.h" #include "epicsAssert.h" #include "errlog.h" +#include "epicsThread.h" #ifdef DEBUG # define ifDepenDebugPrintf(argsInParen) printf argsInParen #else # define ifDepenDebugPrintf(argsInParen) #endif - + +static osiSockAddr osiLocalAddrResult; +static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT; + /* * Determine the size of an ifreq structure * Made difficult by the fact that addresses larger than the structure @@ -237,11 +241,11 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses /* * osiLocalAddr () */ -epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) +static void osiLocalAddrOnce (void *raw) { - static const unsigned nelem = 100; - static char init = 0; - static osiSockAddr addr; + SOCKET *psocket = raw; + const unsigned nelem = 100; + osiSockAddr addr; int status; struct ifconf ifconf; struct ifreq *pIfreqList; @@ -249,22 +253,18 @@ epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) struct ifreq *pIfreqListEnd; struct ifreq *pnextifreq; - if ( init ) { - return addr; - } - memset ( (void *) &addr, '\0', sizeof ( addr ) ); addr.sa.sa_family = AF_UNSPEC; pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) ); if ( ! pIfreqList ) { errlogPrintf ( "osiLocalAddr(): no memory to complete request\n" ); - return addr; + goto fail; } ifconf.ifc_len = nelem * sizeof ( *pIfreqList ); ifconf.ifc_req = pIfreqList; - status = socket_ioctl ( socket, SIOCGIFCONF, &ifconf ); + status = socket_ioctl ( *psocket, SIOCGIFCONF, &ifconf ); if ( status < 0 || ifconf.ifc_len == 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( @@ -272,8 +272,7 @@ epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) errlogPrintf ( "osiLocalAddr(): SIOCGIFCONF ioctl failed because \"%s\"\n", sockErrBuf ); - free ( pIfreqList ); - return addr; + goto fail; } pIfreqListEnd = (struct ifreq *) ( ifconf.ifc_len + (char *) ifconf.ifc_req ); @@ -300,7 +299,7 @@ epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) addrCpy.sa = pIfreqList->ifr_addr; - status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList ); + status = socket_ioctl ( *psocket, SIOCGIFFLAGS, pIfreqList ); if ( status < 0 ) { errlogPrintf ( "osiLocalAddr(): net intf flags fetch for %s failed\n", pIfreqList->ifr_name ); continue; @@ -321,11 +320,26 @@ epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s found\n", pIfreqList->ifr_name) ); - init = 1; - addr = addrCpy; - break; + osiLocalAddrResult = addrCpy; + free ( pIfreqList ); + return; } + errlogPrintf ( + "osiLocalAddr(): only loopback found\n"); +fail: + /* fallback to loopback */ + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + osiLocalAddrResult = addr; + free ( pIfreqList ); - return addr; +} + + +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) +{ + epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket); + return osiLocalAddrResult; } From 041423092f82ff1085d970ad5f062315394cb903 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 15:51:38 -0400 Subject: [PATCH 119/204] osdnetIntf.c cleanup indentation --- src/libCom/osi/os/WIN32/osdNetIntf.c | 184 +++++++++++++-------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/src/libCom/osi/os/WIN32/osdNetIntf.c b/src/libCom/osi/os/WIN32/osdNetIntf.c index bb50f97b0..fd6b74717 100644 --- a/src/libCom/osi/os/WIN32/osdNetIntf.c +++ b/src/libCom/osi/os/WIN32/osdNetIntf.c @@ -53,67 +53,67 @@ static void osiLocalAddrOnce ( void *raw ) { SOCKET *psocket = raw; osiSockAddr addr; - int status; - INTERFACE_INFO *pIfinfo; + int status; + INTERFACE_INFO *pIfinfo; INTERFACE_INFO *pIfinfoList = NULL; - unsigned nelem; - DWORD numifs; - DWORD cbBytesReturned; + unsigned nelem; + DWORD numifs; + DWORD cbBytesReturned; memset ( (void *) &addr, '\0', sizeof ( addr ) ); addr.sa.sa_family = AF_UNSPEC; - /* only valid for winsock 2 and above */ - if ( wsaMajorVersion() < 2 ) { + /* only valid for winsock 2 and above */ + if ( wsaMajorVersion() < 2 ) { goto fail; - } + } - nelem = 10; - pIfinfoList = (INTERFACE_INFO *) calloc ( nelem, sizeof (INTERFACE_INFO) ); - if (!pIfinfoList) { + nelem = 10; + pIfinfoList = (INTERFACE_INFO *) calloc ( nelem, sizeof (INTERFACE_INFO) ); + if (!pIfinfoList) { errlogPrintf ("calloc failed\n"); goto fail; } status = WSAIoctl (*psocket, SIO_GET_INTERFACE_LIST, NULL, 0, - (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), - &cbBytesReturned, NULL, NULL); + (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), + &cbBytesReturned, NULL, NULL); - if (status != 0 || cbBytesReturned == 0) { + if (status != 0 || cbBytesReturned == 0) { errlogPrintf ("WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); goto fail; } - numifs = cbBytesReturned / sizeof(INTERFACE_INFO); - for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ + numifs = cbBytesReturned / sizeof(INTERFACE_INFO); + for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ - /* - * dont use interfaces that have been disabled - */ - if (!(pIfinfo->iiFlags & IFF_UP)) { - continue; - } + /* + * dont use interfaces that have been disabled + */ + if (!(pIfinfo->iiFlags & IFF_UP)) { + continue; + } - /* - * dont use the loop back interface - */ - if (pIfinfo->iiFlags & IFF_LOOPBACK) { - continue; - } + /* + * dont use the loop back interface + */ + if (pIfinfo->iiFlags & IFF_LOOPBACK) { + continue; + } - addr.sa = pIfinfo->iiAddress.Address; + addr.sa = pIfinfo->iiAddress.Address; - /* Work around MS Winsock2 bugs */ + /* Work around MS Winsock2 bugs */ if (addr.sa.sa_family == 0) { addr.sa.sa_family = AF_INET; } osiLocalAddrResult = addr; return; - } + } errlogPrintf ( - "osiLocalAddr(): only loopback found\n"); + "osiLocalAddr(): only loopback found\n"); fail: /* fallback to loopback */ memset ( (void *) &addr, '\0', sizeof ( addr ) ); @@ -136,12 +136,12 @@ epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr) { - int status; - INTERFACE_INFO *pIfinfo; - INTERFACE_INFO *pIfinfoList; - unsigned nelem; - int numifs; - DWORD cbBytesReturned; + int status; + INTERFACE_INFO *pIfinfo; + INTERFACE_INFO *pIfinfoList; + unsigned nelem; + int numifs; + DWORD cbBytesReturned; osiSockAddrNode *pNewNode; if ( pMatchAddr->sa.sa_family == AF_INET ) { @@ -158,56 +158,56 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses } } - /* only valid for winsock 2 and above */ - if (wsaMajorVersion() < 2 ) { - fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n"); - return; - } + /* only valid for winsock 2 and above */ + if (wsaMajorVersion() < 2 ) { + fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n"); + return; + } - nelem = 10; - pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO)); - if(!pIfinfoList){ - return; - } + nelem = 10; + pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO)); + if(!pIfinfoList){ + return; + } - status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, - NULL, 0, - (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), - &cbBytesReturned, NULL, NULL); + status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, + NULL, 0, + (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), + &cbBytesReturned, NULL, NULL); - if (status != 0 || cbBytesReturned == 0) { - fprintf(stderr, "WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); - free(pIfinfoList); - return; - } + if (status != 0 || cbBytesReturned == 0) { + fprintf(stderr, "WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); + free(pIfinfoList); + return; + } - numifs = cbBytesReturned/sizeof(INTERFACE_INFO); - for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ + numifs = cbBytesReturned/sizeof(INTERFACE_INFO); + for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ - /* - * dont bother with interfaces that have been disabled - */ - if (!(pIfinfo->iiFlags & IFF_UP)) { - continue; - } - - if (pIfinfo->iiFlags & IFF_LOOPBACK) { + /* + * dont bother with interfaces that have been disabled + */ + if (!(pIfinfo->iiFlags & IFF_UP)) { continue; - } + } - /* - * work around WS2 bug - */ - if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { - if (pIfinfo->iiAddress.Address.sa_family == 0) { - pIfinfo->iiAddress.Address.sa_family = AF_INET; - } - } + if (pIfinfo->iiFlags & IFF_LOOPBACK) { + continue; + } - /* - * if it isnt a wildcarded interface then look for - * an exact match - */ + /* + * work around WS2 bug + */ + if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { + if (pIfinfo->iiAddress.Address.sa_family == 0) { + pIfinfo->iiAddress.Address.sa_family = AF_INET; + } + } + + /* + * if it isnt a wildcarded interface then look for + * an exact match + */ if (pMatchAddr->sa.sa_family != AF_UNSPEC) { if (pIfinfo->iiAddress.Address.sa_family != pMatchAddr->sa.sa_family) { continue; @@ -218,11 +218,11 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses if (pMatchAddr->sa.sa_family != AF_INET) { continue; } - if (pMatchAddr->ia.sin_addr.s_addr != htonl(INADDR_ANY)) { - if (pIfinfo->iiAddress.AddressIn.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr) { - continue; - } - } + if (pMatchAddr->ia.sin_addr.s_addr != htonl(INADDR_ANY)) { + if (pIfinfo->iiAddress.AddressIn.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr) { + continue; + } + } } pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); @@ -231,7 +231,7 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses return; } - if (pIfinfo->iiAddress.Address.sa_family == AF_INET && + if (pIfinfo->iiAddress.Address.sa_family == AF_INET && pIfinfo->iiFlags & IFF_BROADCAST) { const unsigned mask = pIfinfo->iiNetmask.AddressIn.sin_addr.s_addr; const unsigned bcast = pIfinfo->iiBroadcastAddress.AddressIn.sin_addr.s_addr; @@ -240,16 +240,16 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses pNewNode->addr.ia.sin_family = AF_INET; pNewNode->addr.ia.sin_addr.s_addr = result; pNewNode->addr.ia.sin_port = htons ( 0 ); - } + } else { pNewNode->addr.sa = pIfinfo->iiBroadcastAddress.Address; } - /* - * LOCK applied externally - */ + /* + * LOCK applied externally + */ ellAdd (pList, &pNewNode->node); - } + } - free (pIfinfoList); + free (pIfinfoList); } From 67aa96b957763bf156ea9f3bac5366ad813d11da Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Tue, 18 Aug 2015 15:51:38 -0400 Subject: [PATCH 120/204] freeListItemsAvail.patch from lp:1479316 --- src/libCom/freeList/freeListLib.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libCom/freeList/freeListLib.c b/src/libCom/freeList/freeListLib.c index 3ddd1331c..5e07ba9c3 100644 --- a/src/libCom/freeList/freeListLib.c +++ b/src/libCom/freeList/freeListLib.c @@ -148,6 +148,10 @@ epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt) epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt) { FREELISTPVT *pfl = pvt; - return pfl->nBlocksAvailable; + size_t nBlocksAvailable; + epicsMutexMustLock(pfl->lock); + nBlocksAvailable = pfl->nBlocksAvailable; + epicsMutexUnlock(pfl->lock); + return nBlocksAvailable; } From 35c07a958f4978c01b8d7a8ec64fa039f49cc57d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 19 Aug 2015 07:28:53 -0400 Subject: [PATCH 121/204] libCom/osi: osiLocalAddr() missing free() --- src/libCom/osi/os/WIN32/osdNetIntf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libCom/osi/os/WIN32/osdNetIntf.c b/src/libCom/osi/os/WIN32/osdNetIntf.c index fd6b74717..c508bfdfd 100644 --- a/src/libCom/osi/os/WIN32/osdNetIntf.c +++ b/src/libCom/osi/os/WIN32/osdNetIntf.c @@ -109,6 +109,7 @@ static void osiLocalAddrOnce ( void *raw ) } osiLocalAddrResult = addr; + free ( pIfinfoList ); return; } From 536c6e91ff0b35e984e5ea4f07bfb3fcce2b8f8f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Aug 2015 11:26:47 -0500 Subject: [PATCH 122/204] Export private dbLock*Ref() functions for tests --- src/ioc/db/dbLockPvt.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index 86d186619..f7e6a04c3 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -84,9 +84,10 @@ struct dbLocker { lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */ }; -lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */ -void dbLockIncRef(lockSet* ls); -void dbLockDecRef(lockSet *ls); +/* These are exported for testing only */ +epicsShareFunc lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */ +epicsShareFunc void dbLockIncRef(lockSet* ls); +epicsShareFunc void dbLockDecRef(lockSet *ls); /* Calling dbLockerPrepare directly is an internal * optimization used when dbLocker on the stack. From d890c8096129989022bc59db5519ff4cada84768 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 20 Aug 2015 15:52:30 -0400 Subject: [PATCH 123/204] dbLock: restore initialization of PV_LINK PV_LINK -> DB_LINK must happen in doResolveLinks after add_record --- src/ioc/db/dbLink.c | 8 ++++--- src/ioc/db/dbLock.c | 52 +++--------------------------------------- src/ioc/db/dbLockPvt.h | 1 + src/ioc/misc/iocInit.c | 4 ++++ 4 files changed, 13 insertions(+), 52 deletions(-) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 0a440cfcb..1d5ca9944 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -127,7 +127,7 @@ static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer, /***************************** Database Links *****************************/ -static long dbDbInitLink(struct link *plink, short dbfType) +static long dbDbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) { DBADDR dbaddr; long status; @@ -145,6 +145,8 @@ static long dbDbInitLink(struct link *plink, short dbfType) /* merging into the same lockset is deferred to the caller. * cf. initPVLinks() */ + dbLockSetMerge(NULL, precord, dbaddr.precord); + assert(precord->lset->plockSet==dbaddr.precord->lset->plockSet); return 0; } @@ -386,7 +388,7 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } -lset dbDb_lset = { dbDbRemoveLink, +lset dbDb_lset = { NULL, dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, @@ -403,7 +405,7 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { /* Make it a DB link if possible */ - if (!dbDbInitLink(plink, dbfType)) + if (!dbDbInitLink(precord, plink, dbfType)) return; } diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 56f347abd..b30d1a28e 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -516,53 +516,9 @@ static int createLockRecord(void* junk, DBENTRY* pdbentry) lrec->precord = prec; prec->lset = lrec; - return 0; -} -static int initPVLinks(void* junk, DBENTRY* pdbentry) -{ - size_t i; - dbRecordType *rtype=pdbentry->precordType; - dbCommon *prec = pdbentry->precnode->precord; - lockSet *A=prec->lset->plockSet; - - if(!A) { - A = prec->lset->plockSet = makeSet(); - ellAdd(&A->lockRecordList, &prec->lset->node); - } - - /* for each link originating from this record */ - for(i=0; ino_links; i++) { - DBADDR *paddr; - dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]]; - DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); - lockSet *B; - - if(plink->type!=PV_LINK) - continue; - - dbInitLink(prec, plink, pdesc->field_type); - - if(plink->type!=DB_LINK) - continue; - - paddr = (DBADDR*)plink->value.pv_link.pvt; - B = paddr->precord->lset->plockSet; - - if(A==B) { - /* these records are already in the same lockset */ - } else if(B) { - assert(A!=B); - dbLockSetMerge(NULL, prec, paddr->precord); - assert(prec->lset->plockSet==paddr->precord->lset->plockSet); - - } else { - /* fast merge paddr->precord into A */ - paddr->precord->lset->plockSet = A; - dbLockIncRef(A); - ellAdd(&A->lockRecordList, &paddr->precord->lset->node); - } - } + prec->lset->plockSet = makeSet(); + ellAdd(&prec->lset->plockSet->lockRecordList, &prec->lset->node); return 0; } @@ -570,10 +526,8 @@ void dbLockInitRecords(dbBase *pdbbase) { epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL); - /* create all lockRecords */ + /* create all lockRecords and lockSets */ forEachRecord(NULL, pdbbase, &createLockRecord); - /* create lockSets */ - forEachRecord(NULL, pdbbase, &initPVLinks); } static int freeLockRecord(void* junk, DBENTRY* pdbentry) diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index f7e6a04c3..58b92f7eb 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -10,6 +10,7 @@ #define DBLOCKPVT_H #include "dbLock.h" +#include "epicsSpin.h" /* enable additional error checking */ #define LOCKSET_DEBUG diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index cce2c7938..0b18c261d 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -495,6 +495,7 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, /* For all the links in the record type... */ for (j = 0; j < pdbRecordType->no_links; j++) { dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; + DBLINK *plink = (DBLINK*)((char*)precord + pdbFldDes->offset); if (ellCount(&precord->rdes->devList) > 0 && pdbFldDes->isDevLink) { devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); @@ -506,6 +507,9 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, } } } + + if (plink->type == PV_LINK) + dbInitLink(precord, plink, pdbFldDes->field_type); } } From 6fffcf998575a7d28ee456578fc5dcba98421a02 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 24 Aug 2015 10:32:37 -0500 Subject: [PATCH 124/204] Make dbLockTest work when LOCKSET_DEBUG undefined --- src/ioc/db/test/dbLockTest.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ioc/db/test/dbLockTest.c b/src/ioc/db/test/dbLockTest.c index 042917546..c51481d0d 100644 --- a/src/ioc/db/test/dbLockTest.c +++ b/src/ioc/db/test/dbLockTest.c @@ -151,7 +151,9 @@ static void testMultiLock(void) { dbCommon *prec[8]; dbLocker *plockA; +#ifdef LOCKSET_DEBUG epicsThreadId myself = epicsThreadGetIdSelf(); +#endif testDiag("Test multi-locker function (lock everything)"); @@ -404,7 +406,11 @@ static void testLinkNOP(void) MAIN(dbLockTest) { +#ifdef LOCKSET_DEBUG testPlan(99); +#else + testPlan(87); +#endif testSets(); testSingleLock(); testMultiLock(); From f21fce0b42908119bc557da65f490329259488c2 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 24 Aug 2015 10:47:51 -0500 Subject: [PATCH 125/204] Remove some unnecesary #includes --- src/ioc/db/callback.c | 1 - src/ioc/db/dbAccess.c | 1 - src/ioc/db/dbChannel.c | 1 - src/ioc/db/dbLink.c | 1 - src/ioc/db/dbLock.c | 4 ++-- src/ioc/db/dbNotify.c | 1 - src/ioc/db/dbScan.c | 1 - 7 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index 56a399ff6..f7b9b9e59 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -24,7 +24,6 @@ #include "dbDefs.h" #include "epicsAtomic.h" #include "epicsEvent.h" -#include "epicsExit.h" #include "epicsInterrupt.h" #include "epicsRingPointer.h" #include "epicsString.h" diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 3da0d627d..ffbfd8b47 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -32,7 +32,6 @@ #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" -#include "epicsSpin.h" #include "errMdef.h" #define epicsExportSharedSymbols diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 6c57040df..d60d729a0 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -19,7 +19,6 @@ #include "cantProceed.h" #include "epicsAssert.h" -#include "epicsExit.h" #include "epicsString.h" #include "errlog.h" #include "freeList.h" diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 1d5ca9944..5ffd4fc17 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -23,7 +23,6 @@ #include "cantProceed.h" #include "cvtFast.h" #include "dbDefs.h" -#include "epicsSpin.h" #include "ellLib.h" #include "epicsThread.h" #include "epicsTime.h" diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index b30d1a28e..b518ebea4 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -17,12 +17,12 @@ #include "dbDefs.h" #include "ellLib.h" #include "epicsAssert.h" +#include "epicsAtomic.h" #include "epicsMutex.h" #include "epicsPrint.h" +#include "epicsSpin.h" #include "epicsStdio.h" #include "epicsThread.h" -#include "epicsSpin.h" -#include "epicsAtomic.h" #include "errMdef.h" #define epicsExportSharedSymbols diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index ede8dd30c..043bf91d1 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -25,7 +25,6 @@ #include "ellLib.h" #include "epicsAssert.h" #include "epicsEvent.h" -#include "epicsExit.h" #include "epicsMutex.h" #include "epicsString.h" #include "epicsThread.h" diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index e0f2b3603..08a3cd3e2 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -25,7 +25,6 @@ #include "dbDefs.h" #include "ellLib.h" #include "epicsEvent.h" -#include "epicsExit.h" #include "epicsMutex.h" #include "epicsPrint.h" #include "epicsRingBytes.h" From a81d123ea5f3e26edd5404425cff08583d776188 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 24 Aug 2015 11:26:25 -0500 Subject: [PATCH 126/204] Undo s/printf/errlogPrintf/ in iocsh commands --- src/ioc/db/dbLock.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index b518ebea4..7822ea05c 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -894,20 +894,20 @@ long dblsr(char *recordname,int level) dbInitEntry(pdbbase,pdbentry); status = dbFindRecord(pdbentry,recordname); if(status) { - errlogPrintf("Record not found\n"); + printf("Record not found\n"); dbFinishEntry(pdbentry); - goto done; + return 0; } precord = pdbentry->precnode->precord; dbFinishEntry(pdbentry); plockRecord = precord->lset; - if(!plockRecord) goto done; /* too early (before iocInit) */ + if (!plockRecord) return 0; /* before iocInit */ plockSet = plockRecord->plockSet; } else { plockSet = (lockSet *)ellFirst(&lockSetsActive); } for( ; plockSet; plockSet = (lockSet *)ellNext(&plockSet->node)) { - errlogPrintf("Lock Set %lu %d members %d refs epicsMutexId %p\n", + printf("Lock Set %lu %d members %d refs epicsMutexId %p\n", plockSet->id,ellCount(&plockSet->lockRecordList),plockSet->refcount,plockSet->lock); if(level==0) { if(recordname) break; continue; } @@ -915,7 +915,7 @@ long dblsr(char *recordname,int level) plockRecord; plockRecord = (lockRecord *)ellNext(&plockRecord->node)) { precord = plockRecord->precord; pdbRecordType = precord->rdes; - errlogPrintf("%s\n",precord->name); + printf("%s\n",precord->name); if(level<=1) continue; for(link=0; (linkno_links) ; link++) { DBADDR *pdbAddr; @@ -923,25 +923,23 @@ long dblsr(char *recordname,int level) plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if(plink->type != DB_LINK) continue; pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); - errlogPrintf("\t%s",pdbFldDes->name); + printf("\t%s",pdbFldDes->name); if(pdbFldDes->field_type==DBF_INLINK) { - errlogPrintf("\t INLINK"); + printf("\t INLINK"); } else if(pdbFldDes->field_type==DBF_OUTLINK) { - errlogPrintf("\tOUTLINK"); + printf("\tOUTLINK"); } else if(pdbFldDes->field_type==DBF_FWDLINK) { - errlogPrintf("\tFWDLINK"); + printf("\tFWDLINK"); } - errlogPrintf(" %s %s", + printf(" %s %s", ((plink->value.pv_link.pvlMask&pvlOptPP)?" PP":"NPP"), msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); - errlogPrintf(" %s\n",pdbAddr->precord->name); + printf(" %s\n",pdbAddr->precord->name); } } if(recordname) break; } -done: - errlogFlush(); - return(0); + return 0; } long dbLockShowLocked(int level) @@ -949,21 +947,19 @@ long dbLockShowLocked(int level) int indListType; lockSet *plockSet; - errlogPrintf("lockSets %d listTypeFree %d\n", - ellCount(&lockSetsActive), + printf("Active lockSets: %d\n", ellCount(&lockSetsActive)); #ifndef LOCKSET_NOFREE - ellCount(&lockSetsFree) -#else - -1 + printf("Free lockSets: %d\n", ellCount(&lockSetsFree)); #endif - ); /*Even if failure on lockSetModifyLock will continue */ for(indListType=0; indListType <= 1; ++indListType) { plockSet = (lockSet *)ellFirst(&lockSetsActive); if(plockSet) { - if(indListType==0) errlogPrintf("listTypeScanLock\n"); - else errlogPrintf("listTypeRecordLock\n"); + if (indListType==0) + printf("listTypeScanLock\n"); + else + printf("listTypeRecordLock\n"); } while(plockSet) { epicsMutexLockStatus status; @@ -977,8 +973,7 @@ long dbLockShowLocked(int level) plockSet = (lockSet *)ellNext(&plockSet->node); } } - errlogFlush(); - return(0); + return 0; } int * dbLockSetAddrTrace(dbCommon *precord) From 696e00eec9935ad439ff0cd14066a7212c6fea14 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 24 Aug 2015 12:18:23 -0500 Subject: [PATCH 127/204] Cleanup in callback.c --- src/ioc/db/callback.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index b15220469..351d1af07 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -92,7 +92,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2}; int callbackSetQueueSize(int size) { if (callbackIsInit) { - errlogPrintf("Callback system already initialized\n"); + fprintf(stderr, "Callback system already initialized\n"); return -1; } callbackQueueSize = size; @@ -102,7 +102,7 @@ int callbackSetQueueSize(int size) int callbackParallelThreads(int count, const char *prio) { if (callbackIsInit) { - errlogPrintf("Callback system already initialized\n"); + fprintf(stderr, "Callback system already initialized\n"); return -1; } @@ -112,7 +112,7 @@ int callbackParallelThreads(int count, const char *prio) count = callbackParallelThreadsDefault; if (count < 1) count = 1; - if (!prio || strcmp(prio, "") == 0 || strcmp(prio, "*") == 0) { + if (!prio || *prio == 0 || strcmp(prio, "*") == 0) { int i; for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { @@ -121,30 +121,30 @@ int callbackParallelThreads(int count, const char *prio) } else { dbMenu *pdbMenu; + int i; if (!pdbbase) { - errlogPrintf("callbackParallelThreads: pdbbase not set\n"); + fprintf(stderr, "callbackParallelThreads: pdbbase not set\n"); return -1; } + /* Find prio in menuPriority */ pdbMenu = dbFindMenu(pdbbase, "menuPriority"); - if (pdbMenu) { - int i, gotMatch = 0; - - for (i = 0; i < pdbMenu->nChoice; i++) { - gotMatch = (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i])==0) ? TRUE : FALSE; - if (gotMatch) - break; - } - if (gotMatch) { - callbackQueue[i].threadsConfigured = count; - return 0; - } - else { - errlogPrintf("Unknown priority \"%s\"\n", prio); - return -1; - } + if (!pdbMenu) { + fprintf(stderr, "callbackParallelThreads: No Priority menu\n"); + return -1; } + + for (i = 0; i < pdbMenu->nChoice; i++) { + if (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i]) == 0) + goto found; + } + fprintf(stderr, "callbackParallelThreads: " + "Unknown priority \"%s\"\n", prio); + return -1; + +found: + callbackQueue[i].threadsConfigured = count; } return 0; } From 717d7ff4eb7010aaabf65cb0f460f7afd007e9ad Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 25 Aug 2015 18:00:39 -0400 Subject: [PATCH 128/204] db/test: dbStressTest conditional TIME_STATS --- src/ioc/db/test/dbStressLock.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/test/dbStressLock.c b/src/ioc/db/test/dbStressLock.c index 7aa33100c..f2783bc4e 100644 --- a/src/ioc/db/test/dbStressLock.c +++ b/src/ioc/db/test/dbStressLock.c @@ -39,6 +39,10 @@ #include "xRecord.h" +#if defined(CLOCK_MONOTONIC) +# define TIME_STATS +#endif + #define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B); #define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B); @@ -59,8 +63,10 @@ static dbCommon **precords; typedef struct { int id; unsigned long N[3]; +#ifdef TIME_STATS double X[3]; double X2[3]; +#endif unsigned int done; epicsEventId donevent; @@ -153,16 +159,22 @@ void doreTarget(workerPriv *p) static void worker(void *raw) { +#ifdef TIME_STATS struct timespec before; +#endif workerPriv *priv = raw; testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf()); +#ifdef TIME_STATS clock_gettime(CLOCK_MONOTONIC, &before); +#endif while(!priv->done) { double sel = getRand(); +#ifdef TIME_STATS struct timespec after; +#endif double duration; int act; @@ -177,15 +189,19 @@ void worker(void *raw) act = 2; } +#ifdef TIME_STATS clock_gettime(CLOCK_MONOTONIC, &after); duration = (double)((long)after.tv_nsec - (long)before.tv_nsec); duration *= 1e-9; duration += (double)(after.tv_sec - before.tv_sec); +#endif priv->N[act]++; +#ifdef TIME_STATS priv->X[act] += duration; priv->X2[act] += duration*duration; +#endif } epicsEventMustTrigger(priv->donevent); @@ -198,12 +214,11 @@ MAIN(dbStressTest) unsigned int i; workerPriv *priv; char *nwork=getenv("NWORK"); - struct timespec seed; + epicsTimeStamp seed; - testPlan(95); + epicsTimeGetCurrent(&seed); - clock_gettime(CLOCK_REALTIME, &seed); - srand(seed.tv_nsec); + srand(seed.nsec); if(nwork) { long val = 0; @@ -212,6 +227,8 @@ MAIN(dbStressTest) nworkers = val; } + testPlan(80+nworkers*3); + priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory"); testDiag("lock set stress test"); @@ -319,8 +336,10 @@ MAIN(dbStressTest) for(i=0; i0); testOk1(priv[i].N[1]>0); From cd79a7b53d0c6eb1fc7e0eb09a275a3535397631 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 27 Aug 2015 10:30:42 -0500 Subject: [PATCH 129/204] dbCa: use dbf_type_to_DBR_TIME() macro --- src/ioc/db/dbCa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index caf0addbb..93104fc03 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -1038,7 +1038,7 @@ static void dbCaTask(void *arg) pca->pgetNative = dbCalloc(pca->nelements, element_size); epicsMutexUnlock(pca->lock); status = ca_add_array_event( - ca_field_type(pca->chid)+DBR_TIME_STRING, + dbf_type_to_DBR_TIME(ca_field_type(pca->chid)), ca_element_count(pca->chid), pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0); if (status != ECA_NORMAL) { From 15db30ba7931dbe3a20066e641bd4507de01cf17 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 27 Aug 2015 13:46:22 -0400 Subject: [PATCH 130/204] dbUnitTest: add testMonitor Synchronize unittests by waiting for monitor updates --- src/ioc/db/dbUnitTest.c | 113 +++++++++++++++++++++++++++++++++++++++- src/ioc/db/dbUnitTest.h | 20 +++++++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index 48d92a7fd..8e23ab691 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -16,18 +16,33 @@ #include "epicsUnitTest.h" #include "osiFileName.h" #include "registry.h" +#include "epicsEvent.h" #define epicsExportSharedSymbols #include "dbAccess.h" #include "dbBase.h" +#include "dbChannel.h" +#include "dbEvent.h" #include "dbStaticLib.h" #include "dbUnitTest.h" #include "initHooks.h" #include "iocInit.h" +static dbEventCtx testEvtCtx; +static epicsMutexId testEvtLock; +static ELLLIST testEvtList; /* holds testMonitor::node */ + +struct testMonitor { + ELLNODE node; + dbEventSubscription sub; + epicsEventId event; + unsigned count; +}; + void testdbPrepare(void) { - /* No-op at the moment */ + if(!testEvtLock) + testEvtLock = epicsMutexMustCreate(); } void testdbReadDatabase(const char* file, @@ -46,10 +61,21 @@ void testIocInitOk(void) { if(iocBuildIsolated() || iocRun()) testAbort("Failed to start up test database"); + if(!(testEvtCtx=db_init_events())) + testAbort("Failed to initialize test dbEvent context"); + if(DB_EVENT_OK!=db_start_events(testEvtCtx, "CAS-test", NULL, NULL, epicsThreadPriorityCAServerLow)) + testAbort("Failed to start test dbEvent context"); } void testIocShutdownOk(void) { + epicsMutexMustLock(testEvtLock); + if(ellCount(&testEvtList)) + testDiag("Warning, testing monitors still active at testIocShutdownOk()"); + epicsMutexUnlock(testEvtLock); + + db_close_events(testEvtCtx); + testEvtCtx = NULL; if(iocShutdown()) testAbort("Failed to shutdown test database"); } @@ -199,3 +225,88 @@ dbCommon* testdbRecordPtr(const char* pv) return addr.precord; } + +static +void testmonupdate(void *user_arg, struct dbChannel *chan, + int eventsRemaining, struct db_field_log *pfl) +{ + testMonitor *mon = user_arg; + + epicsMutexMustLock(testEvtLock); + mon->count++; + epicsMutexUnlock(testEvtLock); + epicsEventMustTrigger(mon->event); +} + +testMonitor* testMonitorCreate(const char* pvname, unsigned mask, unsigned opt) +{ + long status; + testMonitor *mon; + dbChannel *chan; + assert(testEvtCtx); + + mon = callocMustSucceed(1, sizeof(*mon), "testMonitorCreate"); + + mon->event = epicsEventMustCreate(epicsEventEmpty); + + chan = dbChannelCreate(pvname); + if(!chan) + testAbort("testMonitorCreate - dbChannelCreate(\"%s\") fails", pvname); + if(!!(status=dbChannelOpen(chan))) + testAbort("testMonitorCreate - dbChannelOpen(\"%s\") fails w/ %ld", pvname, status); + + mon->sub = db_add_event(testEvtCtx, chan, &testmonupdate, mon, mask); + if(!mon->sub) + testAbort("testMonitorCreate - db_add_event(\"%s\") fails", pvname); + + db_event_enable(mon->sub); + + epicsMutexMustLock(testEvtLock); + ellAdd(&testEvtList, &mon->node); + epicsMutexUnlock(testEvtLock); + + return mon; +} + +void testMonitorDestroy(testMonitor *mon) +{ + if(!mon) return; + + db_event_disable(mon->sub); + + epicsMutexMustLock(testEvtLock); + ellDelete(&testEvtList, &mon->node); + epicsMutexUnlock(testEvtLock); + + db_cancel_event(mon->sub); + + epicsEventDestroy(mon->event); + + free(mon); +} + +void testMonitorWait(testMonitor *mon) +{ + switch(epicsEventWaitWithTimeout(mon->event, 10.0)) + { + case epicsEventOK: + return; + case epicsEventWaitTimeout: + default: + testAbort("testMonitorWait() exceeds timeout"); + } +} + +unsigned testMonitorCount(testMonitor *mon, unsigned reset) +{ + unsigned count; + epicsMutexMustLock(testEvtLock); + count = mon->count; + if(reset) { + mon->count = 0; + epicsEventWaitWithTimeout(mon->event, 0); /* clear the event */ + } + epicsMutexUnlock(testEvtLock); + return count; +} + diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h index 80867dc9d..f6145d505 100644 --- a/src/ioc/db/dbUnitTest.h +++ b/src/ioc/db/dbUnitTest.h @@ -57,6 +57,26 @@ epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list epicsShareFunc dbCommon* testdbRecordPtr(const char* pv); +typedef struct testMonitor testMonitor; + +/* Begin monitoring the named PV for changes */ +epicsShareFunc testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt); +/* End monitoring */ +epicsShareFunc void testMonitorDestroy(testMonitor*); +/* Return immediately if it has been updated since create, last wait, + * or reset (count w/ reset=1). + * Otherwise, block until the value of the target PV is updated. + */ +epicsShareFunc void testMonitorWait(testMonitor*); +/* Return the number of monitor events which have occured since create, + * or a pervious reset (called reset=1). + * Calling w/ reset=0 only returns the count. + * Calling w/ reset=1 resets the count to zero and ensures that the next + * wait will block unless subsequent events occur. Returns the previous + * count. + */ +epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset); + #ifdef __cplusplus } #endif From 4544be090d8ebf985d41a75afb2e40fe07cd6a9b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 27 Aug 2015 12:13:49 -0400 Subject: [PATCH 131/204] std/rec/test: add linkRetargetLinkTest --- src/std/rec/test/Makefile | 7 ++ src/std/rec/test/epicsRunRecordTests.c | 3 + src/std/rec/test/linkRetargetLink.db | 17 +++++ src/std/rec/test/linkRetargetLinkTest.c | 86 +++++++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 src/std/rec/test/linkRetargetLink.db create mode 100644 src/std/rec/test/linkRetargetLinkTest.c diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 6b28b5a27..07750b70d 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -26,6 +26,13 @@ testHarness_SRCS += arrayOpTest.c TESTFILES += ../arrayOpTest.db TESTS += arrayOpTest +TESTPROD_HOST += linkRetargetLinkTest +linkRetargetLinkTest_SRCS += linkRetargetLinkTest.c +linkRetargetLinkTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += linkRetargetLinkTest.c +TESTFILES += ../linkRetargetLink.db +TESTS += linkRetargetLinkTest + TARGETS += $(COMMON_DIR)/asTestIoc.dbd asTestIoc_DBD += base.dbd asTestIoc_DBD += asTest.dbd diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c index 1a5858d76..3476d2138 100644 --- a/src/std/rec/test/epicsRunRecordTests.c +++ b/src/std/rec/test/epicsRunRecordTests.c @@ -15,6 +15,7 @@ int analogMonitorTest(void); int arrayOpTest(void); int asTest(void); +int linkRetargetLinkTest(void); void epicsRunRecordTests(void) { @@ -26,5 +27,7 @@ void epicsRunRecordTests(void) runTest(asTest); + runTest(linkRetargetLinkTest); + epicsExit(0); /* Trigger test harness */ } diff --git a/src/std/rec/test/linkRetargetLink.db b/src/std/rec/test/linkRetargetLink.db new file mode 100644 index 000000000..3306e43ea --- /dev/null +++ b/src/std/rec/test/linkRetargetLink.db @@ -0,0 +1,17 @@ +record(ai, "rec:ai") { + field(INP, "0") +} +record(ai, "rec:src1") { + field(VAL, "1") +} +record(ai, "rec:src2") { + field(VAL, "2") +} +record(stringout, "rec:link1") { + field(VAL, "rec:src1") + field(OUT, "rec:ai.INP CA") +} +record(stringout, "rec:link2") { + field(VAL, "rec:src2 CP") + field(OUT, "rec:ai.INP CA") +} diff --git a/src/std/rec/test/linkRetargetLinkTest.c b/src/std/rec/test/linkRetargetLinkTest.c new file mode 100644 index 000000000..79e271a83 --- /dev/null +++ b/src/std/rec/test/linkRetargetLinkTest.c @@ -0,0 +1,86 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + * + * Test using several stringout records to retarget the link of another record + */ + +#include + +#include "dbAccess.h" + +#include "dbUnitTest.h" +#include "errlog.h" +#include "epicsThread.h" + +#include "testMain.h" + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void testRetarget(void) +{ + testMonitor *lnkmon, *valmon; + + testdbPrepare(); + + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("linkRetargetLink.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + lnkmon = testMonitorCreate("rec:ai.INP", DBE_VALUE, 0); + valmon = testMonitorCreate("rec:ai", DBE_VALUE, 0); + + /* initially rec:ai.INP is CONSTANT */ + + testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 0.0); + testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "0"); + + /* rec:ai.INP becomes DB_LINK, but no processing is triggered */ + testdbPutFieldOk("rec:link1.PROC", DBF_LONG, 0); + + testMonitorWait(lnkmon); + + testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 0.0); + testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "rec:src1 NPP NMS"); + + /* trigger a read from rec:ai.INP */ + testdbPutFieldOk("rec:ai.PROC", DBF_LONG, 0); + + testMonitorWait(valmon); + + testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 1.0); + + /* rec:ai.INP becomes CA_LINK w/ CP, processing is triggered */ + testdbPutFieldOk("rec:link2.PROC", DBF_LONG, 0); + + testMonitorWait(lnkmon); + testMonitorWait(valmon); + + testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 2.0); + testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "rec:src2 CP NMS"); + + testMonitorDestroy(lnkmon); + testMonitorDestroy(valmon); + + testIocShutdownOk(); + + testdbCleanup(); +} + +MAIN(linkRetargetLinkTest) +{ + testPlan(0); + testRetarget(); + return testDone(); +} From 94f00cb0c6f751a6fa3964de9d0b41603ee70cf0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 27 Aug 2015 12:44:50 -0400 Subject: [PATCH 132/204] dbCa: more verbose error message in eventCallback --- src/ioc/db/dbCa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 93104fc03..3939cec7a 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -776,7 +776,8 @@ static void eventCallback(struct event_handler_args arg) pca->gotInNative = TRUE; break; default: - errMessage(-1, "dbCa: eventCallback Logic Error\n"); + errlogPrintf("dbCa: eventCallback Logic Error. dbr=%ld dbf=%d\n", + arg.type, ca_field_type(pca->chid)); break; } pdbr_time_double = (struct dbr_time_double *)arg.dbr; From 51f0a4509b85341ae5d35a35f0d8c85c80255aa1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 27 Aug 2015 15:43:05 -0400 Subject: [PATCH 133/204] std/rec/test: missing testdbPrepare() --- src/std/rec/test/arrayOpTest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/std/rec/test/arrayOpTest.c b/src/std/rec/test/arrayOpTest.c index 6d5ce4fc2..27c0becdd 100644 --- a/src/std/rec/test/arrayOpTest.c +++ b/src/std/rec/test/arrayOpTest.c @@ -26,6 +26,8 @@ static void testGetPutArray(void) epicsInt32 *pbtr; waveformRecord *prec; + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); From 1587c44d7951aa92818a7f3a2dc963bad7fd4e51 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 28 Aug 2015 15:19:23 -0500 Subject: [PATCH 134/204] db/test: Add missing dependency --- src/ioc/db/test/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index afed4f7b0..17b4518f7 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -148,5 +148,6 @@ include $(TOP)/configure/RULES xRecord$(DEP): $(COMMON_DIR)/xRecord.h dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h +dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h From 9331ca3f44b8f1992f3f4d580f33614e72760d0a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 31 Aug 2015 11:17:34 -0400 Subject: [PATCH 135/204] dbLockTest: check additional recursive case --- src/ioc/db/test/dbLockTest.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/test/dbLockTest.c b/src/ioc/db/test/dbLockTest.c index c51481d0d..04331267c 100644 --- a/src/ioc/db/test/dbLockTest.c +++ b/src/ioc/db/test/dbLockTest.c @@ -216,6 +216,15 @@ static void testMultiLock(void) testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,myself); #endif + /* recursive locking of individual records is allowed */ + dbScanLock(prec[0]); + testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3); + dbScanUnlock(prec[0]); + + /* recursive locking of dbScanLock isn't + * dbScanLockMany(plockA); <-- would fail + */ + dbScanUnlockMany(plockA); testDiag("After locker unlocked"); @@ -407,9 +416,9 @@ static void testLinkNOP(void) MAIN(dbLockTest) { #ifdef LOCKSET_DEBUG - testPlan(99); + testPlan(100); #else - testPlan(87); + testPlan(88); #endif testSets(); testSingleLock(); From 87999c7047d5414d1a82e6551ded82fe6c13f605 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 31 Aug 2015 11:20:35 -0400 Subject: [PATCH 136/204] dbLock: better error check when recursive locking attempted --- src/ioc/db/dbLock.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 7822ea05c..8a02adabf 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -390,6 +390,11 @@ void dbScanLockMany(dbLocker* locker) const epicsThreadId myself = epicsThreadGetIdSelf(); #endif + if(ellCount(&locker->locked)!=0) { + cantProceed("dbScanLockMany(%p) already locked. Recursive locking not allowed", locker); + return; + } + retry: assert(ellCount(&locker->locked)==0); dbLockUpdateRefs(locker, 1); From 895b415968c622eb842c04fb087a4841e60c6641 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 31 Aug 2015 11:29:46 -0500 Subject: [PATCH 137/204] dbCaLinkTest fixes * Attempt to fix Windows build failures * Use C-style comments, not C++ --- src/ioc/db/test/dbCaLinkTest.c | 43 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index c0b5f7b9d..2e086e6ce 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -23,17 +23,20 @@ #include "dbAccess.h" #include "epicsStdio.h" #include "dbEvent.h" -/* hackish duplication since we can't include db_access.h here */ -typedef void* chid; + +/* Declarations from cadef.h and db_access.h which we can't include here */ +typedef void * chid; +epicsShareExtern const unsigned short dbr_value_size[]; +epicsShareExtern short epicsShareAPI ca_field_type (chid chan); #define MAX_UNITS_SIZE 8 + #include "dbCaPvt.h" #include "errlog.h" +#include "testMain.h" #include "xRecord.h" #include "arrRecord.h" -#include "testMain.h" - #define testOp(FMT,A,OP,B) testOk((A)OP(B), #A " ("FMT") " #OP " " #B " ("FMT")", A,B) void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); @@ -46,7 +49,7 @@ void waitCB(void *unused) { if(waitEvent) epicsEventMustTrigger(waitEvent); - waitCounter++; //TODO: atomic + waitCounter++; /* TODO: atomic */ } static @@ -88,6 +91,7 @@ static void putLink(DBLINK *plink, short dbr, const void*buf, long nReq) { long ret; + waitEvent = epicsEventMustCreate(epicsEventEmpty); ret = dbCaPutLinkCallback(plink, dbr, buf, nReq, @@ -108,6 +112,7 @@ static void testNativeLink(void) DBLINK *psrclnk; epicsInt32 temp; long nReq; + testDiag("Link to a scalar numeric field"); testdbPrepare(); @@ -125,7 +130,7 @@ static void testNativeLink(void) ptarg= (xRecord*)testdbRecordPtr("target"); psrclnk = &psrc->lnk; - // ensure this is really a CA link + /* ensure this is really a CA link */ testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg)); testOk1(psrclnk->type==CA_LINK); @@ -140,7 +145,7 @@ static void testNativeLink(void) waitForUpdate(psrclnk); dbScanLock((dbCommon*)psrc); - // local CA_LINK connects immediately + /* local CA_LINK connects immediately */ testOk1(dbCaIsLinkConnected(psrclnk)); nReq = 422; testOk1(dbCaGetNelements(psrclnk, &nReq)==0); @@ -171,6 +176,7 @@ static void testStringLink(void) DBLINK *psrclnk; char temp[MAX_STRING_SIZE]; long nReq; + testDiag("Link to a string field"); testdbPrepare(); @@ -188,7 +194,7 @@ static void testStringLink(void) ptarg= (xRecord*)testdbRecordPtr("target"); psrclnk = &psrc->lnk; - // ensure this is really a CA link + /* ensure this is really a CA link */ testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg)); testOk1(psrclnk->type==CA_LINK); @@ -203,7 +209,7 @@ static void testStringLink(void) waitForUpdate(psrclnk); dbScanLock((dbCommon*)psrc); - // local CA_LINK connects immediately + /* local CA_LINK connects immediately */ testOk1(dbCaIsLinkConnected(psrclnk)); nReq = 1; @@ -232,6 +238,7 @@ static void wasproc(xRecord *prec) static void testCP(void) { xRecord *psrc, *ptarg; + testDiag("Link CP modifier"); testdbPrepare(); @@ -258,7 +265,7 @@ static void testCP(void) epicsEventMustWait(waitEvent); dbScanLock((dbCommon*)psrc); - testOp("%u",waitCounter,==,1); // initial processing + testOp("%u",waitCounter,==,1); /* initial processing */ dbScanUnlock((dbCommon*)psrc); dbScanLock((dbCommon*)ptarg); @@ -269,7 +276,7 @@ static void testCP(void) epicsEventMustWait(waitEvent); dbScanLock((dbCommon*)psrc); - testOp("%u",waitCounter,==,2); // process due to monitor update + testOp("%u",waitCounter,==,2); /* process due to monitor update */ dbScanUnlock((dbCommon*)psrc); testIocShutdownOk(); @@ -299,6 +306,7 @@ static void checkArray(const char *msg, int match = 1; unsigned i; epicsInt32 x, *b; + for(b=buf,x=first,i=0;ivalue.pv_link.pvt; + if(lnk->type!=CA_LINK || !pca->pputNative) return; epicsMutexMustLock(pca->lock); @@ -449,6 +456,7 @@ static void softarr(arrRecord *prec) { long nReq = prec->nelm; long status = dbGetLink(&prec->inp, DBR_DOUBLE, prec->bptr, NULL, &nReq); + if(status) { testFail("dbGetLink() -> %ld", status); } else { @@ -465,6 +473,7 @@ static void testreTargetTypeChange(void) arrRecord *psrc, *ptarg1, *ptarg2; double *bufsrc, *buftarg1; epicsInt32 *buftarg2; + testDiag("Retarget an link to a PV with a different type DOUBLE->LONG"); testdbPrepare(); @@ -489,7 +498,7 @@ static void testreTargetTypeChange(void) testIocInitOk(); eltc(1); - epicsEventMustWait(waitEvent); // wait for initial processing + epicsEventMustWait(waitEvent); /* wait for initial processing */ bufsrc = psrc->bptr; buftarg1= ptarg1->bptr; @@ -509,7 +518,7 @@ static void testreTargetTypeChange(void) db_post_events(ptarg1, ptarg1->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); dbScanUnlock((dbCommon*)ptarg1); - epicsEventMustWait(waitEvent); // wait for update + epicsEventMustWait(waitEvent); /* wait for update */ dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); @@ -519,7 +528,7 @@ static void testreTargetTypeChange(void) testDiag("Retarget"); testdbPutFieldOk("source.INP", DBR_STRING, "target2 CP"); - epicsEventMustWait(waitEvent); // wait for update + epicsEventMustWait(waitEvent); /* wait for update */ dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); From f2ff270644c3c5d1dd14baee7a8f16993e22f49b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 31 Aug 2015 15:55:39 -0500 Subject: [PATCH 138/204] Fix comment --- src/ioc/db/test/dbLockTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbLockTest.c b/src/ioc/db/test/dbLockTest.c index 04331267c..39e21a650 100644 --- a/src/ioc/db/test/dbLockTest.c +++ b/src/ioc/db/test/dbLockTest.c @@ -221,7 +221,7 @@ static void testMultiLock(void) testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3); dbScanUnlock(prec[0]); - /* recursive locking of dbScanLock isn't + /* recursive locking with dbScanLockMany() isn't * dbScanLockMany(plockA); <-- would fail */ From 8ce86bb061351be1ead6586252a1a795819b607e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 31 Aug 2015 16:08:08 -0500 Subject: [PATCH 139/204] Undefine LOCKSET_DEBUG & LOCKSET_NOFREE --- src/ioc/db/dbLockPvt.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index 58b92f7eb..8e19f3f31 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -1,8 +1,7 @@ /*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Brookhaven * National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -12,12 +11,14 @@ #include "dbLock.h" #include "epicsSpin.h" -/* enable additional error checking */ -#define LOCKSET_DEBUG -/* disable the free list for lockSets */ -#define LOCKSET_NOFREE -/* disable use of recomputeCnt optimization */ -/*#define LOCKSET_NOCNT*/ +/* Define to enable additional error checking */ +#undef LOCKSET_DEBUG + +/* Define to disable the free list for lockSets */ +#undef LOCKSET_NOFREE + +/* Define to disable use of recomputeCnt optimization */ +#undef LOCKSET_NOCNT /* except for refcount (and lock), all members of dbLockSet * are guarded by its lock. From 8aea808b1da9f5ab72b515a7388ee2bc542ea4fc Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 7 Sep 2015 10:50:48 -0400 Subject: [PATCH 140/204] libCom: cross mingw imports posix time.h by default at least mingw 4.9.1 for Debian 8 i386 host. --- src/ioc/db/test/dbStressLock.c | 4 ++-- src/libCom/osi/osiClockTime.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/test/dbStressLock.c b/src/ioc/db/test/dbStressLock.c index 354268674..3f3a0a898 100644 --- a/src/ioc/db/test/dbStressLock.c +++ b/src/ioc/db/test/dbStressLock.c @@ -40,7 +40,7 @@ #include "xRecord.h" -#if defined(CLOCK_MONOTONIC) +#if defined(CLOCK_MONOTONIC) && !defined(_WIN32) # define TIME_STATS #endif @@ -175,8 +175,8 @@ void worker(void *raw) double sel = getRand(); #ifdef TIME_STATS struct timespec after; -#endif double duration; +#endif int act; if(sel<0.33) { diff --git a/src/libCom/osi/osiClockTime.c b/src/libCom/osi/osiClockTime.c index ec4e8a5aa..d565033a0 100644 --- a/src/libCom/osi/osiClockTime.c +++ b/src/libCom/osi/osiClockTime.c @@ -39,7 +39,7 @@ static struct { static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; -#ifdef CLOCK_REALTIME +#if defined(CLOCK_REALTIME) && !defined(_WIN32) /* This code is not used on systems without Posix CLOCK_REALTIME such * as Darwin, but the only way to detect that is from the OS headers, * so the Makefile can't exclude building this file on those systems. From 655735fa1390533963539ce2ee9fd1ca4fb9313d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 7 Sep 2015 15:06:02 -0400 Subject: [PATCH 141/204] asTest: fix link with mingw This test has device support, which the registerRDD script always emits as dllimport, so it fails to link if the dsets are part of the executable itself. Use iocsh to avoid calling the RDD function directly since a DLL can't link against functions in the main executable. --- src/std/rec/test/Makefile | 7 +- src/std/rec/test/asTest.c | 196 +---------------------------- src/std/rec/test/asTestLib.c | 234 +++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 195 deletions(-) create mode 100644 src/std/rec/test/asTestLib.c diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 6b28b5a27..ad204a580 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -10,7 +10,12 @@ TOP=../../../.. include $(TOP)/configure/CONFIG -PROD_LIBS = dbRecStd dbCore ca Com +TESTLIBRARY = dbRecStdTest + +dbRecStdTest_SRCS += asTestLib.c +dbRecStdTest_LIBS += dbRecStd dbCore ca Com + +PROD_LIBS = dbRecStdTest dbRecStd dbCore ca Com TARGETS += $(COMMON_DIR)/recTestIoc.dbd recTestIoc_DBD = base.dbd diff --git a/src/std/rec/test/asTest.c b/src/std/rec/test/asTest.c index 842a754d9..412e9ca33 100644 --- a/src/std/rec/test/asTest.c +++ b/src/std/rec/test/asTest.c @@ -36,205 +36,13 @@ #include "testMain.h" +epicsShareFunc void testRestore(void); + #include "epicsExport.h" -void asTestIoc_registerRecordDeviceDriver(struct dbBase *); - -static unsigned iran; - -static void hookPass0(initHookState state) -{ - DBENTRY entry; - if(state!=initHookAfterInitDevSup) - return; - testDiag("initHookAfterInitDevSup"); - - dbInitEntry(pdbbase, &entry); - - /* rec0.VAL is initially 1, set it to 2 */ - if(dbFindRecord(&entry, "rec0.VAL")==0) { - aoRecord *prec = entry.precnode->precord; - testOk(prec->val==1, "VAL %d==1 (initial value from .db)", (int)prec->val); - testOk1(dbPutString(&entry, "2")==0); - testOk(prec->val==2, "VAL %d==2", (int)prec->val); - } else { - testFail("Missing rec0"); - testSkip(1, "missing record"); - } - - /* rec0.OUT is initially "rec0.DISV", set it to "rec0.SEVR" */ - if(dbFindRecord(&entry, "rec0.OUT")==0) { - aoRecord *prec = entry.precnode->precord; - if(prec->out.type==CONSTANT) - testOk(strcmp(prec->out.text,"rec0.DISV")==0, - "%s==rec0.DISV (initial value from .db)", - prec->out.text); - else - testFail("Wrong link type: %d", (int)prec->out.type); - - testOk1(dbPutString(&entry, "rec0.SEVR")==0); - } else{ - testFail("Missing rec0"); - testSkip(1, "missing record"); - } - - /* rec0.SDIS is initially NULL, set it to "rec0.STAT" */ - if(dbFindRecord(&entry, "rec0.SDIS")==0) { - aoRecord *prec = entry.precnode->precord; - if(prec->sdis.type==CONSTANT) - testOk1(prec->sdis.value.constantStr==NULL); - else - testFail("Wrong link type: %d", (int)prec->sdis.type); - - testOk1(dbPutString(&entry, "rec0.STAT")==0); - } else{ - testFail("Missing rec0"); - testSkip(1, "missing record"); - } - - /* can't restore array field in pass0 */ - - dbFinishEntry(&entry); -} - -static long initRec0(aoRecord *prec) -{ - DBLINK *plink = &prec->out; - testDiag("init_record(%s)", prec->name); - testOk(prec->val==2, "VAL %d==2 (pass0 value)", (int)prec->val); - prec->val = 3; - testOk(prec->val==3, "VAL %d==3", (int)prec->val); - - testOk1(plink->type==DB_LINK); - if(plink->type==DB_LINK) - testOk(strcmp(plink->value.pv_link.pvname,"rec0.SEVR")==0, - "%s==rec0.SEVR (pass0 value)", plink->value.pv_link.pvname); - else - testFail("Wrong link type"); - - plink = &prec->sdis; - - testOk1(plink->type==DB_LINK); - if(plink->type==DB_LINK) - testOk(strcmp(plink->value.pv_link.pvname,"rec0.STAT")==0, - "%s==rec0.STAT (pass0 value)", plink->value.pv_link.pvname); - else - testFail("Wrong link type"); - - iran |= 1; - return 2; /* we set .VAL, so don't use RVAL */ -} - -static long initRec1(waveformRecord *prec) -{ - testDiag("init_record(%s)", prec->name); - testOk(prec->nord==0, "NORD %d==0", (int)prec->nord); - iran |= 2; - return 0; -} - -static double values[] = {1,2,3,4,5}; - -static void hookPass1(initHookState state) -{ - DBENTRY entry; - DBADDR addr; - if(state!=initHookAfterInitDatabase) - return; - testDiag("initHookAfterInitDatabase"); - - dbInitEntry(pdbbase, &entry); - - if(dbFindRecord(&entry, "rec0.VAL")==0) { - aoRecord *prec = entry.precnode->precord; - testOk(prec->val==3, "VAL %d==3 (init_record value)", (int)prec->val); - testOk1(dbPutString(&entry, "4")==0); - testOk(prec->val==4, "VAL %d==4", (int)prec->val); - } else{ - testFail("Missing rec0"); - testSkip(1, "missing record"); - } - - /* Can't restore links in pass 1 */ - - if(dbNameToAddr("rec1.VAL", &addr)) { - testFail("missing rec1"); - testSkip(3, "missing record"); - } else { - struct rset *prset = dbGetRset(&addr); - dbfType ftype = addr.field_type; - long count=-1, offset=-1, maxcount = addr.no_elements; - testOk1(prset && prset->get_array_info && prset->put_array_info); - testOk1((*prset->get_array_info)(&addr, &count, &offset)==0); - /* count is ignored */ - testOk1((*dbPutConvertRoutine[DBF_DOUBLE][ftype])(&addr, values, NELEMENTS(values), maxcount,offset)==0); - testOk1((*prset->put_array_info)(&addr, NELEMENTS(values))==0); - } - - dbFinishEntry(&entry); -} - -static void testRestore(void) -{ - aoRecord *rec0; - waveformRecord *rec1; - testDiag("test Restore"); - - initHookRegister(hookPass0); - initHookRegister(hookPass1); - - testdbPrepare(); - - testdbReadDatabase("asTestIoc.dbd", NULL, NULL); - - asTestIoc_registerRecordDeviceDriver(pdbbase); - - testdbReadDatabase("asTest.db", NULL, NULL); - - rec0 = (aoRecord*)testdbRecordPtr("rec0"); - rec1 = (waveformRecord*)testdbRecordPtr("rec1"); - - eltc(0); - testIocInitOk(); - eltc(1); - - testDiag("Post initialization"); - - testOk1(iran==3); - - testOk1(rec0->val==4); - testOk1(rec1->nord==5); - { - double *buf = rec1->bptr; - testOk(buf[0]==1, "buf[0] %f==1", buf[0]); - testOk1(buf[1]==2); - testOk1(buf[2]==3); - testOk1(buf[3]==4); - testOk1(buf[4]==5); - } - - testIocShutdownOk(); - - testdbCleanup(); -} - MAIN(asTest) { testPlan(29); testRestore(); return testDone(); } - -struct dset6 { - dset common; - DEVSUPFUN proc; - DEVSUPFUN linconv; -}; - -static long noop() {return 0;} - -static struct dset6 devAOasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec0, NULL}, (DEVSUPFUN)noop, NULL}; -static struct dset6 devWFasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec1, NULL}, (DEVSUPFUN)noop, NULL}; - -epicsExportAddress(dset, devAOasTest); -epicsExportAddress(dset, devWFasTest); diff --git a/src/std/rec/test/asTestLib.c b/src/std/rec/test/asTestLib.c new file mode 100644 index 000000000..8a76695cf --- /dev/null +++ b/src/std/rec/test/asTestLib.c @@ -0,0 +1,234 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + * + * Test the hooks that autosave uses during initialization + */ + +#include "string.h" + +#include "epicsString.h" +#include "dbUnitTest.h" +#include "epicsThread.h" +#include "iocInit.h" +#include "dbBase.h" +#include "link.h" +#include "recSup.h" +#include "iocsh.h" +#include "dbAccess.h" +#include "dbConvert.h" +#include "dbStaticLib.h" +#include "registry.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "osiFileName.h" +#include "initHooks.h" +#include "devSup.h" +#include "errlog.h" + +#include "aoRecord.h" +#include "waveformRecord.h" + +#include "epicsExport.h" + +static unsigned iran; + +static void hookPass0(initHookState state) +{ + DBENTRY entry; + if(state!=initHookAfterInitDevSup) + return; + testDiag("initHookAfterInitDevSup"); + + dbInitEntry(pdbbase, &entry); + + /* rec0.VAL is initially 1, set it to 2 */ + if(dbFindRecord(&entry, "rec0.VAL")==0) { + aoRecord *prec = entry.precnode->precord; + testOk(prec->val==1, "VAL %d==1 (initial value from .db)", (int)prec->val); + testOk1(dbPutString(&entry, "2")==0); + testOk(prec->val==2, "VAL %d==2", (int)prec->val); + } else { + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + + /* rec0.OUT is initially "rec0.DISV", set it to "rec0.SEVR" */ + if(dbFindRecord(&entry, "rec0.OUT")==0) { + aoRecord *prec = entry.precnode->precord; + if(prec->out.type==CONSTANT) + testOk(strcmp(prec->out.text,"rec0.DISV")==0, + "%s==rec0.DISV (initial value from .db)", + prec->out.text); + else + testFail("Wrong link type: %d", (int)prec->out.type); + + testOk1(dbPutString(&entry, "rec0.SEVR")==0); + } else{ + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + + /* rec0.SDIS is initially NULL, set it to "rec0.STAT" */ + if(dbFindRecord(&entry, "rec0.SDIS")==0) { + aoRecord *prec = entry.precnode->precord; + if(prec->sdis.type==CONSTANT) + testOk1(prec->sdis.value.constantStr==NULL); + else + testFail("Wrong link type: %d", (int)prec->sdis.type); + + testOk1(dbPutString(&entry, "rec0.STAT")==0); + } else{ + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + + /* can't restore array field in pass0 */ + + dbFinishEntry(&entry); +} + +static long initRec0(aoRecord *prec) +{ + DBLINK *plink = &prec->out; + testDiag("init_record(%s)", prec->name); + testOk(prec->val==2, "VAL %d==2 (pass0 value)", (int)prec->val); + prec->val = 3; + testOk(prec->val==3, "VAL %d==3", (int)prec->val); + + testOk1(plink->type==DB_LINK); + if(plink->type==DB_LINK) + testOk(strcmp(plink->value.pv_link.pvname,"rec0.SEVR")==0, + "%s==rec0.SEVR (pass0 value)", plink->value.pv_link.pvname); + else + testFail("Wrong link type"); + + plink = &prec->sdis; + + testOk1(plink->type==DB_LINK); + if(plink->type==DB_LINK) + testOk(strcmp(plink->value.pv_link.pvname,"rec0.STAT")==0, + "%s==rec0.STAT (pass0 value)", plink->value.pv_link.pvname); + else + testFail("Wrong link type"); + + iran |= 1; + return 2; /* we set .VAL, so don't use RVAL */ +} + +static long initRec1(waveformRecord *prec) +{ + testDiag("init_record(%s)", prec->name); + testOk(prec->nord==0, "NORD %d==0", (int)prec->nord); + iran |= 2; + return 0; +} + +static double values[] = {1,2,3,4,5}; + +static void hookPass1(initHookState state) +{ + DBENTRY entry; + DBADDR addr; + if(state!=initHookAfterInitDatabase) + return; + testDiag("initHookAfterInitDatabase"); + + dbInitEntry(pdbbase, &entry); + + if(dbFindRecord(&entry, "rec0.VAL")==0) { + aoRecord *prec = entry.precnode->precord; + testOk(prec->val==3, "VAL %d==3 (init_record value)", (int)prec->val); + testOk1(dbPutString(&entry, "4")==0); + testOk(prec->val==4, "VAL %d==4", (int)prec->val); + } else{ + testFail("Missing rec0"); + testSkip(1, "missing record"); + } + + /* Can't restore links in pass 1 */ + + if(dbNameToAddr("rec1.VAL", &addr)) { + testFail("missing rec1"); + testSkip(3, "missing record"); + } else { + struct rset *prset = dbGetRset(&addr); + dbfType ftype = addr.field_type; + long count=-1, offset=-1, maxcount = addr.no_elements; + testOk1(prset && prset->get_array_info && prset->put_array_info); + testOk1((*prset->get_array_info)(&addr, &count, &offset)==0); + /* count is ignored */ + testOk1((*dbPutConvertRoutine[DBF_DOUBLE][ftype])(&addr, values, NELEMENTS(values), maxcount,offset)==0); + testOk1((*prset->put_array_info)(&addr, NELEMENTS(values))==0); + } + + dbFinishEntry(&entry); +} + +epicsShareFunc +void testRestore(void) +{ + aoRecord *rec0; + waveformRecord *rec1; + testDiag("test Restore"); + + initHookRegister(hookPass0); + initHookRegister(hookPass1); + + testdbPrepare(); + + testdbReadDatabase("asTestIoc.dbd", NULL, NULL); + + iocshCmd("asTestIoc_registerRecordDeviceDriver(pdbbase)"); + + testdbReadDatabase("asTest.db", NULL, NULL); + + rec0 = (aoRecord*)testdbRecordPtr("rec0"); + rec1 = (waveformRecord*)testdbRecordPtr("rec1"); + + eltc(0); + testIocInitOk(); + eltc(1); + + testDiag("Post initialization"); + + testOk1(iran==3); + + testOk1(rec0->val==4); + testOk1(rec1->nord==5); + { + double *buf = rec1->bptr; + testOk(buf[0]==1, "buf[0] %f==1", buf[0]); + testOk1(buf[1]==2); + testOk1(buf[2]==3); + testOk1(buf[3]==4); + testOk1(buf[4]==5); + } + + testIocShutdownOk(); + + testdbCleanup(); + + /* recSup doesn't cleanup after itself */ + free(bptr); +} + +struct dset6 { + dset common; + DEVSUPFUN proc; + DEVSUPFUN linconv; +}; + +static long noop() {return 0;} + +static struct dset6 devAOasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec0, NULL}, (DEVSUPFUN)noop, NULL}; +static struct dset6 devWFasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec1, NULL}, (DEVSUPFUN)noop, NULL}; + +epicsExportAddress(dset, devAOasTest); +epicsExportAddress(dset, devWFasTest); From 7195372cafb3a502f5e541b45d2e32c3c6e81b52 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 7 Sep 2015 15:23:04 -0400 Subject: [PATCH 142/204] release note for new locking --- documentation/RELEASE_NOTES.html | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 43838dcf6..8ae4e76d3 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,23 @@ --> +

Database Multi-locking

+ +

dbLock.c is re-written with an expanded API, and the removal of global mutex locks.

+ +

The new API functions center around dbScanLockMany(), which behaves like dbScanLock() +applied to an arbitrary group of records. dbLockerAlloc() is used to prepare a list +or record pointers, then dbScanLockMany() is called. When it returns, all of the records +listed may be accessed (in any order) until dbScanUnlockMany() is called.

+ +

The Application Developer's Guide has been updated to describe the API and implementation +is more detail.

+ +

Previously a global mutex 'lockSetModifyLock' was locked and unlocked +during dbScanLock(), acting as a sequencing point for otherwise unrelated calls. +The new dbLock.c implementation does not include any global mutex in dbScanLock() or dbScanLockMany(). +Locking/unlocking of unrelated lock sets is now completely concurrent.

+

Generate Version Header

A Perl script and Makefile rules have been added to allow modules to generate From fc775d76aa332ac934b0156f20ced69650e44ae2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 7 Sep 2015 15:37:46 -0400 Subject: [PATCH 143/204] asTest: copy+paste error Didn't remove all use of local variable --- src/std/rec/test/asTestLib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/std/rec/test/asTestLib.c b/src/std/rec/test/asTestLib.c index 8a76695cf..17a1ac702 100644 --- a/src/std/rec/test/asTestLib.c +++ b/src/std/rec/test/asTestLib.c @@ -216,7 +216,7 @@ void testRestore(void) testdbCleanup(); /* recSup doesn't cleanup after itself */ - free(bptr); + free(rec1->bptr); } struct dset6 { From 355151329729ba230b5b64ecd37a35610b0e6a79 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 8 Sep 2015 09:52:52 -0400 Subject: [PATCH 144/204] dbStressLock: attempt to fix solaris linking failure --- src/ioc/db/test/dbStressLock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbStressLock.c b/src/ioc/db/test/dbStressLock.c index 3f3a0a898..0f8eeb298 100644 --- a/src/ioc/db/test/dbStressLock.c +++ b/src/ioc/db/test/dbStressLock.c @@ -40,7 +40,7 @@ #include "xRecord.h" -#if defined(CLOCK_MONOTONIC) && !defined(_WIN32) +#if defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && !defined(_WIN32) # define TIME_STATS #endif From 8f0950288a4a92bd618a1f76fff98af2385b1867 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 8 Sep 2015 10:04:06 -0400 Subject: [PATCH 145/204] dbChannelTest missing call to testdbPrepare() --- src/ioc/db/test/dbChannelTest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 156c7e9bd..ac82f3fdc 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -158,6 +158,8 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */ testPlan(76); + testdbPrepare(); + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); From 405f50533e9a5efd34abb28e0c1c2e52abfb3197 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 10 Sep 2015 09:56:50 -0500 Subject: [PATCH 146/204] Fix for linux-x86 --- src/ioc/dbStatic/dbStaticRun.c | 12 ++++++------ src/ioc/misc/iocInit.c | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticRun.c b/src/ioc/dbStatic/dbStaticRun.c index bd53bb13e..f7fcbd5c5 100644 --- a/src/ioc/dbStatic/dbStaticRun.c +++ b/src/ioc/dbStatic/dbStaticRun.c @@ -210,20 +210,20 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) precordName, pdbRecordType->name); return(S_dbLib_noRecSup); } - precnode->precord = dbCalloc(1,pdbRecordType->rec_size); - precord = precnode->precord; - precord->rdes = pdbRecordType; + precord = dbCalloc(1, pdbRecordType->rec_size); + precnode->precord = precord; pflddes = pdbRecordType->papFldDes[0]; if(!pflddes) { epicsPrintf("dbAllocRecord pflddes for NAME not found\n"); return(S_dbLib_flddesNotFound); } - if(strlen(precordName)>=pflddes->size) { + assert(pflddes->offset == 0); + assert(pflddes->size == sizeof(precord->name)); + if(strlen(precordName) >= sizeof(precord->name)) { epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName); return(S_dbLib_nameLength); } - pfield = (char*)precord + pflddes->offset; - strcpy(pfield,precordName); + strcpy(precord->name, precordName); for(i=1; ino_fields; i++) { pflddes = pdbRecordType->papFldDes[i]; diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 6ebe807ff..f7f259201 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -467,6 +467,7 @@ static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord, if (!prset) return; /* unlikely */ precord->rset = prset; + precord->rdes = pdbRecordType; precord->mlok = epicsMutexMustCreate(); ellInit(&precord->mlis); From 4081377c4b3eed1a718f814d459edb73af9d030d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 10 Sep 2015 13:26:37 -0500 Subject: [PATCH 147/204] Build fixes for Solaris --- src/ioc/db/test/Makefile | 1 + src/libCom/osi/os/default/gnuReadline.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 002b35c9f..b75a64929 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -64,6 +64,7 @@ TESTFILES += ../dbLockTest.db TESTPROD_HOST += dbStressTest dbStressTest_SRCS += dbStressLock.c dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +dbStressTest_SYS_LIBS_solaris += rt TESTS += dbStressTest TESTPROD_HOST += testdbConvert diff --git a/src/libCom/osi/os/default/gnuReadline.c b/src/libCom/osi/os/default/gnuReadline.c index 2e3c2eec9..78ed17828 100644 --- a/src/libCom/osi/os/default/gnuReadline.c +++ b/src/libCom/osi/os/default/gnuReadline.c @@ -18,7 +18,9 @@ #include "epicsExit.h" -static struct osdContext {} present; +static struct osdContext { + char dummy; /* Required for older compilers */ +} present; static enum {rlNone, rlIdle, rlBusy} rlState = rlNone; From 122b9d318567c1496b76a574af63a930942fa9fe Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Wed, 16 Sep 2015 13:43:43 +0200 Subject: [PATCH 148/204] db/test: Link dbStressTest against librt on all targets (not just solaris) man clock_gettime: "Link with -lrt (only for glibc versions before 2.17)." --- src/ioc/db/test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index b75a64929..172818d92 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -64,7 +64,7 @@ TESTFILES += ../dbLockTest.db TESTPROD_HOST += dbStressTest dbStressTest_SRCS += dbStressLock.c dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp -dbStressTest_SYS_LIBS_solaris += rt +dbStressTest_SYS_LIBS += rt TESTS += dbStressTest TESTPROD_HOST += testdbConvert From a245c3805acba407a7796ce783272d5e35b30680 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Wed, 16 Sep 2015 17:33:20 +0200 Subject: [PATCH 149/204] ioc/db/test: fix Makefile to link against librt on Solaris and Linux only --- src/ioc/db/test/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 172818d92..53d7523a4 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -64,7 +64,8 @@ TESTFILES += ../dbLockTest.db TESTPROD_HOST += dbStressTest dbStressTest_SRCS += dbStressLock.c dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp -dbStressTest_SYS_LIBS += rt +dbStressTest_SYS_LIBS_solaris += rt +dbStressTest_SYS_LIBS_Linux += rt TESTS += dbStressTest TESTPROD_HOST += testdbConvert From b099ea2966b0851be95bf2b8f47106a67e02d195 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 18 Oct 2015 21:41:46 -0500 Subject: [PATCH 150/204] Clean up g++ unused-but-set-variable warning in pcas --- src/ca/legacy/pcas/generic/casDGClient.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ca/legacy/pcas/generic/casDGClient.cc b/src/ca/legacy/pcas/generic/casDGClient.cc index 4e7a23176..ce0d6e1ba 100644 --- a/src/ca/legacy/pcas/generic/casDGClient.cc +++ b/src/ca/legacy/pcas/generic/casDGClient.cc @@ -354,9 +354,11 @@ caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp ) status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 ); - this->out.commitMsg (); + if ( status == S_cas_success ) { + this->out.commitMsg (); + } - return S_cas_success; + return S_cas_success; } /* From a8e285067dca238623f0931daa3d8f9767d92af1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 151/204] dbmf: cleanup indent whitespace only --- src/libCom/dbmf/dbmf.c | 94 +++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/libCom/dbmf/dbmf.c b/src/libCom/dbmf/dbmf.c index c0ad12216..208bc2807 100644 --- a/src/libCom/dbmf/dbmf.c +++ b/src/libCom/dbmf/dbmf.c @@ -94,47 +94,47 @@ void* epicsShareAPI dbmfMalloc(size_t size) pfreeList = &pdbmfPvt->freeList; if(*pfreeList == NULL) { int i; - size_t nbytesTotal; + size_t nbytesTotal; if(dbmfDebug) printf("dbmfMalloc allocating new storage\n"); - nbytesTotal = pdbmfPvt->chunkSize + sizeof(chunkNode); + nbytesTotal = pdbmfPvt->chunkSize + sizeof(chunkNode); pmem = (char *)malloc(nbytesTotal); - if(!pmem) { + if(!pmem) { epicsMutexUnlock(pdbmfPvt->lock); - printf("dbmfMalloc malloc failed\n"); - return(NULL); - } - pchunkNode = (chunkNode *)(pmem + pdbmfPvt->chunkSize); - pchunkNode->pchunk = pmem; - pchunkNode->nNotFree=0; - ellAdd(&pdbmfPvt->chunkList,&pchunkNode->node); - for(i=0; ichunkItems; i++) { - pitemHeader = (itemHeader *)pmem; - pitemHeader->pchunkNode = pchunkNode; - pnextFree = &pitemHeader->pnextFree; - *pnextFree = *pfreeList; *pfreeList = (void *)pmem; - pdbmfPvt->nFree++; - pmem += pdbmfPvt->allocSize; - } + printf("dbmfMalloc malloc failed\n"); + return(NULL); + } + pchunkNode = (chunkNode *)(pmem + pdbmfPvt->chunkSize); + pchunkNode->pchunk = pmem; + pchunkNode->nNotFree=0; + ellAdd(&pdbmfPvt->chunkList,&pchunkNode->node); + for(i=0; ichunkItems; i++) { + pitemHeader = (itemHeader *)pmem; + pitemHeader->pchunkNode = pchunkNode; + pnextFree = &pitemHeader->pnextFree; + *pnextFree = *pfreeList; *pfreeList = (void *)pmem; + pdbmfPvt->nFree++; + pmem += pdbmfPvt->allocSize; + } } if(size<=pdbmfPvt->size) { pnextFree = *pfreeList; *pfreeList = *pnextFree; - pmem = (void *)pnextFree; + pmem = (void *)pnextFree; pdbmfPvt->nAlloc++; pdbmfPvt->nFree--; - pitemHeader = (itemHeader *)pnextFree; - pitemHeader->pchunkNode->nNotFree += 1; + pitemHeader = (itemHeader *)pnextFree; + pitemHeader->pchunkNode->nNotFree += 1; } else { - pmem = malloc(sizeof(itemHeader) + size); - if(!pmem) { - epicsMutexUnlock(pdbmfPvt->lock); - printf("dbmfMalloc malloc failed\n"); - return(NULL); - } - pdbmfPvt->nAlloc++; - pdbmfPvt->nGtSize++; - pitemHeader = (itemHeader *)pmem; - pitemHeader->pchunkNode = NULL; - if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n", + pmem = malloc(sizeof(itemHeader) + size); + if(!pmem) { + epicsMutexUnlock(pdbmfPvt->lock); + printf("dbmfMalloc malloc failed\n"); + return(NULL); + } + pdbmfPvt->nAlloc++; + pdbmfPvt->nGtSize++; + pitemHeader = (itemHeader *)pmem; + pitemHeader->pchunkNode = NULL; + if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n", (unsigned long)size,pmem); } epicsMutexUnlock(pdbmfPvt->lock); @@ -157,23 +157,23 @@ void epicsShareAPI dbmfFree(void* mem) if(!mem) return; if(!pdbmfPvt) { - printf("dbmfFree called but dbmfInit never called\n"); - return; + printf("dbmfFree called but dbmfInit never called\n"); + return; } pmem -= sizeof(itemHeader); epicsMutexMustLock(pdbmfPvt->lock); pitemHeader = (itemHeader *)pmem; if(!pitemHeader->pchunkNode) { - if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem); - free((void *)pmem); pdbmfPvt->nAlloc--; + if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem); + free((void *)pmem); pdbmfPvt->nAlloc--; }else { void **pfreeList = &pdbmfPvt->freeList; void **pnextFree = &pitemHeader->pnextFree; pchunkNode = pitemHeader->pchunkNode; - pchunkNode->nNotFree--; + pchunkNode->nNotFree--; *pnextFree = *pfreeList; *pfreeList = pnextFree; - pdbmfPvt->nAlloc--; pdbmfPvt->nFree++; + pdbmfPvt->nAlloc--; pdbmfPvt->nFree++; } epicsMutexUnlock(pdbmfPvt->lock); } @@ -220,22 +220,22 @@ void epicsShareAPI dbmfFreeChunks(void) chunkNode *pnext;; if(!pdbmfPvt) { - printf("dbmfFreeChunks called but dbmfInit never called\n"); - return; + printf("dbmfFreeChunks called but dbmfInit never called\n"); + return; } epicsMutexMustLock(pdbmfPvt->lock); if(pdbmfPvt->nFree - != (pdbmfPvt->chunkItems * ellCount(&pdbmfPvt->chunkList))) { - printf("dbmfFinish: not all free\n"); + != (pdbmfPvt->chunkItems * ellCount(&pdbmfPvt->chunkList))) { + printf("dbmfFinish: not all free\n"); epicsMutexUnlock(pdbmfPvt->lock); - return; + return; } pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList); while(pchunkNode) { - pnext = (chunkNode *)ellNext(&pchunkNode->node); - ellDelete(&pdbmfPvt->chunkList,&pchunkNode->node); - free(pchunkNode->pchunk); - pchunkNode = pnext; + pnext = (chunkNode *)ellNext(&pchunkNode->node); + ellDelete(&pdbmfPvt->chunkList,&pchunkNode->node); + free(pchunkNode->pchunk); + pchunkNode = pnext; } pdbmfPvt->nFree = 0; pdbmfPvt->freeList = NULL; epicsMutexUnlock(pdbmfPvt->lock); From 3258927bab46c4c8de4908a1110084659cf0ebb5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 152/204] freeListLib: cleanup indent all whitespace --- src/libCom/freeList/freeListLib.c | 106 +++++++++++++++--------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/libCom/freeList/freeListLib.c b/src/libCom/freeList/freeListLib.c index 5e07ba9c3..77cd566b1 100644 --- a/src/libCom/freeList/freeListLib.c +++ b/src/libCom/freeList/freeListLib.c @@ -54,13 +54,13 @@ epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt) { FREELISTPVT *pfl = pvt; # ifdef EPICS_FREELIST_DEBUG - return callocMustSucceed(1,pfl->size,"freeList Debug Calloc"); + return callocMustSucceed(1,pfl->size,"freeList Debug Calloc"); # else - void *ptemp; + void *ptemp; - ptemp = freeListMalloc(pvt); - if(ptemp) memset((char *)ptemp,0,pfl->size); - return(ptemp); + ptemp = freeListMalloc(pvt); + if(ptemp) memset((char *)ptemp,0,pfl->size); + return(ptemp); # endif } @@ -68,45 +68,45 @@ epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) { FREELISTPVT *pfl = pvt; # ifdef EPICS_FREELIST_DEBUG - return callocMustSucceed(1,pfl->size,"freeList Debug Malloc"); + return callocMustSucceed(1,pfl->size,"freeList Debug Malloc"); # else - void *ptemp; - void **ppnext; - allocMem *pallocmem; - int i; + void *ptemp; + void **ppnext; + allocMem *pallocmem; + int i; - epicsMutexMustLock(pfl->lock); - ptemp = pfl->head; + epicsMutexMustLock(pfl->lock); + ptemp = pfl->head; + if(ptemp==0) { + ptemp = (void *)malloc(pfl->nmalloc*pfl->size); if(ptemp==0) { - ptemp = (void *)malloc(pfl->nmalloc*pfl->size); - if(ptemp==0) { - epicsMutexUnlock(pfl->lock); - return(0); - } - pallocmem = (allocMem *)calloc(1,sizeof(allocMem)); - if(pallocmem==0) { - epicsMutexUnlock(pfl->lock); - free(ptemp); - return(0); - } - pallocmem->memory = ptemp; - if(pfl->mallochead) - pallocmem->next = pfl->mallochead; - pfl->mallochead = pallocmem; - for(i=0; inmalloc; i++) { - ppnext = ptemp; - *ppnext = pfl->head; - pfl->head = ptemp; - ptemp = ((char *)ptemp) + pfl->size; - } - ptemp = pfl->head; - pfl->nBlocksAvailable += pfl->nmalloc; + epicsMutexUnlock(pfl->lock); + return(0); } - ppnext = pfl->head; - pfl->head = *ppnext; - pfl->nBlocksAvailable--; - epicsMutexUnlock(pfl->lock); - return(ptemp); + pallocmem = (allocMem *)calloc(1,sizeof(allocMem)); + if(pallocmem==0) { + epicsMutexUnlock(pfl->lock); + free(ptemp); + return(0); + } + pallocmem->memory = ptemp; + if(pfl->mallochead) + pallocmem->next = pfl->mallochead; + pfl->mallochead = pallocmem; + for(i=0; inmalloc; i++) { + ppnext = ptemp; + *ppnext = pfl->head; + pfl->head = ptemp; + ptemp = ((char *)ptemp) + pfl->size; + } + ptemp = pfl->head; + pfl->nBlocksAvailable += pfl->nmalloc; + } + ppnext = pfl->head; + pfl->head = *ppnext; + pfl->nBlocksAvailable--; + epicsMutexUnlock(pfl->lock); + return(ptemp); # endif } @@ -114,17 +114,17 @@ epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem) { FREELISTPVT *pfl = pvt; # ifdef EPICS_FREELIST_DEBUG - memset ( pmem, 0xdd, pfl->size ); - free(pmem); + memset ( pmem, 0xdd, pfl->size ); + free(pmem); # else - void **ppnext; + void **ppnext; - epicsMutexMustLock(pfl->lock); - ppnext = pmem; - *ppnext = pfl->head; - pfl->head = pmem; - pfl->nBlocksAvailable++; - epicsMutexUnlock(pfl->lock); + epicsMutexMustLock(pfl->lock); + ppnext = pmem; + *ppnext = pfl->head; + pfl->head = pmem; + pfl->nBlocksAvailable++; + epicsMutexUnlock(pfl->lock); # endif } @@ -136,10 +136,10 @@ epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt) phead = pfl->mallochead; while(phead) { - pnext = phead->next; - free(phead->memory); - free(phead); - phead = pnext; + pnext = phead->next; + free(phead->memory); + free(phead); + phead = pnext; } epicsMutexDestroy(pfl->lock); free(pvt); From 03bdf4effac6b38ddb3e8f4f8e3c992a31d82cd9 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 153/204] include valgrind.h --- src/libCom/Makefile | 2 + src/libCom/valgrind/valgrind.h | 6587 ++++++++++++++++++++++++++++++++ 2 files changed, 6589 insertions(+) create mode 100644 src/libCom/valgrind/valgrind.h diff --git a/src/libCom/Makefile b/src/libCom/Makefile index 767180316..31a25e954 100644 --- a/src/libCom/Makefile +++ b/src/libCom/Makefile @@ -12,6 +12,8 @@ include $(TOP)/configure/CONFIG SRC = $(TOP)/src LIBCOM = $(SRC)/libCom +INC += valgrind/valgrind.h + include $(LIBCOM)/as/Makefile include $(LIBCOM)/bucketLib/Makefile include $(LIBCOM)/calc/Makefile diff --git a/src/libCom/valgrind/valgrind.h b/src/libCom/valgrind/valgrind.h new file mode 100644 index 000000000..6954d751d --- /dev/null +++ b/src/libCom/valgrind/valgrind.h @@ -0,0 +1,6587 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2013 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + + +/* ------------------------------------------------------------------ */ +/* VERSION NUMBER OF VALGRIND */ +/* ------------------------------------------------------------------ */ + +/* Specify Valgrind's version number, so that user code can + conditionally compile based on our version number. Note that these + were introduced at version 3.6 and so do not exist in version 3.5 + or earlier. The recommended way to use them to check for "version + X.Y or later" is (eg) + +#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ + && (__VALGRIND_MAJOR__ > 3 \ + || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) +*/ +#define __VALGRIND_MAJOR__ 3 +#define __VALGRIND_MINOR__ 10 + + +#include + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). + + Misc note: how to find out what's predefined in gcc by default: + gcc -Wp,-dM somefile.c +*/ +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_arm64_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + + +#if defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ + || defined(__CYGWIN32__) \ + || (defined(_WIN32) && defined(_M_IX86)) +# define PLAT_x86_win32 1 +#elif defined(__MINGW64__) \ + || (defined(_WIN64) && defined(_M_X64)) +# define PLAT_amd64_win64 1 +#elif defined(__linux__) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__linux__) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 +/* Big Endian uses ELF version 1 */ +# define PLAT_ppc64be_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 +/* Little Endian uses ELF version 2 */ +# define PLAT_ppc64le_linux 1 +#elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) +# define PLAT_arm_linux 1 +#elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) +# define PLAT_arm64_linux 1 +#elif defined(__linux__) && defined(__s390__) && defined(__s390x__) +# define PLAT_s390x_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips==64) +# define PLAT_mips64_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips!=64) +# define PLAT_mips32_linux 1 +#else +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +/* + * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client + * request. Accepts both pointers and integers as arguments. + * + * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind + * client request that does not return a value. + + * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind + * client request and whose value equals the client request result. Accepts + * both pointers and integers as arguments. Note that such calls are not + * necessarily pure functions -- they may have side effects. + */ + +#define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ + _zzq_request, _zzq_arg1, _zzq_arg2, \ + _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ + _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (_zzq_default) + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ + || (defined(PLAT_x86_win32) && defined(__GNUC__)) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgl %%edi,%%edi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */ + +/* ------------------------- x86-Win32 ------------------------- */ + +#if defined(PLAT_x86_win32) && !defined(__GNUC__) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#if defined(_MSC_VER) + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm rol edi, 3 __asm rol edi, 13 \ + __asm rol edi, 29 __asm rol edi, 19 + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ + (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ + (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ + (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) + +static __inline uintptr_t +valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, + uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, + uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, + uintptr_t _zzq_arg5) +{ + volatile uintptr_t _zzq_args[6]; + volatile unsigned int _zzq_result; + _zzq_args[0] = (uintptr_t)(_zzq_request); + _zzq_args[1] = (uintptr_t)(_zzq_arg1); + _zzq_args[2] = (uintptr_t)(_zzq_arg2); + _zzq_args[3] = (uintptr_t)(_zzq_arg3); + _zzq_args[4] = (uintptr_t)(_zzq_arg4); + _zzq_args[5] = (uintptr_t)(_zzq_arg5); + __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default + __SPECIAL_INSTRUCTION_PREAMBLE + /* %EDX = client_request ( %EAX ) */ + __asm xchg ebx,ebx + __asm mov _zzq_result, edx + } + return _zzq_result; +} + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + __asm xchg ecx,ecx \ + __asm mov __addr, eax \ + } \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX ERROR + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm xchg edi,edi \ + } \ + } while (0) + +#else +#error Unsupported compiler. +#endif + +#endif /* PLAT_x86_win32 */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ + || (defined(PLAT_amd64_win64) && defined(__GNUC__)) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgq %%rdi,%%rdi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------- amd64-Win64 ------------------------- */ + +#if defined(PLAT_amd64_win64) && !defined(__GNUC__) + +#error Unsupported compiler. + +#endif /* PLAT_amd64_win64 */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ + "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long long int _zzq_args[6]; \ + unsigned long long int _zzq_result; \ + unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +#if defined(PLAT_ppc64le_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long long int _zzq_args[6]; \ + unsigned long long int _zzq_result; \ + unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R12 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ + "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("mov r3, %1\n\t" /*default*/ \ + "mov r4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = client_request ( R4 ) */ \ + "orr r10, r10, r10\n\t" \ + "mov %0, r3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = guest_NRADDR */ \ + "orr r11, r11, r11\n\t" \ + "mov %0, r3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R4 */ \ + "orr r12, r12, r12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr r9, r9, r9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------- */ + +#if defined(PLAT_arm64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ + "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile("mov x3, %1\n\t" /*default*/ \ + "mov x4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = client_request ( X4 ) */ \ + "orr x10, x10, x10\n\t" \ + "mov %0, x3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "x3", "x4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = guest_NRADDR */ \ + "orr x11, x11, x11\n\t" \ + "mov %0, x3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "x3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir X8 */ \ + "orr x12, x12, x12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr x9, x9, x9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------ s390x-linux ------------------------ */ + +#if defined(PLAT_s390x_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +/* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific + * code. This detection is implemented in platform specific toIR.c + * (e.g. VEX/priv/guest_s390_decoder.c). + */ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "lr 15,15\n\t" \ + "lr 1,1\n\t" \ + "lr 2,2\n\t" \ + "lr 3,3\n\t" + +#define __CLIENT_REQUEST_CODE "lr 2,2\n\t" +#define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" +#define __CALL_NO_REDIR_CODE "lr 4,4\n\t" +#define __VEX_INJECT_IR_CODE "lr 5,5\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(/* r2 = args */ \ + "lgr 2,%1\n\t" \ + /* r3 = default */ \ + "lgr 3,%2\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CLIENT_REQUEST_CODE \ + /* results = r3 */ \ + "lgr %0, 3\n\t" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "2", "3", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __GET_NR_CONTEXT_CODE \ + "lgr %0, 3\n\t" \ + : "=a" (__addr) \ + : \ + : "cc", "3", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_R1 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CALL_NO_REDIR_CODE + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __VEX_INJECT_IR_CODE); \ + } while (0) + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ---------------- */ + +#if defined(PLAT_mips32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +/* .word 0x342 + * .word 0x742 + * .word 0xC2 + * .word 0x4C2*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "srl $0, $0, 13\n\t" \ + "srl $0, $0, 29\n\t" \ + "srl $0, $0, 3\n\t" \ + "srl $0, $0, 19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* T3 = client_request ( T4 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %t9 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%t9 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ---------------- */ + +#if defined(PLAT_mips64_linux) + +typedef + struct { + unsigned long long nraddr; /* where's the code? */ + } + OrigFn; + +/* dsll $0,$0, 3 + * dsll $0,$0, 13 + * dsll $0,$0, 29 + * dsll $0,$0, 19*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ + "dsll $0,$0,29 ; dsll $0,$0,19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = client_request ( $12 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11"); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir $25 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_mips64_linux */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts + the default behaviour equivalance class tag "0000" into the name. + See pub_tool_redir.h for details -- normally you don't need to + think about this, though. */ + +/* Use an extra level of macroisation so as to ensure the soname/fnname + args are fully macro-expanded before pasting them together. */ +#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Also provide end-user facilities for function replacement, rather + than wrapping. A replacement function differs from a wrapper in + that it has no way to get hold of the original function being + called, and hence no way to call onwards to it. In a replacement + function, VALGRIND_GET_ORIG_FN always returns zero. */ + +#define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) + +#define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) + +#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) + +#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) + +#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movl %%esp,%%edi\n\t" \ + "andl $0xfffffff0,%%esp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movl %%edi,%%esp\n\t" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* This is all pretty complex. It's so as to make stack unwinding + work reliably. See bug 243270. The basic problem is the sub and + add of 128 of %rsp in all of the following macros. If gcc believes + the CFA is in %rsp, then unwinding may fail, because what's at the + CFA is not what gcc "expected" when it constructs the CFIs for the + places where the macros are instantiated. + + But we can't just add a CFI annotation to increase the CFA offset + by 128, to match the sub of 128 from %rsp, because we don't know + whether gcc has chosen %rsp as the CFA at that point, or whether it + has chosen some other register (eg, %rbp). In the latter case, + adding a CFI annotation to change the CFA offset is simply wrong. + + So the solution is to get hold of the CFA using + __builtin_dwarf_cfa(), put it in a known register, and add a + CFI annotation to say what the register is. We choose %rbp for + this (perhaps perversely), because: + + (1) %rbp is already subject to unwinding. If a new register was + chosen then the unwinder would have to unwind it in all stack + traces, which is expensive, and + + (2) %rbp is already subject to precise exception updates in the + JIT. If a new register was chosen, we'd have to have precise + exceptions for it too, which reduces performance of the + generated code. + + However .. one extra complication. We can't just whack the result + of __builtin_dwarf_cfa() into %rbp and then add %rbp to the + list of trashed registers at the end of the inline assembly + fragments; gcc won't allow %rbp to appear in that list. Hence + instead we need to stash %rbp in %r15 for the duration of the asm, + and say that %r15 is trashed instead. gcc seems happy to go with + that. + + Oh .. and this all needs to be conditionalised so that it is + unchanged from before this commit, when compiled with older gccs + that don't support __builtin_dwarf_cfa. Furthermore, since + this header file is freestanding, it has to be independent of + config.h, and so the following conditionalisation cannot depend on + configure time checks. + + Although it's not clear from + 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', + this expression excludes Darwin. + .cfi directives in Darwin assembly appear to be completely + different and I haven't investigated how they work. + + For even more entertainment value, note we have to use the + completely undocumented __builtin_dwarf_cfa(), which appears to + really compute the CFA, whereas __builtin_frame_address(0) claims + to but actually doesn't. See + https://bugs.kde.org/show_bug.cgi?id=243270#c47 +*/ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"r"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + "movq %%rbp, %%r15\n\t" \ + "movq %2, %%rbp\n\t" \ + ".cfi_remember_state\n\t" \ + ".cfi_def_cfa rbp, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "movq %%r15, %%rbp\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movq %%rsp,%%r14\n\t" \ + "andq $0xfffffffffffffff0,%%rsp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movq %%r14,%%rsp\n\t" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rlwinm 1,1,0,0,27\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +/* ------------------------- ppc64le-linux ----------------------- */ +#if defined(PLAT_ppc64le_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(12)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +/* This is a bit tricky. We store the original stack pointer in r10 + as it is callee-saves. gcc doesn't allow the use of r11 for some + reason. Also, we can't directly "bic" the stack pointer in thumb + mode since r13 isn't an allowed register number in that context. + So use r4 as a temporary, since that is about to get trashed + anyway, just after each use of this macro. Side effect is we need + to be very careful about any future changes, since + VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ +#define VALGRIND_ALIGN_STACK \ + "mov r10, sp\n\t" \ + "mov r4, sp\n\t" \ + "bic r4, r4, #7\n\t" \ + "mov sp, r4\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, r10\n\t" + +/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "push {r0, r1, r2, r3} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "ldr r2, [%1, #48] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------ */ + +#if defined(PLAT_arm64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ + "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ + "x18", "x19", "x20", "x30", \ + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ + "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ + "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ + "v26", "v27", "v28", "v29", "v30", "v31" + +/* x21 is callee-saved, so we can use it to save and restore SP around + the hidden call. */ +#define VALGRIND_ALIGN_STACK \ + "mov x21, sp\n\t" \ + "bic sp, x21, #15\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, x21\n\t" + +/* These CALL_FN_ macros assume that on arm64-linux, + sizeof(unsigned long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11, \ + arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1, #96] \n\t" \ + "str x8, [sp, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------- s390x-linux ------------------------- */ + +#if defined(PLAT_s390x_linux) + +/* Similar workaround as amd64 (see above), but we use r11 as frame + pointer and save the old r11 in r7. r11 might be used for + argvec, therefore we copy argvec in r1 since r1 is clobbered + after the call anyway. */ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"d"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + ".cfi_remember_state\n\t" \ + "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ + "lgr 7,11\n\t" \ + "lgr 11,%2\n\t" \ + ".cfi_def_cfa r11, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "lgr 11, 7\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE \ + "lgr 1,%1\n\t" +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Nb: On s390 the stack pointer is properly aligned *at all times* + according to the s390 GCC maintainer. (The ABI specification is not + precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and + VALGRIND_RESTORE_STACK are not defined here. */ + +/* These regs are trashed by the hidden call. Note that we overwrite + r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the + function a proper return address. All others are ABI defined call + clobbers. */ +#define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \ + "f0","f1","f2","f3","f4","f5","f6","f7" + +/* Nb: Although r11 is modified in the asm snippets below (inside + VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for + two reasons: + (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not + modified + (2) GCC will complain that r11 cannot appear inside a clobber section, + when compiled with -O -fno-omit-frame-pointer + */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 1, 0(1)\n\t" /* target->r1 */ \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +/* The call abi has the arguments in r2-r6 and stack */ +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1, arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-168\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,168\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-176\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,176\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-184\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,184\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-192\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,192\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-200\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,200\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-208\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,208\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-216\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "mvc 208(8,15), 96(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,216\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ----------------------- */ + +#if defined(PLAT_mips32_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16\n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" /* arg1*/ \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 24\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 24 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "nop\n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 56\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 48(%1) \n\t" \ + "sw $4, 44($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 56 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ------------------------- */ + +#if defined(PLAT_mips64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" /* arg1*/ \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "dsubu $29, $29, 8\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 8\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "dsubu $29, $29, 16\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 16\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "dsubu $29, $29, 24\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 24\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "dsubu $29, $29, 32\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 96(%1)\n\t" \ + "sd $4, 24($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 32\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips64_linux */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* Allows the client program and/or gdbserver to execute a monitor + command. */ + VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + /* The first two pass the va_list argument by value, which + assumes it is the same size as or smaller than a UWord, + which generally isn't the case. Hence are deprecated. + The second two pass the vargs by reference and so are + immune to this problem. */ + /* both :: char* fmt, va_list vargs (DEPRECATED) */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + /* both :: char* fmt, va_list* vargs */ + VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503, + + /* Wine support */ + VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, + + /* Querying of debug info. */ + VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, + + /* Disable/enable error reporting level. Takes a single + Word arg which is the delta to this thread's error + disablement indicator. Hence 1 disables or further + disables errors, and -1 moves back towards enablement. + Other values are not allowed. */ + VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, + + /* Initialise IR injection */ + VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0) \ + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0) + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. The return value + is the number of characters printed, excluding the "**** " part at the + start and the backtrace (if present). */ + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF(const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0) + +/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing + when heap blocks are allocated in order to give accurate results. This + happens automatically for the standard allocator functions such as + malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, + delete[], etc. + + But if your program uses a custom allocator, this doesn't automatically + happen, and Valgrind will not do as well. For example, if you allocate + superblocks with mmap() and then allocates chunks of the superblocks, all + Valgrind's observations will be at the mmap() level and it won't know that + the chunks should be considered separate entities. In Memcheck's case, + that means you probably won't get heap block overrun detection (because + there won't be redzones marked as unaddressable) and you definitely won't + get any leak detection. + + The following client requests allow a custom allocator to be annotated so + that it can be handled accurately by Valgrind. + + VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated + by a malloc()-like function. For Memcheck (an illustrative case), this + does two things: + + - It records that the block has been allocated. This means any addresses + within the block mentioned in error messages will be + identified as belonging to the block. It also means that if the block + isn't freed it will be detected by the leak checker. + + - It marks the block as being addressable and undefined (if 'is_zeroed' is + not set), or addressable and defined (if 'is_zeroed' is set). This + controls how accesses to the block by the program are handled. + + 'addr' is the start of the usable block (ie. after any + redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator + can apply redzones -- these are blocks of padding at the start and end of + each block. Adding redzones is recommended as it makes it much more likely + Valgrind will spot block overruns. `is_zeroed' indicates if the memory is + zeroed (or filled with another predictable value), as is the case for + calloc(). + + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a + heap block -- that will be used by the client program -- is allocated. + It's best to put it at the outermost level of the allocator if possible; + for example, if you have a function my_alloc() which calls + internal_alloc(), and the client request is put inside internal_alloc(), + stack traces relating to the heap block will contain entries for both + my_alloc() and internal_alloc(), which is probably not what you want. + + For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out + custom blocks from within a heap block, B, that has been allocated with + malloc/calloc/new/etc, then block B will be *ignored* during leak-checking + -- the custom blocks will take precedence. + + VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For + Memcheck, it does two things: + + - It records that the block has been deallocated. This assumes that the + block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - It marks the block as being unaddressable. + + VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a + heap block is deallocated. + + VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For + Memcheck, it does four things: + + - It records that the size of a block has been changed. This assumes that + the block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - If the block shrunk, it marks the freed memory as being unaddressable. + + - If the block grew, it marks the new area as undefined and defines a red + zone past the end of the new block. + + - The V-bits of the overlap between the old and the new block are preserved. + + VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block + and before deallocation of the old block. + + In many cases, these three client requests will not be enough to get your + allocator working well with Memcheck. More specifically, if your allocator + writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call + will be necessary to mark the memory as addressable just before the zeroing + occurs, otherwise you'll get a lot of invalid write errors. For example, + you'll need to do this if your allocator recycles freed blocks, but it + zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). + Alternatively, if your allocator reuses freed blocks for allocator-internal + data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. + + Really, what's happening is a blurring of the lines between the client + program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the + memory should be considered unaddressable to the client program, but the + allocator knows more than the rest of the client program and so may be able + to safely access it. Extra client requests are necessary for Valgrind to + understand the distinction between the allocator and the rest of the + program. + + Ignored if addr == 0. +*/ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ + addr, oldSizeB, newSizeB, rzB, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0) + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0) + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0) + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0) + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0) + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0) + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0) + +/* Mark a piece of memory as being a stack. Returns a stack id. + start is the lowest addressable stack byte, end is the highest + addressable stack byte. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0) + +/* Change the start and end address of the stack id. + start is the new lowest addressable stack byte, end is the new highest + addressable stack byte. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0) + +/* Load PDB debug info for Wine PE image_map. */ +#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ + fd, ptr, total_size, delta, 0) + +/* Map a code address to a source file name and line number. buf64 + must point to a 64-byte buffer in the caller's address space. The + result will be dumped in there and is guaranteed to be zero + terminated. If no info is found, the first byte is set to zero. */ +#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MAP_IP_TO_SRCLOC, \ + addr, buf64, 0, 0, 0) + +/* Disable error reporting for this thread. Behaves in a stack like + way, so you can safely call this multiple times provided that + VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times + to re-enable reporting. The first call of this macro disables + reporting. Subsequent calls have no effect except to increase the + number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable + reporting. Child threads do not inherit this setting from their + parents -- they are always created with reporting enabled. */ +#define VALGRIND_DISABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + 1, 0, 0, 0, 0) + +/* Re-enable error reporting, as per comments on + VALGRIND_DISABLE_ERROR_REPORTING. */ +#define VALGRIND_ENABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + -1, 0, 0, 0, 0) + +/* Execute a monitor command from the client program. + If a connection is opened with GDB, the output will be sent + according to the output mode set for vgdb. + If no connection is opened, output will go to the log output. + Returns 1 if command not recognised, 0 otherwise. */ +#define VALGRIND_MONITOR_COMMAND(command) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ + command, 0, 0, 0, 0) + + +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + +#endif /* __VALGRIND_H */ From 7e7cca6164f227e1aff3ecd51de0ce6acc2191c5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 154/204] dbmf: instrument with valgrind --- src/libCom/Makefile | 2 ++ src/libCom/dbmf/dbmf.c | 26 +++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/libCom/Makefile b/src/libCom/Makefile index 31a25e954..696b1cf98 100644 --- a/src/libCom/Makefile +++ b/src/libCom/Makefile @@ -9,6 +9,8 @@ TOP = ../.. include $(TOP)/configure/CONFIG +USR_CFLAGS += -DUSE_VALGRIND + SRC = $(TOP)/src LIBCOM = $(SRC)/libCom diff --git a/src/libCom/dbmf/dbmf.c b/src/libCom/dbmf/dbmf.c index 208bc2807..f5545cb88 100644 --- a/src/libCom/dbmf/dbmf.c +++ b/src/libCom/dbmf/dbmf.c @@ -19,6 +19,15 @@ #include #include +#include "valgrind/valgrind.h" + +#ifndef NVALGRIND +/* buffer around allocations to detect out of bounds access */ +#define REDZONE sizeof(double) +#else +#define REDZONE 0 +#endif + #define epicsExportSharedSymbols #include "epicsMutex.h" #include "ellLib.h" @@ -70,13 +79,17 @@ int epicsShareAPI dbmfInit(size_t size, int chunkItems) pdbmfPvt->lock = epicsMutexMustCreate(); /*allign to at least a double*/ pdbmfPvt->size = size + size%sizeof(double); - pdbmfPvt->allocSize = pdbmfPvt->size + sizeof(itemHeader); + /* layout is + * | itemHeader | REDZONE | size | REDZONE | + */ + pdbmfPvt->allocSize = pdbmfPvt->size + sizeof(itemHeader) + 2*REDZONE; pdbmfPvt->chunkItems = chunkItems; pdbmfPvt->chunkSize = pdbmfPvt->allocSize * pdbmfPvt->chunkItems; pdbmfPvt->nAlloc = 0; pdbmfPvt->nFree = 0; pdbmfPvt->nGtSize = 0; pdbmfPvt->freeList = NULL; + VALGRIND_CREATE_MEMPOOL(pdbmfPvt, REDZONE, 0); return(0); } @@ -124,7 +137,7 @@ void* epicsShareAPI dbmfMalloc(size_t size) pitemHeader = (itemHeader *)pnextFree; pitemHeader->pchunkNode->nNotFree += 1; } else { - pmem = malloc(sizeof(itemHeader) + size); + pmem = malloc(sizeof(itemHeader) + 2*REDZONE + size); if(!pmem) { epicsMutexUnlock(pdbmfPvt->lock); printf("dbmfMalloc malloc failed\n"); @@ -133,12 +146,14 @@ void* epicsShareAPI dbmfMalloc(size_t size) pdbmfPvt->nAlloc++; pdbmfPvt->nGtSize++; pitemHeader = (itemHeader *)pmem; - pitemHeader->pchunkNode = NULL; + pitemHeader->pchunkNode = NULL; /* not part of free list */ if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n", (unsigned long)size,pmem); } epicsMutexUnlock(pdbmfPvt->lock); - return((void *)(pmem + sizeof(itemHeader))); + pmem += sizeof(itemHeader) + REDZONE; + VALGRIND_MEMPOOL_ALLOC(pdbmfPvt, pmem, size); + return((void *)pmem); } char * epicsShareAPI dbmfStrdup(unsigned char *str) @@ -160,7 +175,8 @@ void epicsShareAPI dbmfFree(void* mem) printf("dbmfFree called but dbmfInit never called\n"); return; } - pmem -= sizeof(itemHeader); + VALGRIND_MEMPOOL_FREE(pdbmfPvt, mem); + pmem -= sizeof(itemHeader) + REDZONE; epicsMutexMustLock(pdbmfPvt->lock); pitemHeader = (itemHeader *)pmem; if(!pitemHeader->pchunkNode) { From 770d9ab313a5234345e77f7b3c02874b42885ff4 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 155/204] freeListLib: instrument with valgrind --- src/libCom/freeList/freeListLib.c | 32 ++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/libCom/freeList/freeListLib.c b/src/libCom/freeList/freeListLib.c index 77cd566b1..a0ae73837 100644 --- a/src/libCom/freeList/freeListLib.c +++ b/src/libCom/freeList/freeListLib.c @@ -15,6 +15,15 @@ #include #include +#include "valgrind/valgrind.h" + +#ifndef NVALGRIND +/* buffer around allocations to detect out of bounds access */ +#define REDZONE sizeof(double) +#else +#define REDZONE 0 +#endif + #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsMutex.h" @@ -47,6 +56,7 @@ epicsShareFunc void epicsShareAPI pfl->nBlocksAvailable = 0u; pfl->lock = epicsMutexMustCreate(); *ppvt = (void *)pfl; + VALGRIND_CREATE_MEMPOOL(pfl, REDZONE, 0); return; } @@ -78,7 +88,14 @@ epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) epicsMutexMustLock(pfl->lock); ptemp = pfl->head; if(ptemp==0) { - ptemp = (void *)malloc(pfl->nmalloc*pfl->size); + /* layout of each block. nmalloc+1 REDZONEs for nmallocs. + * The first sizeof(void*) bytes are used to store a pointer + * to the next free block. + * + * | RED | size0 ------ | RED | size1 | ... | RED | + * | | next | ----- | + */ + ptemp = (void *)malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE); if(ptemp==0) { epicsMutexUnlock(pfl->lock); return(0); @@ -89,15 +106,17 @@ epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) free(ptemp); return(0); } - pallocmem->memory = ptemp; + pallocmem->memory = ptemp; /* real allocation */ + ptemp += REDZONE; /* skip first REDZONE */ if(pfl->mallochead) pallocmem->next = pfl->mallochead; pfl->mallochead = pallocmem; for(i=0; inmalloc; i++) { ppnext = ptemp; + VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, sizeof(void*)); *ppnext = pfl->head; pfl->head = ptemp; - ptemp = ((char *)ptemp) + pfl->size; + ptemp = ((char *)ptemp) + pfl->size+REDZONE; } ptemp = pfl->head; pfl->nBlocksAvailable += pfl->nmalloc; @@ -106,6 +125,8 @@ epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) pfl->head = *ppnext; pfl->nBlocksAvailable--; epicsMutexUnlock(pfl->lock); + VALGRIND_MEMPOOL_FREE(pfl, ptemp); + VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, pfl->size); return(ptemp); # endif } @@ -119,6 +140,9 @@ epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem) # else void **ppnext; + VALGRIND_MEMPOOL_FREE(pvt, pmem); + VALGRIND_MEMPOOL_ALLOC(pvt, pmem, sizeof(void*)); + epicsMutexMustLock(pfl->lock); ppnext = pmem; *ppnext = pfl->head; @@ -134,6 +158,8 @@ epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt) allocMem *phead; allocMem *pnext; + VALGRIND_DESTROY_MEMPOOL(pvt); + phead = pfl->mallochead; while(phead) { pnext = phead->next; From 6f0814108a062707c18231325c3b1fd941c82fb1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 156/204] libCom: all valgrind hooks to epicsMutex free-list --- src/libCom/misc/epicsExit.c | 4 ++++ src/libCom/osi/epicsMutex.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/libCom/misc/epicsExit.c b/src/libCom/misc/epicsExit.c index 3de8bc6b2..266835eaa 100644 --- a/src/libCom/misc/epicsExit.c +++ b/src/libCom/misc/epicsExit.c @@ -35,6 +35,8 @@ #include "cantProceed.h" #include "epicsExit.h" +void epicsMutexCleanup(void); + typedef struct exitNode { ELLNODE node; epicsExitFunc func; @@ -113,6 +115,8 @@ epicsShareFunc void epicsExitCallAtExits(void) epicsExitCallAtExitsPvt ( pep ); destroyExitPvt ( pep ); } + /* Handle specially to avoid circular reference */ + epicsMutexCleanup(); } epicsShareFunc void epicsExitCallAtThreadExits(void) diff --git a/src/libCom/osi/epicsMutex.cpp b/src/libCom/osi/epicsMutex.cpp index ec3afaab4..15d12baa3 100644 --- a/src/libCom/osi/epicsMutex.cpp +++ b/src/libCom/osi/epicsMutex.cpp @@ -28,6 +28,7 @@ #define epicsExportSharedSymbols #include "epicsStdio.h" #include "epicsThread.h" +#include "valgrind/valgrind.h" #include "ellLib.h" #include "errlog.h" #include "epicsMutex.h" @@ -85,6 +86,7 @@ epicsMutexId epicsShareAPI epicsMutexOsiCreate( firstTime=0; ellInit(&mutexList); ellInit(&freeList); + VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0); epicsMutexGlobalLock = epicsMutexOsdCreate(); } id = epicsMutexOsdCreate(); @@ -98,9 +100,11 @@ epicsMutexId epicsShareAPI epicsMutexOsiCreate( reinterpret_cast < epicsMutexParm * > ( ellFirst(&freeList) ); if(pmutexNode) { ellDelete(&freeList,&pmutexNode->node); + VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode); } else { pmutexNode = static_cast < epicsMutexParm * > ( calloc(1,sizeof(epicsMutexParm)) ); } + VALGRIND_MEMPOOL_ALLOC(&freeList, pmutexNode, sizeof(epicsMutexParm)); pmutexNode->id = id; # ifdef LOG_LAST_OWNER pmutexNode->lastOwner = 0; @@ -127,6 +131,8 @@ void epicsShareAPI epicsMutexDestroy(epicsMutexId pmutexNode) assert ( lockStat == epicsMutexLockOK ); ellDelete(&mutexList,&pmutexNode->node); epicsMutexOsdDestroy(pmutexNode->id); + VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode); + VALGRIND_MEMPOOL_ALLOC(&freeList, &pmutexNode->node, sizeof(pmutexNode->node)); ellAdd(&freeList,&pmutexNode->node); epicsMutexOsdUnlock(epicsMutexGlobalLock); } @@ -162,6 +168,26 @@ epicsMutexLockStatus epicsShareAPI epicsMutexTryLock( return status; } +/* Empty the freeList. + * Called from epicsExit.c, but not via epicsAtExit() + * to avoid the possibility of a circular reference. + */ +extern "C" +void epicsMutexCleanup(void) +{ + ELLNODE *cur; + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + + while((cur=ellGet(&freeList))!=NULL) { + VALGRIND_MEMPOOL_FREE(&freeList, cur); + free(cur); + } + + epicsMutexOsdUnlock(epicsMutexGlobalLock); +} + void epicsShareAPI epicsMutexShow( epicsMutexId pmutexNode, unsigned int level) { From c1f742e7415ce09a7a3e7830ae72000e3672ca31 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 157/204] libCom: valgrind track taskwd freelist --- src/libCom/taskwd/taskwd.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/libCom/taskwd/taskwd.c b/src/libCom/taskwd/taskwd.c index f8a061c2c..b75e8060a 100644 --- a/src/libCom/taskwd/taskwd.c +++ b/src/libCom/taskwd/taskwd.c @@ -25,6 +25,7 @@ #include "epicsStdioRedirect.h" #include "epicsThread.h" #include "epicsMutex.h" +#include "valgrind/valgrind.h" #include "errlog.h" #include "ellLib.h" #include "errMdef.h" @@ -130,9 +131,15 @@ static void twdTask(void *arg) static void twdShutdown(void *arg) { + ELLNODE *cur; twdCtl = twdctlExit; epicsEventSignal(loopEvent); epicsEventWait(exitEvent); + while((cur=ellGet(&fList))!=NULL) { + VALGRIND_MEMPOOL_FREE(&fList, cur); + free(cur); + } + VALGRIND_DESTROY_MEMPOOL(&fList); } static void twdInitOnce(void *arg) @@ -142,6 +149,8 @@ static void twdInitOnce(void *arg) tLock = epicsMutexMustCreate(); mLock = epicsMutexMustCreate(); fLock = epicsMutexMustCreate(); + ellInit(&fList); + VALGRIND_CREATE_MEMPOOL(&fList, 0, 0); twdCtl = twdctlRun; loopEvent = epicsEventMustCreate(epicsEventEmpty); @@ -391,11 +400,14 @@ static union twdNode *newNode(void) pn = (union twdNode *)ellFirst(&fList); if (pn) { ellDelete(&fList, (void *)pn); - epicsMutexUnlock(fLock); - return pn; + VALGRIND_MEMPOOL_FREE(&fList, pn); } epicsMutexUnlock(fLock); - return calloc(1, sizeof(union twdNode)); + if(!pn) + pn = calloc(1, sizeof(union twdNode)); + if(pn) + VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(*pn)); + return pn; } static union twdNode *allocNode(void) @@ -411,6 +423,8 @@ static union twdNode *allocNode(void) static void freeNode(union twdNode *pn) { + VALGRIND_MEMPOOL_FREE(&fList, pn); + VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE)); epicsMutexMustLock(fLock); ellAdd(&fList, (void *)pn); epicsMutexUnlock(fLock); From d94840ae6c84a8001a62ddf35d6198febc32da49 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 13:44:23 -0500 Subject: [PATCH 158/204] std/filter: plugins cleanup freeList --- src/std/filters/arr.c | 7 +++++++ src/std/filters/dbnd.c | 7 +++++++ src/std/filters/sync.c | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/src/std/filters/arr.c b/src/std/filters/arr.c index b872cd623..889a94ef4 100644 --- a/src/std/filters/arr.c +++ b/src/std/filters/arr.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -200,6 +201,11 @@ static chfPluginIf pif = { NULL /* channel_close */ }; +static void arrShutdown(void* ignore) +{ + freeListCleanup(myStructFreeList); +} + static void arrInitialize(void) { static int firstTime = 1; @@ -211,6 +217,7 @@ static void arrInitialize(void) freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); chfPluginRegister("arr", &pif, opts); + epicsAtExit(arrShutdown, NULL); } epicsExportRegistrar(arrInitialize); diff --git a/src/std/filters/dbnd.c b/src/std/filters/dbnd.c index 0278a9a33..ec3a255fc 100644 --- a/src/std/filters/dbnd.c +++ b/src/std/filters/dbnd.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -121,6 +122,11 @@ static chfPluginIf pif = { NULL /* channel_close */ }; +static void dbndShutdown(void* ignore) +{ + freeListCleanup(myStructFreeList); +} + static void dbndInitialize(void) { static int firstTime = 1; @@ -132,6 +138,7 @@ static void dbndInitialize(void) freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); chfPluginRegister("dbnd", &pif, opts); + epicsAtExit(dbndShutdown, NULL); } epicsExportRegistrar(dbndInitialize); diff --git a/src/std/filters/sync.c b/src/std/filters/sync.c index 2646aefa6..ce21eb7e7 100644 --- a/src/std/filters/sync.c +++ b/src/std/filters/sync.c @@ -16,6 +16,7 @@ #include "db_field_log.h" #include "chfPlugin.h" #include "dbState.h" +#include "epicsExit.h" #include "epicsAssert.h" #include "epicsExport.h" @@ -174,6 +175,11 @@ static chfPluginIf pif = { NULL /* channel_close */ }; +static void syncShutdown(void* ignore) +{ + freeListCleanup(myStructFreeList); +} + static void syncInitialize(void) { static int firstTime = 1; @@ -185,6 +191,7 @@ static void syncInitialize(void) freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); chfPluginRegister("sync", &pif, opts); + epicsAtExit(syncShutdown, NULL); } epicsExportRegistrar(syncInitialize); From e298fb4c2755207d790d4cfeadff367ca37cdcfa Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 18:56:39 -0500 Subject: [PATCH 159/204] dbStatic: avoid write past end of DBENTRY::message Prevent long link strings (and maybe others) from overflowing the fixed length string buffer associated with each DBENTRY. --- src/ioc/dbStatic/dbStaticLib.c | 123 +++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index e4ba47e2f..64f0394b1 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -211,6 +211,27 @@ static char *getpMessage(DBENTRY *pdbentry) return msg; } +static +void dbMsgCpy(DBENTRY *pdbentry, const char *msg) +{ + getpMessage(pdbentry); + strncpy(pdbentry->message, msg, messagesize-1); + pdbentry->message[messagesize-1] = '\0'; +} + +static +void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3); + +static +void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) +{ + va_list args; + getpMessage(pdbentry); + va_start(args, fmt); + epicsVsnprintf(pdbentry->message, messagesize, fmt, args); + va_end(args); +} + /*Public only for dbStaticNoRun*/ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) { @@ -1718,15 +1739,29 @@ char * dbGetString(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; - char *message; DBLINK *plink; - message = getpMessage(pdbentry); - if(!pflddes) {strcpy(message,"fldDes not found"); return(message);} + if(!pflddes) { + dbMsgCpy(pdbentry, "fldDes not found"); + return pdbentry->message; + } switch (pflddes->field_type) { case DBF_STRING: - if(!pfield) {strcpy(message,"Field not found"); return(message);} - strcpy(message, (char *)pfield); + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + if(!pfield) { + dbMsgCpy(pdbentry, "Field not allocated (NULL)"); + return pdbentry->message; + } + break; + default: + break; + } + + switch (pflddes->field_type) { + case DBF_STRING: + dbMsgCpy(pdbentry, (char *)pfield); break; case DBF_CHAR: case DBF_UCHAR: @@ -1742,30 +1777,26 @@ char * dbGetString(DBENTRY *pdbentry) return(dbGetStringNum(pdbentry)); case DBF_INLINK: case DBF_OUTLINK: - if(!pfield) {strcpy(message,"Field not found"); return(message);} plink = (DBLINK *)pfield; switch(plink->type) { case CONSTANT: if(plink->value.constantStr) { - strcpy(message,plink->value.constantStr); + dbMsgCpy(pdbentry, plink->value.constantStr); } else { - strcpy(message,""); + dbMsgCpy(pdbentry, ""); } break; case MACRO_LINK: if(plink->value.macro_link.macroStr) { - strcpy(message,plink->value.macro_link.macroStr); + dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); } else { - strcpy(message,""); + dbMsgCpy(pdbentry, ""); } break; - case PN_LINK: - if(plink->value.pv_link.pvname) - strcpy(message,plink->value.pv_link.pvname); - else - strcpy(message,""); - strcat(message," "); - strcat(message,msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); + case PN_LINK: + dbMsgPrint(pdbentry, "%s %s", + plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", + msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); break; case PV_LINK: case CA_LINK: @@ -1779,68 +1810,63 @@ char * dbGetString(DBENTRY *pdbentry) else if(pvlMask&pvlOptCP) ppind=3; else if(pvlMask&pvlOptCPP) ppind=4; else ppind=0; - if (plink->value.pv_link.pvname) { - strcpy(message, plink->value.pv_link.pvname); - if (pvlMask & pvlOptTSELisTime) - strcat(message, ".TIME"); - } else - strcpy(message,""); - strcat(message," "); - strcat(message,ppstring[ppind]); - strcat(message," "); - strcat(message,msstring[pvlMask&pvlOptMsMode]); + dbMsgPrint(pdbentry, "%s%s %s %s", + plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", + (pvlMask & pvlOptTSELisTime) ? ".TIME" : "", + ppstring[ppind], + msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); break; } case VME_IO: - sprintf(message,"#C%d S%d @%s", + dbMsgPrint(pdbentry, "#C%d S%d @%s", plink->value.vmeio.card,plink->value.vmeio.signal, plink->value.vmeio.parm); break; case CAMAC_IO: - sprintf(message,"#B%d C%d N%d A%d F%d @%s", + dbMsgPrint(pdbentry, "#B%d C%d N%d A%d F%d @%s", plink->value.camacio.b,plink->value.camacio.c, plink->value.camacio.n,plink->value.camacio.a, plink->value.camacio.f,plink->value.camacio.parm); break; case RF_IO: - sprintf(message,"#R%d M%d D%d E%d", + dbMsgPrint(pdbentry, "#R%d M%d D%d E%d", plink->value.rfio.cryo, plink->value.rfio.micro, plink->value.rfio.dataset, plink->value.rfio.element); break; case AB_IO: - sprintf(message,"#L%d A%d C%d S%d @%s", + dbMsgPrint(pdbentry, "#L%d A%d C%d S%d @%s", plink->value.abio.link,plink->value.abio.adapter, plink->value.abio.card,plink->value.abio.signal, plink->value.abio.parm); break; case GPIB_IO: - sprintf(message,"#L%d A%d @%s", + dbMsgPrint(pdbentry, "#L%d A%d @%s", plink->value.gpibio.link,plink->value.gpibio.addr, plink->value.gpibio.parm); break; case BITBUS_IO: - sprintf(message,"#L%u N%u P%u S%u @%s", + dbMsgPrint(pdbentry, "#L%u N%u P%u S%u @%s", plink->value.bitbusio.link,plink->value.bitbusio.node, plink->value.bitbusio.port,plink->value.bitbusio.signal, plink->value.bitbusio.parm); break; case BBGPIB_IO: - sprintf(message,"#L%u B%u G%u @%s", + dbMsgPrint(pdbentry, "#L%u B%u G%u @%s", plink->value.bbgpibio.link,plink->value.bbgpibio.bbaddr, plink->value.bbgpibio.gpibaddr,plink->value.bbgpibio.parm); break; case INST_IO: - sprintf(message,"@%s", plink->value.instio.string); + dbMsgPrint(pdbentry, "@%s", plink->value.instio.string); break; case VXI_IO : if (plink->value.vxiio.flag == VXIDYNAMIC) - sprintf(message,"#V%d C%d S%d @%s", + dbMsgPrint(pdbentry, "#V%d C%d S%d @%s", plink->value.vxiio.frame,plink->value.vxiio.slot, plink->value.vxiio.signal,plink->value.vxiio.parm); else - sprintf(message,"#V%d S%d @%s", + dbMsgPrint(pdbentry, "#V%d S%d @%s", plink->value.vxiio.la,plink->value.vxiio.signal, plink->value.vxiio.parm); break; @@ -1851,16 +1877,15 @@ char * dbGetString(DBENTRY *pdbentry) case DBF_FWDLINK: { DBLINK *plink=(DBLINK *)pfield; - if(!pfield) {strcpy(message,"Field not found"); return(message);} switch(plink->type) { case CONSTANT: - strcpy(message,"0"); + dbMsgCpy(pdbentry, "0"); break; case MACRO_LINK: if(plink->value.macro_link.macroStr) { - strcpy(message,plink->value.macro_link.macroStr); + dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); } else { - strcpy(message,""); + dbMsgCpy(pdbentry, ""); } break; case PV_LINK: @@ -1872,14 +1897,9 @@ char * dbGetString(DBENTRY *pdbentry) pvlMask = plink->value.pv_link.pvlMask; if(pvlMask&pvlOptCA) ppind=2; else ppind=0; - if(plink->value.pv_link.pvname) - strcpy(message,plink->value.pv_link.pvname); - else - strcpy(message,""); - if(ppind) { - strcat(message," "); - strcat(message,ppstring[ppind]); - } + dbMsgPrint(pdbentry, "%s %s", + plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", + ppstring[ppind]); break; } default : @@ -1890,7 +1910,7 @@ char * dbGetString(DBENTRY *pdbentry) default: return(NULL); } - return (message); + return pdbentry->message; } long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) @@ -3265,7 +3285,8 @@ void dbReportDeviceConfig(dbBase *pdbbase,FILE *report) plink = pdbentry->pfield; linkType = plink->type; if(bus[linkType][0]==0) continue; - strcpy(linkValue,dbGetString(pdbentry)); + strncpy(linkValue,dbGetString(pdbentry), NELEMENTS(linkValue)-1); + linkValue[NELEMENTS(linkValue)-1] = '\0'; status = dbFindField(pdbentry,"DTYP"); if(status) break; strcpy(dtypValue,dbGetString(pdbentry)); From ef39f658cdbcb61ca47f101eb5f3364dc208777a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 19:42:31 -0500 Subject: [PATCH 160/204] dbStaticLib: dbGetString() fix spaces in link modifiers --- src/ioc/dbStatic/dbStaticLib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 64f0394b1..c7271b5df 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -52,8 +52,8 @@ static char *pNullString = ""; #define messagesize 276 #define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80) -static char *ppstring[5]={"NPP","PP","CA","CP","CPP"}; -static char *msstring[4]={"NMS","MS","MSI","MSS"}; +static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"}; +static char *msstring[4]={" NMS"," MS"," MSI"," MSS"}; epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = { {"CONSTANT",CONSTANT}, @@ -1794,7 +1794,7 @@ char * dbGetString(DBENTRY *pdbentry) } break; case PN_LINK: - dbMsgPrint(pdbentry, "%s %s", + dbMsgPrint(pdbentry, "%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); break; @@ -1810,7 +1810,7 @@ char * dbGetString(DBENTRY *pdbentry) else if(pvlMask&pvlOptCP) ppind=3; else if(pvlMask&pvlOptCPP) ppind=4; else ppind=0; - dbMsgPrint(pdbentry, "%s%s %s %s", + dbMsgPrint(pdbentry, "%s%s%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", (pvlMask & pvlOptTSELisTime) ? ".TIME" : "", ppstring[ppind], @@ -1897,9 +1897,9 @@ char * dbGetString(DBENTRY *pdbentry) pvlMask = plink->value.pv_link.pvlMask; if(pvlMask&pvlOptCA) ppind=2; else ppind=0; - dbMsgPrint(pdbentry, "%s %s", + dbMsgPrint(pdbentry, "%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", - ppstring[ppind]); + ppind ? ppstring[ppind] : ""); break; } default : From 29d2b7f6a2cd5e02a65c534bf5ada6ff2c1b9134 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 28 Dec 2015 16:36:58 -0500 Subject: [PATCH 161/204] RTEMS: readline missing } --- src/libCom/osi/os/RTEMS/osdReadline.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libCom/osi/os/RTEMS/osdReadline.c b/src/libCom/osi/os/RTEMS/osdReadline.c index 6f1e6732c..03ed7ed90 100644 --- a/src/libCom/osi/os/RTEMS/osdReadline.c +++ b/src/libCom/osi/os/RTEMS/osdReadline.c @@ -54,6 +54,7 @@ osdReadline (const char *prompt, struct readlineContext *context) if (nl) *nl = '\0'; + } return line; } From b7c2815c213c5060dfe763e8eb0867195f6a22ad Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 7 Jan 2016 14:08:51 -0500 Subject: [PATCH 162/204] linkRetargetLinkTest set number of tests --- src/std/rec/test/linkRetargetLinkTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/std/rec/test/linkRetargetLinkTest.c b/src/std/rec/test/linkRetargetLinkTest.c index 79e271a83..e829fa6e0 100644 --- a/src/std/rec/test/linkRetargetLinkTest.c +++ b/src/std/rec/test/linkRetargetLinkTest.c @@ -80,7 +80,7 @@ static void testRetarget(void) MAIN(linkRetargetLinkTest) { - testPlan(0); + testPlan(10); testRetarget(); return testDone(); } From d6eea14fd0a127a156967c48c40b25d2a9907b67 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 11 Jan 2016 23:01:26 -0500 Subject: [PATCH 163/204] dbCa: dbCaGet/PutLink dynamic size dbCaGetLink return actual number of elements w/o zero padding. dbCaPutLink write only requested number of elements, w/o padding --- src/ioc/db/dbCa.c | 24 +++++++----------------- src/ioc/db/dbCaPvt.h | 1 + src/ioc/db/test/dbCaLinkTest.c | 34 ++++++++++------------------------ 3 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 5e7a609fe..981503fee 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -346,19 +346,16 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, assert(pca->pgetNative); status = fConvert(pca->pgetNative, pdest, 0); } else { - unsigned long ntoreport = *nelements, ntoget; + unsigned long ntoget = *nelements; struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off); aConvert = dbGetConvertRoutine[newType][dbrType]; assert(pca->pgetNative); - if (ntoreport > pca->nelements) - ntoreport = pca->nelements; - ntoget = ntoreport; if (ntoget > pca->usedelements) ntoget = pca->usedelements; - *nelements = ntoreport; + *nelements = ntoget; memset((void *)&dbAddr, 0, sizeof(dbAddr)); dbAddr.pfield = pca->pgetNative; @@ -366,12 +363,6 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, dbAddr.field_size = MAX_STRING_SIZE; /*Ignore error return*/ aConvert(&dbAddr, pdest, ntoget, ntoget, 0); - if(ntogetelementSize+(char*)pca->pgetNative, - 0, - (ntoreport-ntoget)*pca->elementSize); - } } done: if (pstat) *pstat = pca->stat; @@ -418,6 +409,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType, if (!pca->pputNative) { pca->pputNative = dbCalloc(pca->nelements, dbr_value_size[ca_field_type(pca->chid)]); + pca->putnelements = 0; /* Fixed and disabled by ANJ, see comment above. plink->value.pv_link.pvlMask |= pvlOptOutNative; */ @@ -439,10 +431,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType, if(nRequest>pca->nelements) nRequest = pca->nelements; status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0); - if(nRequestnelements) { - long elemsize = dbr_value_size[ca_field_type(pca->chid)]; - memset(nRequest*elemsize+(char*)pca->pputNative, 0, (pca->nelements-nRequest)*elemsize); - } + pca->putnelements = nRequest; } link_action |= CA_WRITE_NATIVE; pca->gotOutNative = TRUE; @@ -767,6 +756,7 @@ static void eventCallback(struct event_handler_args arg) goto done; } assert(arg.dbr); + assert(arg.count<=pca->nelements); size = arg.count * dbr_value_size[arg.type]; if (arg.type == DBR_TIME_STRING && ca_field_type(pca->chid) == DBR_ENUM) { @@ -988,11 +978,11 @@ static void dbCaTask(void *arg) assert(pca->pputNative); if (pca->putType == CA_PUT) { status = ca_array_put( - pca->dbrType, pca->nelements, + pca->dbrType, pca->putnelements, pca->chid, pca->pputNative); } else if (pca->putType==CA_PUT_CALLBACK) { status = ca_array_put_callback( - pca->dbrType, pca->nelements, + pca->dbrType, pca->putnelements, pca->chid, pca->pputNative, putComplete, pca); } else { diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index b50867eda..6149d08ae 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -54,6 +54,7 @@ typedef struct caLink size_t elementSize; /* size of one element in pgetNative */ unsigned long nelements; /* PVs max array size */ unsigned long usedelements; /* currently used in pgetNative */ + unsigned long putnelements; /* currently used in pputNative */ char hasReadAccess; char hasWriteAccess; char isConnected; diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index c0b5f7b9d..43a00e8eb 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -294,45 +294,35 @@ static void fillArrayDouble(double *buf, unsigned count, double first) static void checkArray(const char *msg, epicsInt32 *buf, epicsInt32 first, - unsigned used, unsigned total) + unsigned used) { int match = 1; unsigned i; epicsInt32 x, *b; for(b=buf,x=first,i=0;inelm); + checkArray("array update", bufsrc, 1, nReq); } else { testFail("dbGetLink"); testSkip(2, "dbGetLink fails"); @@ -415,12 +405,8 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - /* CA links always write the full target array length */ - testOp("%ld",(long)ptarg->nord,==,(long)ptarg->nelm); - /* However, if the source length is less, then the target - * is zero filled - */ - checkArray("array update", buftarg, 2, num, ptarg->nelm); + testOp("%ld",(long)ptarg->nord,==,(long)num); + dbScanUnlock((dbCommon*)ptarg); /* write again to ensure that buffer is completely updated */ @@ -429,8 +415,8 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - testOp("%ld",(long)ptarg->nord,==,(long)ptarg->nelm); - checkArray("array update", buftarg, 3, num, ptarg->nelm); + testOp("%ld",(long)ptarg->nord,==,(long)num); + checkArray("array update", buftarg, 3, num); dbScanUnlock((dbCommon*)ptarg); testIocShutdownOk(); @@ -513,7 +499,7 @@ static void testreTargetTypeChange(void) dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); - checkArrayDouble("array update", bufsrc, 1, 5, psrc->nelm); + checkArrayDouble("array update", bufsrc, 1, 5); dbScanUnlock((dbCommon*)psrc); testDiag("Retarget"); @@ -523,7 +509,7 @@ static void testreTargetTypeChange(void) dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); - checkArrayDouble("array update", bufsrc, 2, 5, psrc->nelm); + checkArrayDouble("array update", bufsrc, 2, 5); dbScanUnlock((dbCommon*)psrc); testIocShutdownOk(); @@ -583,7 +569,7 @@ static void testCAC(void) MAIN(dbCaLinkTest) { - testPlan(91); + testPlan(87); testNativeLink(); testStringLink(); testCP(); From 1255e75828083bc30fa50ce1ca587de12b8a7c57 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 12 Jan 2016 09:32:47 -0500 Subject: [PATCH 164/204] db/test: missing coverage for dbGetLink w/ larger element count --- src/ioc/db/test/dbCaLinkTest.c | 40 +++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index 43a00e8eb..e2a87e079 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -16,6 +16,7 @@ #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" +#include "cantProceed.h" #include "epicsEvent.h" #include "iocInit.h" #include "dbBase.h" @@ -345,12 +346,13 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) char buf[100]; arrRecord *psrc, *ptarg; DBLINK *psrclnk; - epicsInt32 *bufsrc, *buftarg; + epicsInt32 *bufsrc, *buftarg, *tmpbuf; long nReq; - unsigned num; + unsigned num_min, num_max; testDiag("Link to a array numeric field"); + /* source.INP = "target CA" */ epicsSnprintf(buf, sizeof(buf), "TARGET=target CA,FTVL=LONG,SNELM=%u,TNELM=%u", nsrc, ntarg); testDiag("%s", buf); @@ -374,9 +376,15 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) bufsrc = psrc->bptr; buftarg= ptarg->bptr; - num=psrc->nelm; - if(num>ptarg->nelm) - num=ptarg->nelm; + num_max=num_min=psrc->nelm; + if(num_min>ptarg->nelm) + num_min=ptarg->nelm; + if(num_maxnelm) + num_max=ptarg->nelm; + /* always request more than can possibly be filled */ + num_max += 2; + + tmpbuf = callocMustSucceed(num_max, sizeof(*tmpbuf), "tmpbuf"); startWait(psrclnk); @@ -389,15 +397,26 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) waitForUpdate(psrclnk); dbScanLock((dbCommon*)psrc); + testDiag("fetch source.INP into source.BPTR"); nReq = psrc->nelm; if(dbGetLink(psrclnk, DBR_LONG, bufsrc, NULL, &nReq)==0) { testPass("dbGetLink"); - testOp("%ld",nReq,==,(long)num); + testOp("%ld",nReq,==,(long)num_min); checkArray("array update", bufsrc, 1, nReq); } else { testFail("dbGetLink"); testSkip(2, "dbGetLink fails"); } + testDiag("fetch source.INP into temp buffer w/ larger capacity"); + nReq = num_max; + if(dbGetLink(psrclnk, DBR_LONG, tmpbuf, NULL, &nReq)==0) { + testPass("dbGetLink"); + testOp("%ld",nReq,==,(long)ntarg); + checkArray("array update", tmpbuf, 1, nReq); + } else { + testFail("dbGetLink"); + testSkip(2, "dbGetLink fails"); + } dbScanUnlock((dbCommon*)psrc); fillArray(bufsrc, psrc->nelm, 2); @@ -405,7 +424,7 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - testOp("%ld",(long)ptarg->nord,==,(long)num); + testOp("%ld",(long)ptarg->nord,==,(long)num_min); dbScanUnlock((dbCommon*)ptarg); @@ -415,14 +434,15 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - testOp("%ld",(long)ptarg->nord,==,(long)num); - checkArray("array update", buftarg, 3, num); + testOp("%ld",(long)ptarg->nord,==,(long)num_min); + checkArray("array update", buftarg, 3, num_min); dbScanUnlock((dbCommon*)ptarg); testIocShutdownOk(); testdbCleanup(); + free(tmpbuf); /* records don't cleanup after themselves * so do here to silence valgrind */ @@ -569,7 +589,7 @@ static void testCAC(void) MAIN(dbCaLinkTest) { - testPlan(87); + testPlan(99); testNativeLink(); testStringLink(); testCP(); From b74ecff2fc8d0e532712661bc0c72e608968ddf2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 12 Jan 2016 09:38:28 -0500 Subject: [PATCH 165/204] dbCa: set putnelements for scalar case ensure that putnelements is kept in sync. w/ pputNative --- src/ioc/db/dbCa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 981503fee..8d8d19ae2 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -419,6 +419,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType, fConvert = dbFastPutConvertRoutine[dbrType][newType]; status = fConvert(pbuffer, pca->pputNative, 0); + pca->putnelements = 1; } else { struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, const void *from, long nreq, long nfrom, long off); From 4e7b185977f41a6153ff35fc775bb1d0b4452414 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 12 Jan 2016 10:13:10 -0500 Subject: [PATCH 166/204] update release notes --- documentation/RELEASE_NOTES.html | 16 ++++++++++++++++ src/libCom/Makefile | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 8ae4e76d3..e8fbd540a 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,22 @@ --> +

Valgrind instrumentation

+ +

The header valgrind/valgrind.h is now included in, and installed by, Base. +When included this file defines some macros which expand to provide hints +to the Valgrind runtime. +These hints are now added to some free-lists within libCom, including freeListLib, +to allow valgrind to provide more accurate information about potential leaks.

+ +

valgrind.h automatically disables itself when included targets unsupported by Valgrind. +It can also explicitly be disabled by defining the macro NVALGRIND. +See src/libCom/Makefile for an example.

+ +

As a matter of policy valgrind.h is not, and will not be, included by any header file installed by +Base, and will remain an internal implementation detail. +Support modules which choose to use valgrind.h are advised to avoid to do likewise.

+

Database Multi-locking

dbLock.c is re-written with an expanded API, and the removal of global mutex locks.

diff --git a/src/libCom/Makefile b/src/libCom/Makefile index 696b1cf98..43aef2d46 100644 --- a/src/libCom/Makefile +++ b/src/libCom/Makefile @@ -9,7 +9,8 @@ TOP = ../.. include $(TOP)/configure/CONFIG -USR_CFLAGS += -DUSE_VALGRIND +# Uncomment this to remove the (benign) valgrind helper stubs +#USR_CFLAGS += -DNVALGRIND SRC = $(TOP)/src LIBCOM = $(SRC)/libCom From ecedd9c3623b70345908b8f11ffa03c61a9f044c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 12 Jan 2016 11:36:58 -0500 Subject: [PATCH 167/204] Move dbGetStringNum() to dbStaticLib.c use getpMessage() and keep it private --- src/ioc/dbStatic/dbStaticLib.c | 222 ++++++++++++++++++++++++++++++++- src/ioc/dbStatic/dbStaticRun.c | 218 -------------------------------- 2 files changed, 221 insertions(+), 219 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index c7271b5df..1039da9b5 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -52,6 +52,9 @@ static char *pNullString = ""; #define messagesize 276 #define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80) +/* must be long enough to hold 32-bit signed integer in base 10 */ +STATIC_ASSERT(messagesize>=11); + static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"}; static char *msstring[4]={" NMS"," MS"," MSI"," MSS"}; @@ -83,6 +86,133 @@ static int mapDBFtoDCT[DBF_NOACCESS+1] = { DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK, DCT_NOACCESS}; +static char hex_digit_to_ascii[16]={'0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f'}; + +static void ulongToHexString(epicsUInt32 source,char *pdest) +{ + epicsUInt32 val,temp; + char digit[10]; + int i,j; + + if(source==0) { + strcpy(pdest,"0x0"); + return; + } + *pdest++ = '0'; *pdest++ = 'x'; + val = source; + for(i=0; val!=0; i++) { + temp = val/16; + digit[i] = hex_digit_to_ascii[val - temp*16]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return; +} + +static double delta[2] = {1e-6, 1e-15}; +static int precision[2] = {6, 14}; +static void realToString(double value, char *preturn, int isdouble) +{ + double absvalue; + int logval,prec; + size_t end; + char tstr[30]; + char *ptstr = &tstr[0]; + int round; + int ise = FALSE; + char *loce = NULL; + + if (value == 0) { + strcpy(preturn, "0"); + return; + } + + absvalue = value < 0 ? -value : value; + if (absvalue < (double)INT_MAX) { + epicsInt32 intval = (epicsInt32) value; + double diff = value - intval; + + if (diff < 0) diff = -diff; + if (diff < absvalue * delta[isdouble]) { + cvtLongToString(intval, preturn); + return; + } + } + + /*Now starts the hard cases*/ + if (value < 0) { + *preturn++ = '-'; + value = -value; + } + + logval = (int)log10(value); + if (logval > 6 || logval < -2) { + int nout; + + ise = TRUE; + prec = precision[isdouble]; + nout = sprintf(ptstr, "%.*e", prec, value); + loce = strchr(ptstr, 'e'); + + if (!loce) { + ptstr[nout] = 0; + strcpy(preturn, ptstr); + return; + } + + *loce++ = 0; + } else { + prec = precision[isdouble] - logval; + if ( prec < 0) prec = 0; + sprintf(ptstr, "%.*f", prec, value); + } + + if (prec > 0) { + end = strlen(ptstr) - 1; + round = FALSE; + while (end > 0) { + if (tstr[end] == '.') {end--; break;} + if (tstr[end] == '0') {end--; continue;} + if (!round && end < precision[isdouble]) break; + if (!round && tstr[end] < '8') break; + if (tstr[end-1] == '.') { + if (round) end = end-2; + break; + } + if (tstr[end-1] != '9') break; + round = TRUE; + end--; + } + tstr[end+1] = 0; + while (round) { + if (tstr[end] < '9') {tstr[end]++; break;} + if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;} + tstr[end--] = '0'; + } + } + strcpy(preturn, &tstr[0]); + if (ise) { + if (!(strchr(preturn, '.'))) strcat(preturn, ".0"); + strcat(preturn, "e"); + strcat(preturn, loce); + } +} + +static void floatToString(float value,char *preturn) +{ + realToString((double)value,preturn,0); + return; +} + +static void doubleToString(double value,char *preturn) +{ + realToString(value,preturn,1); + return; +} /*forward references for private routines*/ static FILE *openOutstream(const char *filename); @@ -207,7 +337,7 @@ static char *getpMessage(DBENTRY *pdbentry) msg = dbCalloc(1, messagesize); pdbentry->message = msg; } - *msg = 0; + *msg = '\0'; return msg; } @@ -1913,6 +2043,96 @@ char * dbGetString(DBENTRY *pdbentry) return pdbentry->message; } +char *dbGetStringNum(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + char *message; + unsigned char cvttype; + + /* the following assumes that messagesize is large enough + * to hold the base 10 encoded value of a 32-bit integer. + */ + message = getpMessage(pdbentry); + cvttype = pflddes->base; + switch (pflddes->field_type) { + case DBF_CHAR: + if(cvttype==CT_DECIMAL) + cvtCharToString(*(char*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(char*)pfield),message); + break; + case DBF_UCHAR: + if(cvttype==CT_DECIMAL) + cvtUcharToString(*(unsigned char*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(unsigned char*)pfield),message); + break; + case DBF_SHORT: + if(cvttype==CT_DECIMAL) + cvtShortToString(*(short*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(short*)pfield),message); + break; + case DBF_USHORT: + case DBF_ENUM: + if(cvttype==CT_DECIMAL) + cvtUshortToString(*(unsigned short*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(unsigned short*)pfield),message); + break; + case DBF_LONG: + if(cvttype==CT_DECIMAL) + cvtLongToString(*(epicsInt32*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(epicsInt32*)pfield), message); + break; + case DBF_ULONG: + if(cvttype==CT_DECIMAL) + cvtUlongToString(*(epicsUInt32 *)pfield, message); + else + ulongToHexString(*(epicsUInt32*)pfield, message); + break; + case DBF_FLOAT: + floatToString(*(float *)pfield,message); + break; + case DBF_DOUBLE: + doubleToString(*(double *)pfield,message); + break; + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + short choice_ind; + char *pchoice; + + if(!pfield) {dbMsgCpy(pdbentry, "Field not found"); return(message);} + choice_ind = *((short *) pdbentry->pfield); + if(!pdbMenu || choice_ind<0 || choice_ind>=pdbMenu->nChoice) + return(NULL); + pchoice = pdbMenu->papChoiceValue[choice_ind]; + dbMsgCpy(pdbentry, pchoice); + } + break; + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + char *pchoice; + short choice_ind; + + if(!pfield) {dbMsgCpy(pdbentry, "Field not found"); return(message);} + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(NULL); + choice_ind = *((short *) pdbentry->pfield); + if(choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice) + return(NULL); + pchoice = pdbDeviceMenu->papChoice[choice_ind]; + dbMsgCpy(pdbentry, pchoice); + } + break; + default: + return(NULL); + } + return (message); +} + long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) { short i; diff --git a/src/ioc/dbStatic/dbStaticRun.c b/src/ioc/dbStatic/dbStaticRun.c index f7fcbd5c5..886fa3501 100644 --- a/src/ioc/dbStatic/dbStaticRun.c +++ b/src/ioc/dbStatic/dbStaticRun.c @@ -31,136 +31,6 @@ #include "devSup.h" #include "special.h" - -static char hex_digit_to_ascii[16]={'0','1','2','3','4','5','6','7','8','9', - 'a','b','c','d','e','f'}; - -static void ulongToHexString(epicsUInt32 source,char *pdest) -{ - epicsUInt32 val,temp; - char digit[10]; - int i,j; - - if(source==0) { - strcpy(pdest,"0x0"); - return; - } - *pdest++ = '0'; *pdest++ = 'x'; - val = source; - for(i=0; val!=0; i++) { - temp = val/16; - digit[i] = hex_digit_to_ascii[val - temp*16]; - val = temp; - } - for(j=i-1; j>=0; j--) { - *pdest++ = digit[j]; - } - *pdest = 0; - return; -} - -static double delta[2] = {1e-6, 1e-15}; -static int precision[2] = {6, 14}; -static void realToString(double value, char *preturn, int isdouble) -{ - double absvalue; - int logval,prec; - size_t end; - char tstr[30]; - char *ptstr = &tstr[0]; - int round; - int ise = FALSE; - char *loce = NULL; - - if (value == 0) { - strcpy(preturn, "0"); - return; - } - - absvalue = value < 0 ? -value : value; - if (absvalue < (double)INT_MAX) { - epicsInt32 intval = (epicsInt32) value; - double diff = value - intval; - - if (diff < 0) diff = -diff; - if (diff < absvalue * delta[isdouble]) { - cvtLongToString(intval, preturn); - return; - } - } - - /*Now starts the hard cases*/ - if (value < 0) { - *preturn++ = '-'; - value = -value; - } - - logval = (int)log10(value); - if (logval > 6 || logval < -2) { - int nout; - - ise = TRUE; - prec = precision[isdouble]; - nout = sprintf(ptstr, "%.*e", prec, value); - loce = strchr(ptstr, 'e'); - - if (!loce) { - ptstr[nout] = 0; - strcpy(preturn, ptstr); - return; - } - - *loce++ = 0; - } else { - prec = precision[isdouble] - logval; - if ( prec < 0) prec = 0; - sprintf(ptstr, "%.*f", prec, value); - } - - if (prec > 0) { - end = strlen(ptstr) - 1; - round = FALSE; - while (end > 0) { - if (tstr[end] == '.') {end--; break;} - if (tstr[end] == '0') {end--; continue;} - if (!round && end < precision[isdouble]) break; - if (!round && tstr[end] < '8') break; - if (tstr[end-1] == '.') { - if (round) end = end-2; - break; - } - if (tstr[end-1] != '9') break; - round = TRUE; - end--; - } - tstr[end+1] = 0; - while (round) { - if (tstr[end] < '9') {tstr[end]++; break;} - if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;} - tstr[end--] = '0'; - } - } - strcpy(preturn, &tstr[0]); - if (ise) { - if (!(strchr(preturn, '.'))) strcat(preturn, ".0"); - strcat(preturn, "e"); - strcat(preturn, loce); - } -} - -static void floatToString(float value,char *preturn) -{ - realToString((double)value,preturn,0); - return; -} - -static void doubleToString(double value,char *preturn) -{ - realToString(value,preturn,1); - return; -} - - static long do_nothing(struct dbCommon *precord) { return 0; } /* Dummy DSXT used for soft device supports */ @@ -480,95 +350,7 @@ epicsShareFunc int dbIsDefaultValue(DBENTRY *pdbentry) } return(FALSE); } - -char *dbGetStringNum(DBENTRY *pdbentry) -{ - dbFldDes *pflddes = pdbentry->pflddes; - void *pfield = pdbentry->pfield; - char *message; - unsigned char cvttype; - if(!pdbentry->message) pdbentry->message = dbCalloc(1,50); - message = pdbentry->message; - cvttype = pflddes->base; - switch (pflddes->field_type) { - case DBF_CHAR: - if(cvttype==CT_DECIMAL) - cvtCharToString(*(char*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(char*)pfield),message); - break; - case DBF_UCHAR: - if(cvttype==CT_DECIMAL) - cvtUcharToString(*(unsigned char*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(unsigned char*)pfield),message); - break; - case DBF_SHORT: - if(cvttype==CT_DECIMAL) - cvtShortToString(*(short*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(short*)pfield),message); - break; - case DBF_USHORT: - case DBF_ENUM: - if(cvttype==CT_DECIMAL) - cvtUshortToString(*(unsigned short*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(unsigned short*)pfield),message); - break; - case DBF_LONG: - if(cvttype==CT_DECIMAL) - cvtLongToString(*(epicsInt32*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(epicsInt32*)pfield), message); - break; - case DBF_ULONG: - if(cvttype==CT_DECIMAL) - cvtUlongToString(*(epicsUInt32 *)pfield, message); - else - ulongToHexString(*(epicsUInt32*)pfield, message); - break; - case DBF_FLOAT: - floatToString(*(float *)pfield,message); - break; - case DBF_DOUBLE: - doubleToString(*(double *)pfield,message); - break; - case DBF_MENU: { - dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; - short choice_ind; - char *pchoice; - - if(!pfield) {strcpy(message,"Field not found"); return(message);} - choice_ind = *((short *) pdbentry->pfield); - if(!pdbMenu || choice_ind<0 || choice_ind>=pdbMenu->nChoice) - return(NULL); - pchoice = pdbMenu->papChoiceValue[choice_ind]; - strcpy(message, pchoice); - } - break; - case DBF_DEVICE: { - dbDeviceMenu *pdbDeviceMenu; - char *pchoice; - short choice_ind; - - if(!pfield) {strcpy(message,"Field not found"); return(message);} - pdbDeviceMenu = dbGetDeviceMenu(pdbentry); - if(!pdbDeviceMenu) return(NULL); - choice_ind = *((short *) pdbentry->pfield); - if(choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice) - return(NULL); - pchoice = pdbDeviceMenu->papChoice[choice_ind]; - strcpy(message, pchoice); - } - break; - default: - return(NULL); - } - return (message); -} - long dbPutStringNum(DBENTRY *pdbentry,const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; From e227ae359042f577c2bad431bdc6d777bbb3ac9f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 12 Jan 2016 11:37:01 -0500 Subject: [PATCH 168/204] update asTest test dbGetString() and fix cleanup order --- src/std/rec/test/asTest.c | 2 +- src/std/rec/test/asTest.db | 1 + src/std/rec/test/asTestLib.c | 52 +++++++++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/std/rec/test/asTest.c b/src/std/rec/test/asTest.c index 412e9ca33..44ef66fd2 100644 --- a/src/std/rec/test/asTest.c +++ b/src/std/rec/test/asTest.c @@ -42,7 +42,7 @@ epicsShareFunc void testRestore(void); MAIN(asTest) { - testPlan(29); + testPlan(42); testRestore(); return testDone(); } diff --git a/src/std/rec/test/asTest.db b/src/std/rec/test/asTest.db index a50d1998b..6a4eea4bd 100644 --- a/src/std/rec/test/asTest.db +++ b/src/std/rec/test/asTest.db @@ -1,4 +1,5 @@ record(ao, "rec0") { + field(DESC, "foobar") field(DTYP, "asTest") field(VAL, "1") field(OUT, "rec0.DISV") diff --git a/src/std/rec/test/asTestLib.c b/src/std/rec/test/asTestLib.c index 17a1ac702..2eca72775 100644 --- a/src/std/rec/test/asTestLib.c +++ b/src/std/rec/test/asTestLib.c @@ -39,6 +39,17 @@ static unsigned iran; +static +int checkGetString(DBENTRY *pent, const char *expect) +{ + dbCommon *prec = pent->precnode->precord; + const char *actual = dbGetString(pent); + int ret = strcmp(actual, expect); + testOk(ret==0, "dbGetString(\"%s.%s\") -> '%s' == '%s'", prec->name, + pent->pflddes->name, actual, expect); + return ret; +} + static void hookPass0(initHookState state) { DBENTRY entry; @@ -48,17 +59,46 @@ static void hookPass0(initHookState state) dbInitEntry(pdbbase, &entry); + testDiag("restore integer pass0"); /* rec0.VAL is initially 1, set it to 2 */ if(dbFindRecord(&entry, "rec0.VAL")==0) { aoRecord *prec = entry.precnode->precord; testOk(prec->val==1, "VAL %d==1 (initial value from .db)", (int)prec->val); + checkGetString(&entry, "1"); testOk1(dbPutString(&entry, "2")==0); testOk(prec->val==2, "VAL %d==2", (int)prec->val); + checkGetString(&entry, "2"); } else { testFail("Missing rec0"); - testSkip(1, "missing record"); + testSkip(4, "missing record"); } + testDiag("restore string pass0"); + if(dbFindRecord(&entry, "rec0.DESC")==0) { + aoRecord *prec = entry.precnode->precord; + testOk1(strcmp(prec->desc, "foobar")==0); + checkGetString(&entry, "foobar"); + testOk1(dbPutString(&entry, "hello")==0); + testOk1(strcmp(prec->desc, "hello")==0); + checkGetString(&entry, "hello"); + } else { + testFail("Missing rec0"); + testSkip(4, "missing record"); + } + + if(dbFindRecord(&entry, "rec1.DESC")==0) { + aoRecord *prec = entry.precnode->precord; + testOk1(strcmp(prec->desc, "")==0); + checkGetString(&entry, ""); + testOk1(dbPutString(&entry, "world")==0); + testOk1(strcmp(prec->desc, "world")==0); + checkGetString(&entry, "world"); + } else { + testFail("Missing rec1"); + testSkip(4, "missing record"); + } + + testDiag("restore link pass0"); /* rec0.OUT is initially "rec0.DISV", set it to "rec0.SEVR" */ if(dbFindRecord(&entry, "rec0.OUT")==0) { aoRecord *prec = entry.precnode->precord; @@ -69,6 +109,12 @@ static void hookPass0(initHookState state) else testFail("Wrong link type: %d", (int)prec->out.type); + /* note that dbGetString() reads an empty string before links are initialized + * should probably be considered a bug, but has been the case for so long + * we call it a 'feature'. + */ + checkGetString(&entry, ""); + testOk1(dbPutString(&entry, "rec0.SEVR")==0); } else{ testFail("Missing rec0"); @@ -213,10 +259,10 @@ void testRestore(void) testIocShutdownOk(); - testdbCleanup(); - /* recSup doesn't cleanup after itself */ free(rec1->bptr); + + testdbCleanup(); } struct dset6 { From e75f44100e25501614f71080929a6ab45f871f68 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Jan 2016 20:58:58 -0500 Subject: [PATCH 169/204] dbAccess: dbGet wrap at capacity count not valid count Change the usage of rset::get_array_info in dbGet() in the case that offset>0 and no_elementstype == dbfl_type_rec) { field_type = paddr->field_type; - no_elements = paddr->no_elements; + max_elements = no_elements = paddr->no_elements; } else { field_type = pfl->field_type; - no_elements = pfl->no_elements; + max_elements = no_elements = pfl->no_elements; } if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) @@ -906,7 +906,7 @@ long dbGet(DBADDR *paddr, short dbrType, if (n <= 0) { ;/*do nothing*/ } else if (!pfl || pfl->type == dbfl_type_rec) { - status = convert(paddr, pbuf, n, no_elements, offset); + status = convert(paddr, pbuf, n, max_elements, offset); } else { DBADDR localAddr = *paddr; /* Structure copy */ @@ -917,7 +917,7 @@ long dbGet(DBADDR *paddr, short dbrType, localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; - status = convert(&localAddr, pbuf, n, no_elements, offset); + status = convert(&localAddr, pbuf, n, max_elements, offset); } } done: From 840da801fb30f021d43447301f7094544878a7f2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Jan 2016 20:58:58 -0500 Subject: [PATCH 170/204] std/filters: arr wrap on capacity not length --- src/std/filters/arr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/std/filters/arr.c b/src/std/filters/arr.c index b872cd623..9d09c9f4c 100644 --- a/src/std/filters/arr.c +++ b/src/std/filters/arr.c @@ -95,7 +95,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { long end = my->end; long nTarget = 0; long offset = 0; - long nSource = chan->addr.no_elements; + long nSource = chan->addr.no_elements, maxSource = nSource; /* Only array data */ if (pfl->type == dbfl_type_val) { @@ -126,7 +126,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { pfl->u.r.dtor = freeArray; pfl->u.r.pvt = my->arrayFreeList; offset = (offset + start) % chan->addr.no_elements; - dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr); + dbExtractArrayFromRec(&chan->addr, pdst, nTarget, maxSource, offset, my->incr); pfl->u.r.field = pdst; } } From 4cca4673ca7be2d5a30b2286908e001b5e2fd267 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Jan 2016 20:58:58 -0500 Subject: [PATCH 171/204] compress: copy some text from RRM wiki Still a lot remaining. No idea what to do about the images. --- src/std/rec/compressRecord.dbd.pod | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/std/rec/compressRecord.dbd.pod b/src/std/rec/compressRecord.dbd.pod index 14f1bd407..0d27492bf 100644 --- a/src/std/rec/compressRecord.dbd.pod +++ b/src/std/rec/compressRecord.dbd.pod @@ -9,19 +9,37 @@ =title Compress Record (compress) -... +The data compression record is used to collect and compress data from arrays. +When the INP field references a data array field, +it immediately compresses the entire array into an element of an array using one of several algorithms, +overwriting the previous element. If the INP field obtains its value from a scalar-value field, +the compression record will collect a new sample each time the record is processed +and add it to the compressed data array as a circular buffer. + +The INP link can also specify a constant; +however, if this is the case, the compression algorithms are ignored, +and the record support routines merely return after checking the FLNK field. =head2 Record-specific Menus =head3 Menu compressALG -The ALG field which uses this menu controls the compression algorithm used. - -... - =menu compressALG -... +The ALG field which uses this menu controls the compression algorithm used. + +There are six possible algorithms which can be specified as follows: + +* Circular Buffer +* Average +* N to 1 Low Value +* N to 1 High Value +* N to 1 Average +* N to 1 Median + +=menu bufferingALG + +Selects FIFO (First in first out) or LIFO (Last in first out) insertion order. =head2 Parameter Fields From d2e4fcbbacb8c9da789b0dfc978a86f536071e56 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Jan 2016 20:58:58 -0500 Subject: [PATCH 172/204] compressRecord: LIFO doesn't zero pad Change handling of OFF so that reading a partially populated buffer doesn't pad with zero. Simplify put_value() and get_array_info() --- src/std/rec/compressRecord.c | 103 ++++++++++++++--------------------- 1 file changed, 40 insertions(+), 63 deletions(-) diff --git a/src/std/rec/compressRecord.c b/src/std/rec/compressRecord.c index c0feaacfb..c86b31af5 100644 --- a/src/std/rec/compressRecord.c +++ b/src/std/rec/compressRecord.c @@ -108,67 +108,34 @@ static void monitor(compressRecord *prec) db_post_events(prec, prec->bptr, monitor_mask); } -static void put_value_FIFO(compressRecord *prec, double *psource, int n) -{ - epicsInt32 offset = prec->off; - epicsInt32 nuse = prec->nuse; - epicsInt32 nsam = prec->nsam; - double *pdest = prec->bptr + offset; - int i; - - for (i = 0; i < n; i++) { - *pdest = *psource++; - if (++offset >= nsam) { - pdest = prec->bptr; - offset = 0; - } - else - pdest++; - } - nuse += n; - if (nuse > nsam) - nuse = nsam; - prec->off = offset; - prec->nuse = nuse; - return; -} - -static void put_value_LIFO(compressRecord *prec, double *psource, int n) -{ - epicsInt32 offset = prec->off; - epicsInt32 nuse = prec->nuse; - epicsInt32 nsam = prec->nsam; - epicsInt32 start = offset - nuse; - double *pdest; - int i; - if(start<0) start += nsam; - - for(i = 0; i < n; i++) { - if(--start < 0) start += nsam; - pdest = prec->bptr + start; - *pdest = *psource++; - } - nuse += n; - if(nuse > nsam) nuse = nsam; - offset = start + nuse; - if(offset>=nsam) offset -= nsam; - - prec->off = offset; - prec->nuse = nuse; - - return; -} - static void put_value(compressRecord *prec, double *psource, int n) { - switch(prec->balg) { - case bufferingALG_FIFO: put_value_FIFO(prec, psource, n); break; - case bufferingALG_LIFO: put_value_LIFO(prec, psource, n); break; + int fifo = prec->balg==bufferingALG_FIFO; + epicsUInt32 offset = prec->off; + epicsUInt32 nuse = prec->nuse; + epicsUInt32 nsam = prec->nsam; + + nuse += n; + if(nuse>nsam) + nuse = nsam; + + while(n--) { + /* for LIFO, decrement before */ + if(!fifo) + offset = (offset-1)%nsam; + + prec->bptr[offset] = *psource++; + + /* for FIFO, increment after */ + if(fifo) + offset = (offset+1)%nsam; } - return; + + prec->off = offset; + prec->nuse = nuse; } - + /* qsort comparison function (for median calculation) */ static int compare(const void *arg1, const void *arg2) { @@ -434,13 +401,22 @@ static long cvt_dbaddr(DBADDR *paddr) static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { + /* offset indicates the next element which would be written. + * In FIFO mode offset-1 is the last valid element + * In LIFO mode offset is the first valid element + * (*offset) should be set to the index of the first valid element + */ compressRecord *prec = (compressRecord *) paddr->precord; - epicsInt32 start = prec->off - prec->nuse; - if(start<0) start += prec->nsam; - if(prec->balg == bufferingALG_FIFO) - *no_elements = prec->nuse; - else *no_elements = prec->nsam; - *offset = start; + epicsUInt32 off = prec->off; + epicsUInt32 nuse = prec->nuse; + epicsUInt32 nsam = prec->nsam; + + *no_elements = nuse; + if(prec->balg == bufferingALG_FIFO) { + *offset = (off-nuse)%nsam; + } else { + *offset = off; + } return 0; } @@ -449,7 +425,8 @@ static long put_array_info(DBADDR *paddr, long nNew) { compressRecord *prec = (compressRecord *) paddr->precord; - prec->off = (prec->off + nNew) % prec->nsam; + if(prec->balg == bufferingALG_FIFO) + prec->off = (prec->off + nNew) % prec->nsam; prec->nuse += nNew; if (prec->nuse > prec->nsam) prec->nuse = prec->nsam; From f814398d775e8f55076519b73ec1a995ff3ae555 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Jan 2016 20:58:58 -0500 Subject: [PATCH 173/204] std/rec/test: add compressTest test circular buffer mode for FIFO and LIFO --- src/std/rec/test/Makefile | 7 + src/std/rec/test/compressTest.c | 351 +++++++++++++++++++++++++ src/std/rec/test/compressTest.db | 7 + src/std/rec/test/epicsRunRecordTests.c | 3 + 4 files changed, 368 insertions(+) create mode 100644 src/std/rec/test/compressTest.c create mode 100644 src/std/rec/test/compressTest.db diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 5e7a8fb29..f5a60a751 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -38,6 +38,13 @@ testHarness_SRCS += linkRetargetLinkTest.c TESTFILES += ../linkRetargetLink.db TESTS += linkRetargetLinkTest +TESTPROD_HOST += compressTest +compressTest_SRCS += compressTest.c +compressTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += compressTest.c +TESTFILES += ../compressTest.db +TESTS += compressTest + TARGETS += $(COMMON_DIR)/asTestIoc.dbd asTestIoc_DBD += base.dbd asTestIoc_DBD += asTest.dbd diff --git a/src/std/rec/test/compressTest.c b/src/std/rec/test/compressTest.c new file mode 100644 index 000000000..e9085c68b --- /dev/null +++ b/src/std/rec/test/compressTest.c @@ -0,0 +1,351 @@ +/*************************************************************************\ +* Copyright (c) 2016 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "dbLock.h" +#include "errlog.h" +#include "dbAccess.h" +#include "epicsMath.h" + +#include "aiRecord.h" +#include "compressRecord.h" + +#define testDEq(A,B,D) testOk(fabs((A)-(B))<(D), #A " (%f) ~= " #B " (%f)", A, B) + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static +void checkArrD(const char *pv, long elen, double a, double b, double c, double d) +{ + double buf[4]; + double expect[4]; + long nReq = NELEMENTS(buf), i; + unsigned match; + DBADDR addr; + + expect[0] = a; + expect[1] = b; + expect[2] = c; + expect[3] = d; + + if(dbNameToAddr(pv, &addr)) + testAbort("Unknown PV '%s'", pv); + + if(dbGet(&addr, DBR_DOUBLE, buf, NULL, &nReq, NULL)) + testAbort("Failed to get '%s'", pv); + + match = elen==nReq; + for(i=0; i=0.01) + testDiag("[%ld] -> %f != %f", i, expect[i], buf[i]); + } +} + +static +void checkArrI(const char *pv, long elen, epicsInt32 a, epicsInt32 b, epicsInt32 c, epicsInt32 d) +{ + epicsInt32 buf[4]; + epicsInt32 expect[4]; + long nReq = NELEMENTS(buf), i; + unsigned match; + DBADDR addr; + + expect[0] = a; + expect[1] = b; + expect[2] = c; + expect[3] = d; + + if(dbNameToAddr(pv, &addr)) + testAbort("Unknown PV '%s'", pv); + + if(dbGet(&addr, DBR_LONG, buf, NULL, &nReq, NULL)) + testAbort("Failed to get '%s'", pv); + + match = elen==nReq; + for(i=0; i %d != %d", i, (int)expect[i], (int)buf[i]); + } +} + +static +void testFIFOCirc(void) +{ + aiRecord *vrec; + compressRecord *crec; + double *cbuf; + + testDiag("Test FIFO"); + + testdbPrepare(); + + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=FIFO Buffer,NSAM=4"); + + vrec = (aiRecord*)testdbRecordPtr("val"); + crec = (compressRecord*)testdbRecordPtr("comp"); + + eltc(0); + testIocInitOk(); + eltc(1); + + dbScanLock((dbCommon*)crec); + cbuf = crec->bptr; + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + + testDiag("Push 1.1"); + vrec->val = 1.1; + dbProcess((dbCommon*)crec); + + /* In FIFO mode the valid elements a cbuf[(off-nuse-1)%size] through cbuf[(off-1)%size] */ + testOk1(crec->off==1); + testOk1(crec->inx==0); + testOk1(crec->nuse==1); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 0.0, 0.1); + testDEq(cbuf[2], 0.0, 0.1); + testDEq(cbuf[3], 0.0, 0.1); + checkArrD("comp", 1, 1.1, 0, 0, 0); + + testDiag("Push 2.1"); + vrec->val = 2.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==2); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 0.0, 0.1); + testDEq(cbuf[3], 0.0, 0.1); + checkArrD("comp", 2, 1.1, 2.1, 0, 0); + + testDiag("Push 3.1"); + vrec->val = 3.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==3); + testOk1(crec->inx==0); + testOk1(crec->nuse==3); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 0.0, 0.1); + checkArrD("comp", 3, 1.1, 2.1, 3.1, 0); + + testDiag("Push 4.1"); + vrec->val = 4.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 4.1, 0.1); + checkArrD("comp", 4, 1.1, 2.1, 3.1, 4.1); + + testDiag("Push 5.1"); + vrec->val = 5.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==1); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 5.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 4.1, 0.1); + checkArrD("comp", 4, 2.1, 3.1, 4.1, 5.1); + + testDiag("Push 6.1"); + vrec->val = 6.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 5.1, 0.1); + testDEq(cbuf[1], 6.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 4.1, 0.1); + checkArrD("comp", 4, 3.1, 4.1, 5.1, 6.1); + + dbScanUnlock((dbCommon*)crec); + + testDiag("Reset"); + testdbPutFieldOk("comp.RES", DBF_LONG, 0); + + dbScanLock((dbCommon*)crec); + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + checkArrD("comp", 0, 0, 0, 0, 0); + dbScanUnlock((dbCommon*)crec); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static +void testLIFOCirc(void) +{ + aiRecord *vrec; + compressRecord *crec; + double *cbuf; + + testDiag("Test LIFO"); + + testdbPrepare(); + + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=LIFO Buffer,NSAM=4"); + + vrec = (aiRecord*)testdbRecordPtr("val"); + crec = (compressRecord*)testdbRecordPtr("comp"); + + eltc(0); + testIocInitOk(); + eltc(1); + + dbScanLock((dbCommon*)crec); + cbuf = crec->bptr; + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + + testDiag("Push 1.1"); + vrec->val = 1.1; + dbProcess((dbCommon*)crec); + + testDiag("off %u", crec->off); + testOk1(crec->off==3); + testOk1(crec->inx==0); + testOk1(crec->nuse==1); + testDEq(cbuf[0], 0.0, 0.1); + testDEq(cbuf[1], 0.0, 0.1); + testDEq(cbuf[2], 0.0, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 1, 1.1, 0, 0, 0); + + testDiag("Push 2.1"); + vrec->val = 2.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==2); + testDEq(cbuf[0], 0.0, 0.1); + testDEq(cbuf[1], 0.0, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 2, 2.1, 1.1, 0, 0); + checkArrI("comp", 2, 2, 1, 0, 0); + + testDiag("Push 3.1"); + vrec->val = 3.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==1); + testOk1(crec->inx==0); + testOk1(crec->nuse==3); + testDEq(cbuf[0], 0.0, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 3, 3.1, 2.1, 1.1, 0); + checkArrI("comp", 3, 3, 2, 1, 0); + + testDiag("Push 4.1"); + vrec->val = 4.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 4.1, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 4, 4.1, 3.1, 2.1, 1.1); + checkArrI("comp", 4, 4, 3, 2, 1); + + testDiag("Push 5.1"); + vrec->val = 5.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==3); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 4.1, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 5.1, 0.1); + checkArrD("comp", 4, 5.1, 4.1, 3.1, 2.1); + checkArrI("comp", 4, 5, 4, 3, 2); + + testDiag("Push 6.1"); + vrec->val = 6.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 4.1, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 6.1, 0.1); + testDEq(cbuf[3], 5.1, 0.1); + checkArrD("comp", 4, 6.1, 5.1, 4.1, 3.1); + + dbScanUnlock((dbCommon*)crec); + + testDiag("Reset"); + testdbPutFieldOk("comp.RES", DBF_LONG, 0); + + dbScanLock((dbCommon*)crec); + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + checkArrD("comp", 0, 0, 0, 0, 0); + dbScanUnlock((dbCommon*)crec); + + testIocShutdownOk(); + + testdbCleanup(); +} + +MAIN(compressTest) +{ + testPlan(116); + testFIFOCirc(); + testLIFOCirc(); + return testDone(); +} diff --git a/src/std/rec/test/compressTest.db b/src/std/rec/test/compressTest.db new file mode 100644 index 000000000..59fc620ba --- /dev/null +++ b/src/std/rec/test/compressTest.db @@ -0,0 +1,7 @@ +record(ai, "val") {} +record(compress, "comp") { + field(INP, "val NPP") + field(ALG, "$(ALG)") + field(BALG,"$(BALG)") + field(NSAM,"$(NSAM)") +} diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c index 3476d2138..5612917cc 100644 --- a/src/std/rec/test/epicsRunRecordTests.c +++ b/src/std/rec/test/epicsRunRecordTests.c @@ -13,6 +13,7 @@ #include "epicsExit.h" int analogMonitorTest(void); +int compressTest(void); int arrayOpTest(void); int asTest(void); int linkRetargetLinkTest(void); @@ -23,6 +24,8 @@ void epicsRunRecordTests(void) runTest(analogMonitorTest); + runTest(compressTest); + runTest(arrayOpTest); runTest(asTest); From 61435206805a1b9fc6ef0932b46188b47dc78110 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 13 Jan 2016 21:46:00 -0500 Subject: [PATCH 174/204] update release notes --- documentation/RELEASE_NOTES.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 8ae4e76d3..6f258ed8b 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,12 @@ --> +

compressRecord buffering order

+ +

The compressRecord has a new field BALG which can select between +FIFO (append) and LIFO (prepend) ordering for insertion of new elements. +FIFO ordering is the default.

+

Database Multi-locking

dbLock.c is re-written with an expanded API, and the removal of global mutex locks.

From 445c0ada8c80d6d0ed9aedf35b4f450740b2587e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 9 Feb 2016 14:10:51 -0600 Subject: [PATCH 175/204] Fix Windows problems from Valgrind merge. --- src/libCom/freeList/freeListLib.c | 2 +- src/libCom/valgrind/valgrind.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 src/libCom/freeList/freeListLib.c mode change 100644 => 100755 src/libCom/valgrind/valgrind.h diff --git a/src/libCom/freeList/freeListLib.c b/src/libCom/freeList/freeListLib.c old mode 100644 new mode 100755 index a0ae73837..b8e8e6f23 --- a/src/libCom/freeList/freeListLib.c +++ b/src/libCom/freeList/freeListLib.c @@ -107,7 +107,7 @@ epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) return(0); } pallocmem->memory = ptemp; /* real allocation */ - ptemp += REDZONE; /* skip first REDZONE */ + ptemp = REDZONE + (char *) ptemp; /* skip first REDZONE */ if(pfl->mallochead) pallocmem->next = pfl->mallochead; pfl->mallochead = pallocmem; diff --git a/src/libCom/valgrind/valgrind.h b/src/libCom/valgrind/valgrind.h old mode 100644 new mode 100755 index 6954d751d..c503172d5 --- a/src/libCom/valgrind/valgrind.h +++ b/src/libCom/valgrind/valgrind.h @@ -130,10 +130,10 @@ # define PLAT_amd64_darwin 1 #elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ || defined(__CYGWIN32__) \ - || (defined(_WIN32) && defined(_M_IX86)) + || (defined(_WIN32) && defined(_M_IX86) && defined(__GNUC__)) # define PLAT_x86_win32 1 #elif defined(__MINGW64__) \ - || (defined(_WIN64) && defined(_M_X64)) + || (defined(_WIN64) && defined(_M_X64) && defined(__GNUC__)) # define PLAT_amd64_win64 1 #elif defined(__linux__) && defined(__i386__) # define PLAT_x86_linux 1 From 6376ae8b1ae62d6cbda86a84a6599662a53abfb8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 9 Feb 2016 18:31:17 -0600 Subject: [PATCH 176/204] Fixed some issues with indentation --- src/ioc/dbStatic/dbStaticLib.c | 196 ++++++++++++++++----------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 1039da9b5..3d62cf7f3 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1871,7 +1871,7 @@ char * dbGetString(DBENTRY *pdbentry) void *pfield = pdbentry->pfield; DBLINK *plink; - if(!pflddes) { + if (!pflddes) { dbMsgCpy(pdbentry, "fldDes not found"); return pdbentry->message; } @@ -1880,7 +1880,7 @@ char * dbGetString(DBENTRY *pdbentry) case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: - if(!pfield) { + if (!pfield) { dbMsgCpy(pdbentry, "Field not allocated (NULL)"); return pdbentry->message; } @@ -1891,8 +1891,8 @@ char * dbGetString(DBENTRY *pdbentry) switch (pflddes->field_type) { case DBF_STRING: - dbMsgCpy(pdbentry, (char *)pfield); - break; + dbMsgCpy(pdbentry, (char *)pfield); + break; case DBF_CHAR: case DBF_UCHAR: case DBF_SHORT: @@ -1909,99 +1909,99 @@ char * dbGetString(DBENTRY *pdbentry) case DBF_OUTLINK: plink = (DBLINK *)pfield; switch(plink->type) { - case CONSTANT: - if(plink->value.constantStr) { - dbMsgCpy(pdbentry, plink->value.constantStr); - } else { - dbMsgCpy(pdbentry, ""); - } - break; - case MACRO_LINK: - if(plink->value.macro_link.macroStr) { - dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); - } else { - dbMsgCpy(pdbentry, ""); - } - break; + case CONSTANT: + if (plink->value.constantStr) { + dbMsgCpy(pdbentry, plink->value.constantStr); + } else { + dbMsgCpy(pdbentry, ""); + } + break; + case MACRO_LINK: + if (plink->value.macro_link.macroStr) { + dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); + } else { + dbMsgCpy(pdbentry, ""); + } + break; case PN_LINK: - dbMsgPrint(pdbentry, "%s%s", + dbMsgPrint(pdbentry, "%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); - break; - case PV_LINK: - case CA_LINK: - case DB_LINK: { - int ppind; - short pvlMask; + break; + case PV_LINK: + case CA_LINK: + case DB_LINK: { + int ppind; + short pvlMask; - pvlMask = plink->value.pv_link.pvlMask; - if(pvlMask&pvlOptPP) ppind=1; - else if(pvlMask&pvlOptCA) ppind=2; - else if(pvlMask&pvlOptCP) ppind=3; - else if(pvlMask&pvlOptCPP) ppind=4; - else ppind=0; - dbMsgPrint(pdbentry, "%s%s%s%s", + pvlMask = plink->value.pv_link.pvlMask; + if (pvlMask&pvlOptPP) ppind=1; + else if(pvlMask&pvlOptCA) ppind=2; + else if(pvlMask&pvlOptCP) ppind=3; + else if(pvlMask&pvlOptCPP) ppind=4; + else ppind=0; + dbMsgPrint(pdbentry, "%s%s%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", (pvlMask & pvlOptTSELisTime) ? ".TIME" : "", ppstring[ppind], msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); - break; - } - case VME_IO: - dbMsgPrint(pdbentry, "#C%d S%d @%s", - plink->value.vmeio.card,plink->value.vmeio.signal, - plink->value.vmeio.parm); - break; - case CAMAC_IO: - dbMsgPrint(pdbentry, "#B%d C%d N%d A%d F%d @%s", - plink->value.camacio.b,plink->value.camacio.c, - plink->value.camacio.n,plink->value.camacio.a, - plink->value.camacio.f,plink->value.camacio.parm); - break; - case RF_IO: - dbMsgPrint(pdbentry, "#R%d M%d D%d E%d", - plink->value.rfio.cryo, - plink->value.rfio.micro, - plink->value.rfio.dataset, - plink->value.rfio.element); - break; - case AB_IO: - dbMsgPrint(pdbentry, "#L%d A%d C%d S%d @%s", - plink->value.abio.link,plink->value.abio.adapter, - plink->value.abio.card,plink->value.abio.signal, - plink->value.abio.parm); - break; - case GPIB_IO: - dbMsgPrint(pdbentry, "#L%d A%d @%s", - plink->value.gpibio.link,plink->value.gpibio.addr, - plink->value.gpibio.parm); - break; - case BITBUS_IO: - dbMsgPrint(pdbentry, "#L%u N%u P%u S%u @%s", - plink->value.bitbusio.link,plink->value.bitbusio.node, - plink->value.bitbusio.port,plink->value.bitbusio.signal, - plink->value.bitbusio.parm); - break; - case BBGPIB_IO: - dbMsgPrint(pdbentry, "#L%u B%u G%u @%s", - plink->value.bbgpibio.link,plink->value.bbgpibio.bbaddr, - plink->value.bbgpibio.gpibaddr,plink->value.bbgpibio.parm); - break; - case INST_IO: - dbMsgPrint(pdbentry, "@%s", plink->value.instio.string); - break; - case VXI_IO : - if (plink->value.vxiio.flag == VXIDYNAMIC) - dbMsgPrint(pdbentry, "#V%d C%d S%d @%s", - plink->value.vxiio.frame,plink->value.vxiio.slot, - plink->value.vxiio.signal,plink->value.vxiio.parm); - else - dbMsgPrint(pdbentry, "#V%d S%d @%s", - plink->value.vxiio.la,plink->value.vxiio.signal, - plink->value.vxiio.parm); - break; - default : - return(NULL); + break; + } + case VME_IO: + dbMsgPrint(pdbentry, "#C%d S%d @%s", + plink->value.vmeio.card,plink->value.vmeio.signal, + plink->value.vmeio.parm); + break; + case CAMAC_IO: + dbMsgPrint(pdbentry, "#B%d C%d N%d A%d F%d @%s", + plink->value.camacio.b,plink->value.camacio.c, + plink->value.camacio.n,plink->value.camacio.a, + plink->value.camacio.f,plink->value.camacio.parm); + break; + case RF_IO: + dbMsgPrint(pdbentry, "#R%d M%d D%d E%d", + plink->value.rfio.cryo, + plink->value.rfio.micro, + plink->value.rfio.dataset, + plink->value.rfio.element); + break; + case AB_IO: + dbMsgPrint(pdbentry, "#L%d A%d C%d S%d @%s", + plink->value.abio.link,plink->value.abio.adapter, + plink->value.abio.card,plink->value.abio.signal, + plink->value.abio.parm); + break; + case GPIB_IO: + dbMsgPrint(pdbentry, "#L%d A%d @%s", + plink->value.gpibio.link,plink->value.gpibio.addr, + plink->value.gpibio.parm); + break; + case BITBUS_IO: + dbMsgPrint(pdbentry, "#L%u N%u P%u S%u @%s", + plink->value.bitbusio.link,plink->value.bitbusio.node, + plink->value.bitbusio.port,plink->value.bitbusio.signal, + plink->value.bitbusio.parm); + break; + case BBGPIB_IO: + dbMsgPrint(pdbentry, "#L%u B%u G%u @%s", + plink->value.bbgpibio.link,plink->value.bbgpibio.bbaddr, + plink->value.bbgpibio.gpibaddr,plink->value.bbgpibio.parm); + break; + case INST_IO: + dbMsgPrint(pdbentry, "@%s", plink->value.instio.string); + break; + case VXI_IO : + if (plink->value.vxiio.flag == VXIDYNAMIC) + dbMsgPrint(pdbentry, "#V%d C%d S%d @%s", + plink->value.vxiio.frame,plink->value.vxiio.slot, + plink->value.vxiio.signal,plink->value.vxiio.parm); + else + dbMsgPrint(pdbentry, "#V%d S%d @%s", + plink->value.vxiio.la,plink->value.vxiio.signal, + plink->value.vxiio.parm); + break; + default : + return(NULL); } break; case DBF_FWDLINK: { @@ -2012,10 +2012,10 @@ char * dbGetString(DBENTRY *pdbentry) dbMsgCpy(pdbentry, "0"); break; case MACRO_LINK: - if(plink->value.macro_link.macroStr) { - dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); + if (plink->value.macro_link.macroStr) { + dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); } else { - dbMsgCpy(pdbentry, ""); + dbMsgCpy(pdbentry, ""); } break; case PV_LINK: @@ -2025,11 +2025,11 @@ char * dbGetString(DBENTRY *pdbentry) short pvlMask; pvlMask = plink->value.pv_link.pvlMask; - if(pvlMask&pvlOptCA) ppind=2; + if (pvlMask&pvlOptCA) ppind=2; else ppind=0; - dbMsgPrint(pdbentry, "%s%s", - plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", - ppind ? ppstring[ppind] : ""); + dbMsgPrint(pdbentry, "%s%s", + plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", + ppind ? ppstring[ppind] : ""); break; } default : @@ -3505,8 +3505,8 @@ void dbReportDeviceConfig(dbBase *pdbbase,FILE *report) plink = pdbentry->pfield; linkType = plink->type; if(bus[linkType][0]==0) continue; - strncpy(linkValue,dbGetString(pdbentry), NELEMENTS(linkValue)-1); - linkValue[NELEMENTS(linkValue)-1] = '\0'; + strncpy(linkValue, dbGetString(pdbentry), NELEMENTS(linkValue)-1); + linkValue[NELEMENTS(linkValue)-1] = '\0'; status = dbFindField(pdbentry,"DTYP"); if(status) break; strcpy(dtypValue,dbGetString(pdbentry)); From ce500bc98c999bc88fb585c8df0cb894db425dee Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 10 Feb 2016 14:03:52 -0600 Subject: [PATCH 177/204] More indentation and other trivial cleanup --- src/ioc/dbStatic/dbStaticLib.c | 397 ++++++++++++++++----------------- 1 file changed, 198 insertions(+), 199 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 3d62cf7f3..2240e1bc3 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -86,140 +86,9 @@ static int mapDBFtoDCT[DBF_NOACCESS+1] = { DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK, DCT_NOACCESS}; -static char hex_digit_to_ascii[16]={'0','1','2','3','4','5','6','7','8','9', - 'a','b','c','d','e','f'}; - -static void ulongToHexString(epicsUInt32 source,char *pdest) -{ - epicsUInt32 val,temp; - char digit[10]; - int i,j; - - if(source==0) { - strcpy(pdest,"0x0"); - return; - } - *pdest++ = '0'; *pdest++ = 'x'; - val = source; - for(i=0; val!=0; i++) { - temp = val/16; - digit[i] = hex_digit_to_ascii[val - temp*16]; - val = temp; - } - for(j=i-1; j>=0; j--) { - *pdest++ = digit[j]; - } - *pdest = 0; - return; -} - -static double delta[2] = {1e-6, 1e-15}; -static int precision[2] = {6, 14}; -static void realToString(double value, char *preturn, int isdouble) -{ - double absvalue; - int logval,prec; - size_t end; - char tstr[30]; - char *ptstr = &tstr[0]; - int round; - int ise = FALSE; - char *loce = NULL; - - if (value == 0) { - strcpy(preturn, "0"); - return; - } - - absvalue = value < 0 ? -value : value; - if (absvalue < (double)INT_MAX) { - epicsInt32 intval = (epicsInt32) value; - double diff = value - intval; - - if (diff < 0) diff = -diff; - if (diff < absvalue * delta[isdouble]) { - cvtLongToString(intval, preturn); - return; - } - } - - /*Now starts the hard cases*/ - if (value < 0) { - *preturn++ = '-'; - value = -value; - } - - logval = (int)log10(value); - if (logval > 6 || logval < -2) { - int nout; - - ise = TRUE; - prec = precision[isdouble]; - nout = sprintf(ptstr, "%.*e", prec, value); - loce = strchr(ptstr, 'e'); - - if (!loce) { - ptstr[nout] = 0; - strcpy(preturn, ptstr); - return; - } - - *loce++ = 0; - } else { - prec = precision[isdouble] - logval; - if ( prec < 0) prec = 0; - sprintf(ptstr, "%.*f", prec, value); - } - - if (prec > 0) { - end = strlen(ptstr) - 1; - round = FALSE; - while (end > 0) { - if (tstr[end] == '.') {end--; break;} - if (tstr[end] == '0') {end--; continue;} - if (!round && end < precision[isdouble]) break; - if (!round && tstr[end] < '8') break; - if (tstr[end-1] == '.') { - if (round) end = end-2; - break; - } - if (tstr[end-1] != '9') break; - round = TRUE; - end--; - } - tstr[end+1] = 0; - while (round) { - if (tstr[end] < '9') {tstr[end]++; break;} - if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;} - tstr[end--] = '0'; - } - } - strcpy(preturn, &tstr[0]); - if (ise) { - if (!(strchr(preturn, '.'))) strcat(preturn, ".0"); - strcat(preturn, "e"); - strcat(preturn, loce); - } -} - -static void floatToString(float value,char *preturn) -{ - realToString((double)value,preturn,0); - return; -} - -static void doubleToString(double value,char *preturn) -{ - realToString(value,preturn,1); - return; -} - /*forward references for private routines*/ -static FILE *openOutstream(const char *filename); -static void finishOutstream(FILE *stream); -static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess); -static void zeroDbentry(DBENTRY *pdbentry); -static char *getpMessage(DBENTRY *pdbentry); +static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) + EPICS_PRINTF_STYLE(2,3); static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length); /* internal routines*/ @@ -349,9 +218,6 @@ void dbMsgCpy(DBENTRY *pdbentry, const char *msg) pdbentry->message[messagesize-1] = '\0'; } -static -void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3); - static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) { @@ -362,6 +228,130 @@ void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) va_end(args); } +static void ulongToHexString(epicsUInt32 source,char *pdest) +{ + static const char hex_digit_to_ascii[16] = "0123456789abcdef"; + epicsUInt32 val,temp; + char digit[10]; + int i,j; + + if (source==0) { + strcpy(pdest,"0x0"); + return; + } + *pdest++ = '0'; *pdest++ = 'x'; + val = source; + for (i=0; val!=0; i++) { + temp = val/16; + digit[i] = hex_digit_to_ascii[val - temp*16]; + val = temp; + } + for (j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return; +} + +static void realToString(double value, char *preturn, int isdouble) +{ + static const double delta[2] = {1e-6, 1e-15}; + static const int precision[2] = {6, 14}; + double absvalue; + int logval,prec; + size_t end; + char tstr[30]; + char *ptstr = &tstr[0]; + int round; + int ise = FALSE; + char *loce = NULL; + + if (value == 0) { + strcpy(preturn, "0"); + return; + } + + absvalue = value < 0 ? -value : value; + if (absvalue < (double)INT_MAX) { + epicsInt32 intval = (epicsInt32) value; + double diff = value - intval; + + if (diff < 0) diff = -diff; + if (diff < absvalue * delta[isdouble]) { + cvtLongToString(intval, preturn); + return; + } + } + + /*Now starts the hard cases*/ + if (value < 0) { + *preturn++ = '-'; + value = -value; + } + + logval = (int)log10(value); + if (logval > 6 || logval < -2) { + int nout; + + ise = TRUE; + prec = precision[isdouble]; + nout = sprintf(ptstr, "%.*e", prec, value); + loce = strchr(ptstr, 'e'); + + if (!loce) { + ptstr[nout] = 0; + strcpy(preturn, ptstr); + return; + } + + *loce++ = 0; + } else { + prec = precision[isdouble] - logval; + if ( prec < 0) prec = 0; + sprintf(ptstr, "%.*f", prec, value); + } + + if (prec > 0) { + end = strlen(ptstr) - 1; + round = FALSE; + while (end > 0) { + if (tstr[end] == '.') {end--; break;} + if (tstr[end] == '0') {end--; continue;} + if (!round && end < precision[isdouble]) break; + if (!round && tstr[end] < '8') break; + if (tstr[end-1] == '.') { + if (round) end = end-2; + break; + } + if (tstr[end-1] != '9') break; + round = TRUE; + end--; + } + tstr[end+1] = 0; + while (round) { + if (tstr[end] < '9') {tstr[end]++; break;} + if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;} + tstr[end--] = '0'; + } + } + strcpy(preturn, &tstr[0]); + if (ise) { + if (!(strchr(preturn, '.'))) strcat(preturn, ".0"); + strcat(preturn, "e"); + strcat(preturn, loce); + } +} + +static void floatToString(float value, char *preturn) +{ + realToString((double)value, preturn, 0); +} + +static void doubleToString(double value, char *preturn) +{ + realToString(value, preturn, 1); +} + /*Public only for dbStaticNoRun*/ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) { @@ -2045,7 +2035,7 @@ char * dbGetString(DBENTRY *pdbentry) char *dbGetStringNum(DBENTRY *pdbentry) { - dbFldDes *pflddes = pdbentry->pflddes; + dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; char *message; unsigned char cvttype; @@ -2057,80 +2047,89 @@ char *dbGetStringNum(DBENTRY *pdbentry) cvttype = pflddes->base; switch (pflddes->field_type) { case DBF_CHAR: - if(cvttype==CT_DECIMAL) - cvtCharToString(*(char*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(char*)pfield),message); - break; + if (cvttype==CT_DECIMAL) + cvtCharToString(*(char*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(char*)pfield),message); + break; case DBF_UCHAR: - if(cvttype==CT_DECIMAL) - cvtUcharToString(*(unsigned char*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(unsigned char*)pfield),message); - break; + if (cvttype==CT_DECIMAL) + cvtUcharToString(*(unsigned char*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(unsigned char*)pfield),message); + break; case DBF_SHORT: - if(cvttype==CT_DECIMAL) - cvtShortToString(*(short*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(short*)pfield),message); - break; + if (cvttype==CT_DECIMAL) + cvtShortToString(*(short*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(short*)pfield),message); + break; case DBF_USHORT: case DBF_ENUM: - if(cvttype==CT_DECIMAL) - cvtUshortToString(*(unsigned short*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(unsigned short*)pfield),message); - break; + if (cvttype==CT_DECIMAL) + cvtUshortToString(*(unsigned short*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(unsigned short*)pfield),message); + break; case DBF_LONG: - if(cvttype==CT_DECIMAL) - cvtLongToString(*(epicsInt32*)pfield, message); - else - ulongToHexString((epicsUInt32)(*(epicsInt32*)pfield), message); - break; + if (cvttype==CT_DECIMAL) + cvtLongToString(*(epicsInt32*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(epicsInt32*)pfield), message); + break; case DBF_ULONG: - if(cvttype==CT_DECIMAL) - cvtUlongToString(*(epicsUInt32 *)pfield, message); - else - ulongToHexString(*(epicsUInt32*)pfield, message); - break; + if (cvttype==CT_DECIMAL) + cvtUlongToString(*(epicsUInt32 *)pfield, message); + else + ulongToHexString(*(epicsUInt32*)pfield, message); + break; case DBF_FLOAT: - floatToString(*(float *)pfield,message); - break; + floatToString(*(float *)pfield,message); + break; case DBF_DOUBLE: - doubleToString(*(double *)pfield,message); - break; - case DBF_MENU: { - dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; - short choice_ind; - char *pchoice; + doubleToString(*(double *)pfield,message); + break; + case DBF_MENU: + { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + short choice_ind; + char *pchoice; - if(!pfield) {dbMsgCpy(pdbentry, "Field not found"); return(message);} - choice_ind = *((short *) pdbentry->pfield); - if(!pdbMenu || choice_ind<0 || choice_ind>=pdbMenu->nChoice) - return(NULL); - pchoice = pdbMenu->papChoiceValue[choice_ind]; - dbMsgCpy(pdbentry, pchoice); - } - break; - case DBF_DEVICE: { - dbDeviceMenu *pdbDeviceMenu; - char *pchoice; - short choice_ind; + if (!pfield) { + dbMsgCpy(pdbentry, "Field not found"); + return message; + } + choice_ind = *((short *) pdbentry->pfield); + if (!pdbMenu || choice_ind<0 || choice_ind>=pdbMenu->nChoice) + return NULL; + pchoice = pdbMenu->papChoiceValue[choice_ind]; + dbMsgCpy(pdbentry, pchoice); + } + break; + case DBF_DEVICE: + { + dbDeviceMenu *pdbDeviceMenu; + char *pchoice; + short choice_ind; - if(!pfield) {dbMsgCpy(pdbentry, "Field not found"); return(message);} - pdbDeviceMenu = dbGetDeviceMenu(pdbentry); - if(!pdbDeviceMenu) return(NULL); - choice_ind = *((short *) pdbentry->pfield); - if(choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice) - return(NULL); - pchoice = pdbDeviceMenu->papChoice[choice_ind]; - dbMsgCpy(pdbentry, pchoice); - } - break; + if (!pfield) { + dbMsgCpy(pdbentry, "Field not found"); + return message; + } + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if (!pdbDeviceMenu) + return NULL; + choice_ind = *((short *) pdbentry->pfield); + if (choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice) + return NULL; + pchoice = pdbDeviceMenu->papChoice[choice_ind]; + dbMsgCpy(pdbentry, pchoice); + } + break; default: - return(NULL); + return NULL; } - return (message); + return message; } long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) From f528f347cdd971b66d1a6351bfd389d15a8b23f3 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 12 Feb 2016 11:33:54 -0600 Subject: [PATCH 178/204] Increase testMonitorWait() timeout --- src/ioc/db/dbUnitTest.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index 8e23ab691..07e12e739 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -287,13 +287,15 @@ void testMonitorDestroy(testMonitor *mon) void testMonitorWait(testMonitor *mon) { - switch(epicsEventWaitWithTimeout(mon->event, 10.0)) + static const double delay = 60.0; + + switch(epicsEventWaitWithTimeout(mon->event, delay)) { case epicsEventOK: return; case epicsEventWaitTimeout: default: - testAbort("testMonitorWait() exceeds timeout"); + testAbort("testMonitorWait() exceeded %g second timeout", delay); } } From 18d863b91801f08b031717d7c7ccf89cd9329ddd Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Feb 2016 12:06:10 -0500 Subject: [PATCH 179/204] db/test: missing/wrong .db file names --- src/ioc/db/test/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 53d7523a4..956ce616c 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -52,7 +52,7 @@ dbPutLinkTest_SRCS += dbPutLinkTest.c dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbPutLinkTest.c TESTS += dbPutLinkTest -TESTFILES += ../dbPutLinkTest.db +TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db TESTPROD_HOST += dbLockTest dbLockTest_SRCS += dbLockTest.c @@ -67,6 +67,7 @@ dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp dbStressTest_SYS_LIBS_solaris += rt dbStressTest_SYS_LIBS_Linux += rt TESTS += dbStressTest +TESTFILES += ../dbStressLock.db TESTPROD_HOST += testdbConvert testdbConvert_SRCS += testdbConvert.c @@ -93,7 +94,7 @@ dbCaStatsTest_SRCS += dbCaStatsTest.c dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbCaStatsTest.c TESTS += dbCaStatsTest -TESTFILES += ../dbCaStatsTest.db +TESTFILES += ../dbCaStats.db TESTPROD_HOST += dbCaLinkTest dbCaLinkTest_SRCS += dbCaLinkTest.c From 48da96cce56b2ef1eb2b78ebfca8cce34914cdd7 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Feb 2016 12:29:06 -0500 Subject: [PATCH 180/204] libCom/test: epicsMMIOTest missing test spec --- src/libCom/test/epicsMMIOTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/test/epicsMMIOTest.c b/src/libCom/test/epicsMMIOTest.c index 0bf13fbd2..939627348 100644 --- a/src/libCom/test/epicsMMIOTest.c +++ b/src/libCom/test/epicsMMIOTest.c @@ -47,7 +47,7 @@ MAIN(epicsMMIOTest) STATIC_ASSERT(sizeof(H16)==2); STATIC_ASSERT(sizeof(H32)==4); - testPlan(0); + testPlan(14); testDiag("8-bit ops"); From 0575d3764fa4c7f56252bc8e37a6589ac9afdc89 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Feb 2016 12:29:06 -0500 Subject: [PATCH 181/204] libCom/test: fixup RTEMS tests to run clean --- src/libCom/test/epicsStdioTest.c | 6 ++++++ src/libCom/test/epicsStdlibTest.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/libCom/test/epicsStdioTest.c b/src/libCom/test/epicsStdioTest.c index d649aa061..c2d7eb58a 100644 --- a/src/libCom/test/epicsStdioTest.c +++ b/src/libCom/test/epicsStdioTest.c @@ -127,6 +127,12 @@ MAIN(epicsStdioTest) testPlan(163); #endif testEpicsSnprintf(); +#ifdef __rtems__ + /* ensure there is a writeable area */ + mkdir( "/tmp", S_IRWXU ); + testStdoutRedir("/tmp/report"); +#else testStdoutRedir("report"); +#endif return testDone(); } diff --git a/src/libCom/test/epicsStdlibTest.c b/src/libCom/test/epicsStdlibTest.c index a55453ad1..d82f834b8 100644 --- a/src/libCom/test/epicsStdlibTest.c +++ b/src/libCom/test/epicsStdlibTest.c @@ -421,8 +421,14 @@ MAIN(epicsStdlibTest) testOk(epicsScanDouble("NAN", &d) && isnan(d), "Double 'NAN'"); testOk(epicsScanFloat("Nan", &f) && isnan(f), "Float 'Nan'"); testOk(epicsScanDouble("Nan", &d) && isnan(d), "Double 'Nan'"); +#ifdef __rtems__ + testTodoBegin("RTEMS (newlib) parser doesn't recognise 'nan()'"); +#endif testOk(epicsScanFloat("nan()", &f) && isnan(f), "Float 'nan()'"); testOk(epicsScanDouble("nan()", &d) && isnan(d), "Double 'nan()'"); +#ifdef __rtems__ + testTodoEnd(); +#endif testOk(epicsScanFloat("INF", &f) && f == epicsINF, "Float 'INF'"); From 45a741ac6f0d819782409a2f8a848980c7ba2990 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Feb 2016 12:29:06 -0500 Subject: [PATCH 182/204] libCom/RTEMS: ensure lookup of localhost on RTEMS create /etc/hosts if it doesn't already exist --- src/libCom/RTEMS/rtems_init.c | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/libCom/RTEMS/rtems_init.c b/src/libCom/RTEMS/rtems_init.c index e89006b51..f2d83a7ab 100644 --- a/src/libCom/RTEMS/rtems_init.c +++ b/src/libCom/RTEMS/rtems_init.c @@ -18,8 +18,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -335,6 +337,46 @@ initialize_remote_filesystem(char **argv, int hasLocalFilesystem) #endif } +static +char rtems_etc_hosts[] = "127.0.0.1 localhost\n"; + +/* If it doesn't already exist, create /etc/hosts with an entry for 'localhost' */ +static +void fixup_hosts(void) +{ + FILE *fp; + int ret; + struct stat STAT; + + ret=stat("/etc/hosts", &STAT); + if(ret==0) + { + return; /* already exists, assume file */ + } else if(errno!=ENOENT) { + perror("error: fixup_hosts stat /etc/hosts"); + return; + } + + ret = mkdir("/etc", 0775); + if(ret!=0 && errno!=EEXIST) + { + perror("error: fixup_hosts create /etc"); + return; + } + + if((fp=fopen("/etc/hosts", "w"))==NULL) + { + perror("error: fixup_hosts create /etc/hosts"); + } + + if(fwrite(rtems_etc_hosts, 1, sizeof(rtems_etc_hosts)-1, fp)!=sizeof(rtems_etc_hosts)-1) + { + perror("error: failed to write /etc/hosts"); + } + + fclose(fp); +} + /* * Get to the startup script directory * The TFTP filesystem requires a trailing '/' on chdir arguments. @@ -546,6 +588,7 @@ Init (rtems_task_argument ignored) printf("\n***** Initializing network *****\n"); rtems_bsdnet_initialize_network(); initialize_remote_filesystem(argv, initialize_local_filesystem(argv)); + fixup_hosts(); /* * More environment: iocsh prompt and hostname From 84880e876ddfb26ea41b791fd0112af22fd05d46 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Feb 2016 12:29:22 -0500 Subject: [PATCH 183/204] dbUnitTest: show PWD when database load fails --- src/ioc/db/dbUnitTest.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index 07e12e739..d0d5906c8 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -15,6 +15,7 @@ #include "dbmf.h" #include "epicsUnitTest.h" #include "osiFileName.h" +#include "osiUnistd.h" #include "registry.h" #include "epicsEvent.h" @@ -52,9 +53,14 @@ void testdbReadDatabase(const char* file, if(!path) path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common"; - if(dbReadDatabase(&pdbbase, file, path, substitutions)) - testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)", - file, path, substitutions); + if(dbReadDatabase(&pdbbase, file, path, substitutions)) { + char buf[100]; + const char *cwd = getcwd(buf, sizeof(buf)); + if(!cwd) + cwd = ""; + testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)\n from: \"%s\"", + file, path, substitutions, cwd); + } } void testIocInitOk(void) From 969b1030b26859880ba3a1eaff6f43779aae78f1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Feb 2016 12:29:22 -0500 Subject: [PATCH 184/204] RTEMS: show PWD and nfsMount() args on start --- src/libCom/RTEMS/rtems_init.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libCom/RTEMS/rtems_init.c b/src/libCom/RTEMS/rtems_init.c index f2d83a7ab..156fc7154 100644 --- a/src/libCom/RTEMS/rtems_init.c +++ b/src/libCom/RTEMS/rtems_init.c @@ -333,6 +333,8 @@ initialize_remote_filesystem(char **argv, int hasLocalFilesystem) argv[1] = abspath; } } + errlogPrintf("nfsMount(\"%s\", \"%s\", \"%s\")\n", + server_name, server_path, mount_point); nfsMount(server_name, server_path, mount_point); #endif } @@ -403,6 +405,8 @@ set_directory (const char *commandline) directoryPath[l+1] = '\0'; if (chdir (directoryPath) < 0) LogFatal ("Can't set initial directory(%s): %s\n", directoryPath, strerror(errno)); + else + errlogPrintf("chdir(\"%s\")\n", directoryPath); free(directoryPath); } @@ -645,11 +649,13 @@ Init (rtems_task_argument ignored) /* * Run the EPICS startup script */ - printf ("***** Starting EPICS application *****\n"); + printf ("***** Preparing EPICS application *****\n"); iocshRegisterRTEMS (); set_directory (argv[1]); epicsEnvSet ("IOC_STARTUP_SCRIPT", argv[1]); atexit(exitHandler); + errlogFlush(); + printf ("***** Starting EPICS application *****\n"); i = main ((sizeof argv / sizeof argv[0]) - 1, argv); printf ("***** IOC application terminating *****\n"); epicsThreadSleep(1.0); From b4fd19e7dbdad3855e2aa7729d65f3c4e8385f3e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Feb 2016 17:09:44 -0500 Subject: [PATCH 185/204] libCom/test: remove test_debug from epicsStackTraceTest No one will look at this output unless the test fails, then more output is better. --- src/libCom/test/epicsStackTraceTest.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/libCom/test/epicsStackTraceTest.c b/src/libCom/test/epicsStackTraceTest.c index 69cb499f2..b89fc7836 100644 --- a/src/libCom/test/epicsStackTraceTest.c +++ b/src/libCom/test/epicsStackTraceTest.c @@ -27,8 +27,6 @@ /* estimated size of (compiled) epicsStackTraceRecurseGbl */ #define WINDOW_SZ 400 -static int test_debug = 0; - typedef struct TestDataRec_ { char buf[TST_BUFSZ]; int pos; @@ -114,8 +112,7 @@ findStringOcc(const char *buf, const char *what) buf += l; } - if ( test_debug ) - testDiag("found %i x %s\n", rval, what); + testDiag("found %i x %s\n", rval, what); return rval; } @@ -143,8 +140,7 @@ findNumOcc(const char *buf) if ( j != i && ptrs[j] == ptrs[i] ) { if ( (char*)ptrs[i] >= (char*)epicsStackTraceRecurseGbl && (char*)ptrs[i] < (char*)epicsStackTraceRecurseGbl + WINDOW_SZ ) { rval ++; - if ( test_debug ) - testDiag("found address %p again\n", ptrs[i]); + testDiag("found address %p again\n", ptrs[i]); } } j++; @@ -160,9 +156,6 @@ MAIN(epicsStackTraceTest) int gblFound, lclFound, numFound, dynFound; char *nl, *p; - if ( getenv("EPICS_STACK_TRACE_TEST_DEBUG") ) - test_debug = 1; - testData.pos = 0; testPlan(5); @@ -222,16 +215,14 @@ MAIN(epicsStackTraceTest) testSkip(1 , "no support for dumping addresses on this platform"); } - if ( test_debug ) { - p = testData.buf; - while ( (nl = strchr(p,'\n')) ) { - *nl = 0; - testDiag("%s",p); - *nl = '\n'; - p = nl+1; - } - testDiag("%s", p); + p = testData.buf; + while ( (nl = strchr(p,'\n')) ) { + *nl = 0; + testDiag("%s",p); + *nl = '\n'; + p = nl+1; } + testDiag("%s", p); testDone(); From 2be59e985d31fc520ab151392e3b6141c13c2eb0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 16 Feb 2016 15:07:40 -0500 Subject: [PATCH 186/204] Avoid race in linkRetarget Add dbCaSync() to avoid a race between a call to dbCaGetField() and the link becoming connected. --- src/ioc/db/dbCa.c | 42 ++++++++++++++++++++++++- src/ioc/db/dbCa.h | 4 +++ src/ioc/db/dbCaPvt.h | 1 + src/std/rec/test/linkRetargetLinkTest.c | 8 +++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 965115f3d..3c5948401 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -13,7 +13,7 @@ * Date: 26MAR96 * */ - +#define EPICS_DBCA_PRIVATE_API #include #include #include @@ -191,6 +191,42 @@ static void caLinkDec(caLink *pca) if (callback) callback(userPvt); } +/* Block until worker thread has processed all previously queued actions. + * Does not prevent additional actions from being queued. + */ +void dbCaSync(void) +{ + epicsEventId wake; + caLink templink; + + /* we only partially initialize templink. + * It has no link field and no subscription + * so the worker must handle it early + */ + memset(&templink, 0, sizeof(templink)); + templink.refcount = 1; + + wake = epicsEventMustCreate(epicsEventEmpty); + templink.lock = epicsMutexMustCreate(); + + templink.userPvt = wake; + + addAction(&templink, CA_SYNC); + + epicsEventMustWait(wake); + /* Worker holds workListLock when calling epicsEventMustTrigger() + * we cycle through workListLock to ensure worker call to + * epicsEventMustTrigger() returns before we destroy the event. + */ + epicsMutexMustLock(workListLock); + epicsMutexUnlock(workListLock); + + assert(templink.refcount==1); + + epicsMutexDestroy(templink.lock); + epicsEventDestroy(wake); +} + void dbCaCallbackProcess(void *userPvt) { struct link *plink = (struct link *)userPvt; @@ -947,9 +983,13 @@ static void dbCaTask(void *arg) break; /* workList is empty */ } link_action = pca->link_action; + if (link_action&CA_SYNC) + epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */ pca->link_action = 0; if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding; epicsMutexUnlock(workListLock); /* Give back immediately */ + if (link_action&CA_SYNC) + continue; if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */ caLinkDec(pca); /* No alarm is raised. Since link is changing so what? */ diff --git a/src/ioc/db/dbCa.h b/src/ioc/db/dbCa.h index cfdf7321f..85100c48f 100644 --- a/src/ioc/db/dbCa.h +++ b/src/ioc/db/dbCa.h @@ -71,6 +71,10 @@ epicsShareFunc long dbCaGetUnits(const struct link *plink, extern struct ca_client_context * dbCaClientContext; +#ifdef EPICS_DBCA_PRIVATE_API +epicsShareFunc void dbCaSync(void); +#endif + #ifdef __cplusplus } #endif diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index 6149d08ae..b5fc33635 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -32,6 +32,7 @@ #define CA_MONITOR_NATIVE 0x10 #define CA_MONITOR_STRING 0x20 #define CA_GET_ATTRIBUTES 0x40 +#define CA_SYNC 0x1000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 diff --git a/src/std/rec/test/linkRetargetLinkTest.c b/src/std/rec/test/linkRetargetLinkTest.c index e829fa6e0..894c94587 100644 --- a/src/std/rec/test/linkRetargetLinkTest.c +++ b/src/std/rec/test/linkRetargetLinkTest.c @@ -9,6 +9,7 @@ * * Test using several stringout records to retarget the link of another record */ +#define EPICS_DBCA_PRIVATE_API #include @@ -37,6 +38,13 @@ static void testRetarget(void) eltc(0); testIocInitOk(); eltc(1); + /* wait for local CA links to be connected or dbPutField() will fail */ + /* wait for initial CA_CONNECT actions to be processed. + * Assume that local CA links deliver callbacks synchronously + * eg. that ca_create_channel() will invoke the connection callback + * before returning. + */ + dbCaSync(); lnkmon = testMonitorCreate("rec:ai.INP", DBE_VALUE, 0); valmon = testMonitorCreate("rec:ai", DBE_VALUE, 0); From 64cd49410e8f31f3e949e81bcbe9fbed17a90080 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 29 Feb 2016 16:22:43 -0600 Subject: [PATCH 187/204] Make epicsMutex intialization use epicsThreadOnceId Includes Michael's patch to replace calls to the epicsMutex API from inside the libCom/osi/os/RTEMS/osdThread.c code. Fixes lp:1542539 --- src/libCom/osi/epicsMutex.cpp | 21 ++++--- src/libCom/osi/os/RTEMS/osdThread.c | 94 +++++++++++++++++------------ 2 files changed, 68 insertions(+), 47 deletions(-) diff --git a/src/libCom/osi/epicsMutex.cpp b/src/libCom/osi/epicsMutex.cpp index 15d12baa3..8580af204 100644 --- a/src/libCom/osi/epicsMutex.cpp +++ b/src/libCom/osi/epicsMutex.cpp @@ -34,7 +34,7 @@ #include "epicsMutex.h" #include "epicsThread.h" -static int firstTime = 1; +static epicsThreadOnceId epicsMutexOsiOnce = EPICS_THREAD_ONCE_INIT; static ELLLIST mutexList; static ELLLIST freeList; @@ -76,19 +76,20 @@ const char * epicsMutex::invalidMutex::what () const throw () return "epicsMutex::invalidMutex()"; } +static void epicsMutexOsiInit(void *) { + ellInit(&mutexList); + ellInit(&freeList); + VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0); + epicsMutexGlobalLock = epicsMutexOsdCreate(); +} epicsMutexId epicsShareAPI epicsMutexOsiCreate( const char *pFileName,int lineno) { epicsMutexOSD * id; - if(firstTime) { - firstTime=0; - ellInit(&mutexList); - ellInit(&freeList); - VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0); - epicsMutexGlobalLock = epicsMutexOsdCreate(); - } + epicsThreadOnce(&epicsMutexOsiOnce, epicsMutexOsiInit, NULL); + id = epicsMutexOsdCreate(); if(!id) { return 0; @@ -218,7 +219,9 @@ void epicsShareAPI epicsMutexShowAll(int onlyLocked,unsigned int level) { epicsMutexParm *pmutexNode; - if(firstTime) return; + if (epicsMutexOsiOnce == EPICS_THREAD_ONCE_INIT) + return; + printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n", ellCount(&mutexList),ellCount(&freeList)); epicsMutexLockStatus lockStat = diff --git a/src/libCom/osi/os/RTEMS/osdThread.c b/src/libCom/osi/os/RTEMS/osdThread.c index 1fa032820..ed6e73368 100644 --- a/src/libCom/osi/os/RTEMS/osdThread.c +++ b/src/libCom/osi/os/RTEMS/osdThread.c @@ -1,15 +1,12 @@ /*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * RTEMS osdThread.c * $Revision-Id$ * Author: W. Eric Norum - * eric@cls.usask.ca - * (306) 966-6055 */ /* @@ -56,15 +53,23 @@ struct taskVar { unsigned int threadVariableCapacity; void **threadVariables; }; -static epicsMutexId taskVarMutex; +static struct epicsMutexOSD *taskVarMutex; static struct taskVar *taskVarHead; #define RTEMS_NOTEPAD_TASKVAR 11 /* * Support for `once-only' execution */ -static int initialized = 0; -static epicsMutexId onceMutex; +static volatile int initialized = 0; /* strictly speaking 'volatile' is not enough here, but it shouldn't hurt */ +static struct epicsMutexOSD *onceMutex; + +static +void epicsMutexOsdMustLock(struct epicsMutexOSD * L) +{ + while(epicsMutexOsdLock(L)!=epicsMutexLockOK) { + cantProceed("epicsThreadOnce() mutex error"); + } +} /* * Just map osi 0 to 99 into RTEMS 199 to 100 @@ -99,7 +104,7 @@ int epicsThreadGetOssPriorityValue(unsigned int osiPriority) /* * epicsThreadLowestPriorityLevelAbove () */ -epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove +epicsShareFunc epicsThreadBooleanStatus epicsThreadLowestPriorityLevelAbove (unsigned int priority, unsigned *pPriorityJustAbove) { unsigned newPriority = priority + 1; @@ -115,7 +120,7 @@ epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityL /* * epicsThreadHighestPriorityLevelBelow () */ -epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow +epicsShareFunc epicsThreadBooleanStatus epicsThreadHighestPriorityLevelBelow (unsigned int priority, unsigned *pPriorityJustBelow) { unsigned newPriority = priority - 1; @@ -150,13 +155,13 @@ epicsThreadGetStackSize (epicsThreadStackSizeClass size) static void taskVarLock (void) { - epicsMutexLock (taskVarMutex); + epicsMutexOsdMustLock (taskVarMutex); } static void taskVarUnlock (void) { - epicsMutexUnlock (taskVarMutex); + epicsMutexOsdUnlock (taskVarMutex); } /* @@ -193,7 +198,8 @@ void epicsThreadExitMain (void) } static void -setThreadInfo (rtems_id tid, const char *name, EPICSTHREADFUNC funptr,void *parm) +setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr, + void *parm) { struct taskVar *v; uint32_t note; @@ -218,7 +224,8 @@ setThreadInfo (rtems_id tid, const char *name, EPICSTHREADFUNC funptr,void *parm if (funptr) { sc = rtems_task_start (tid, threadWrapper, (rtems_task_argument)v); if (sc != RTEMS_SUCCESSFUL) - errlogPrintf ("setThreadInfo: Can't start %s: %s\n",name, rtems_status_text (sc)); + errlogPrintf ("setThreadInfo: Can't start %s: %s\n", + name, rtems_status_text(sc)); } } @@ -236,8 +243,10 @@ epicsThreadInit (void) rtems_task_priority old; rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old); - onceMutex = epicsMutexMustCreate(); - taskVarMutex = epicsMutexMustCreate (); + onceMutex = epicsMutexOsdCreate(); + taskVarMutex = epicsMutexOsdCreate(); + if (!onceMutex || !taskVarMutex) + cantProceed("epicsThreadInit() can't create global mutexes\n"); rtems_task_ident (RTEMS_SELF, 0, &tid); setThreadInfo (tid, "_main_", NULL, NULL); osdThreadHooksRunMain((epicsThreadId)tid); @@ -265,7 +274,8 @@ epicsThreadCreate (const char *name, if (!initialized) epicsThreadInit(); if (stackSize < RTEMS_MINIMUM_STACK_SIZE) { - errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n",name,stackSize); + errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n", + name, stackSize); stackSize = RTEMS_MINIMUM_STACK_SIZE; } strncpy (c, name, sizeof c); @@ -276,7 +286,8 @@ epicsThreadCreate (const char *name, RTEMS_FLOATING_POINT|RTEMS_LOCAL, &tid); if (sc != RTEMS_SUCCESSFUL) { - errlogPrintf ("epicsThreadCreate create failure for %s: %s\n",name, rtems_status_text (sc)); + errlogPrintf ("epicsThreadCreate create failure for %s: %s\n", + name, rtems_status_text(sc)); return 0; } setThreadInfo (tid, name, funptr,parm); @@ -288,10 +299,10 @@ threadMustCreate (const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void *parm) { - epicsThreadId tid; + epicsThreadId tid = epicsThreadCreate(name, priority, stackSize, funptr, parm); - if ((tid = epicsThreadCreate (name, priority, stackSize, funptr, parm)) == NULL) - cantProceed (0); + if (tid == NULL) + cantProceed(0); return tid; } @@ -301,8 +312,9 @@ epicsThreadSuspendSelf (void) rtems_status_code sc; sc = rtems_task_suspend (RTEMS_SELF); - if(sc != RTEMS_SUCCESSFUL) - errlogPrintf("epicsThreadSuspendSelf failed: %s\n", rtems_status_text (sc)); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadSuspendSelf failed: %s\n", + rtems_status_text(sc)); } void epicsThreadResume(epicsThreadId id) @@ -311,8 +323,9 @@ void epicsThreadResume(epicsThreadId id) rtems_status_code sc; sc = rtems_task_resume (tid); - if(sc != RTEMS_SUCCESSFUL) - errlogPrintf("epicsThreadResume failed: %s\n", rtems_status_text (sc)); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadResume failed: %s\n", + rtems_status_text (sc)); } unsigned int epicsThreadGetPriority(epicsThreadId id) @@ -323,8 +336,9 @@ unsigned int epicsThreadGetPriority(epicsThreadId id) sc = rtems_task_set_priority (tid, RTEMS_CURRENT_PRIORITY, &pri); if (sc != RTEMS_SUCCESSFUL) - errlogPrintf("epicsThreadGetPriority failed: %s\n", rtems_status_text (sc)); - return epicsThreadGetOsiPriorityValue (pri); + errlogPrintf("epicsThreadGetPriority failed: %s\n", + rtems_status_text(sc)); + return epicsThreadGetOsiPriorityValue(pri); } unsigned int epicsThreadGetPrioritySelf(void) @@ -341,7 +355,8 @@ epicsThreadSetPriority (epicsThreadId id,unsigned int osip) sc = rtems_task_set_priority (tid, pri, &pri); if (sc != RTEMS_SUCCESSFUL) - errlogPrintf("epicsThreadSetPriority failed: %s\n", rtems_status_text (sc)); + errlogPrintf("epicsThreadSetPriority failed: %s\n", + rtems_status_text(sc)); } int @@ -385,7 +400,7 @@ epicsThreadSleep (double seconds) } sc = rtems_task_wake_after (delay); if(sc != RTEMS_SUCCESSFUL) - errlogPrintf("epicsThreadSleep: %s\n", rtems_status_text (sc)); + errlogPrintf("epicsThreadSleep: %s\n", rtems_status_text(sc)); } epicsThreadId @@ -425,7 +440,9 @@ void epicsThreadGetName (epicsThreadId id, char *name, size_t size) } taskVarUnlock (); if (!haveName) { -#if (__RTEMS_MAJOR__>4 || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>8) || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==8 && __RTEMS_REVISION__>=99)) +#if (__RTEMS_MAJOR__>4 || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>8) || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==8 && __RTEMS_REVISION__>=99)) if (_Objects_Get_name_as_string((rtems_id)id, size, name) != NULL) haveName = 1; #else @@ -486,26 +503,26 @@ void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg) #define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1 if (!initialized) epicsThreadInit(); - epicsMutexMustLock(onceMutex); + epicsMutexOsdMustLock(onceMutex); if (*id != EPICS_THREAD_ONCE_DONE) { if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ - epicsMutexUnlock(onceMutex); + epicsMutexOsdUnlock(onceMutex); func(arg); - epicsMutexMustLock(onceMutex); + epicsMutexOsdMustLock(onceMutex); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if (*id == epicsThreadGetIdSelf()) { - epicsMutexUnlock(onceMutex); + epicsMutexOsdUnlock(onceMutex); cantProceed("Recursive epicsThreadOnce() initialization\n"); } else while (*id != EPICS_THREAD_ONCE_DONE) { /* Another thread is in the above func(arg) call. */ - epicsMutexUnlock(onceMutex); + epicsMutexOsdUnlock(onceMutex); epicsThreadSleep(epicsThreadSleepQuantum()); - epicsMutexMustLock(onceMutex); + epicsMutexOsdMustLock(onceMutex); } } - epicsMutexUnlock(onceMutex); + epicsMutexOsdUnlock(onceMutex); } /* @@ -539,7 +556,8 @@ void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt) rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); v = (struct taskVar *)note; if (varIndex >= v->threadVariableCapacity) { - v->threadVariables = realloc (v->threadVariables, (varIndex + 1) * sizeof (void *)); + v->threadVariables = realloc (v->threadVariables, + (varIndex + 1) * sizeof(void *)); if (v->threadVariables == NULL) cantProceed("epicsThreadPrivateSet realloc failed\n"); for (i = v->threadVariableCapacity ; i < varIndex ; i++) From e01d4b14d6b09ffec9dda9a02acc7d023a94af5b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 29 Feb 2016 20:37:25 -0500 Subject: [PATCH 188/204] rec/test: add missing TESTFILES --- src/std/rec/test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index f5a60a751..07daa1bf4 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -52,7 +52,7 @@ TESTPROD_HOST += asTest asTest_SRCS += asTest.c asTest_SRCS += asTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += asTest.c -TESTFILES += ../asTest.db +TESTFILES += $(COMMON_DIR)/asTestIoc.dbd ../asTest.db TESTS += asTest TARGETS += $(COMMON_DIR)/analogMonitorTest.dbd From 5d42e60745df46fc1937a439270950981d2e9018 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 29 Feb 2016 20:43:40 -0500 Subject: [PATCH 189/204] rec/test: asTest fixup rtems test harness Since this test has device support it must appear in a DLL for windows dynamic builds. However, the rRDD function is in the executable, and not accessible here. So use iocsh. For rtems/vxworks the test harness clears iocsh registrations, so iocsh can't work here. --- src/std/rec/test/asTestLib.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/std/rec/test/asTestLib.c b/src/std/rec/test/asTestLib.c index 2eca72775..62b5e62fc 100644 --- a/src/std/rec/test/asTestLib.c +++ b/src/std/rec/test/asTestLib.c @@ -217,6 +217,10 @@ static void hookPass1(initHookState state) dbFinishEntry(&entry); } +#if defined(__rtems__) || defined(vxWorks) +void asTestIoc_registerRecordDeviceDriver(struct dbBase *); +#endif + epicsShareFunc void testRestore(void) { @@ -231,7 +235,18 @@ void testRestore(void) testdbReadDatabase("asTestIoc.dbd", NULL, NULL); + /* since this test has device support it must appear in a + * DLL for windows dynamic builds. + * However, the rRDD function is in the executable, + * and not accessible here. So use iocsh. + * For rtems/vxworks the test harness clears + * iocsh registrations, so iocsh can't work here. + */ +#if defined(__rtems__) || defined(vxWorks) + asTestIoc_registerRecordDeviceDriver(pdbbase); +#else iocshCmd("asTestIoc_registerRecordDeviceDriver(pdbbase)"); +#endif testdbReadDatabase("asTest.db", NULL, NULL); From 4a0ef34771f2ed8c072122927756e7df91f98052 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 29 Feb 2016 21:08:25 -0500 Subject: [PATCH 190/204] edit RELEASE_NOTES.html --- documentation/RELEASE_NOTES.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 5ba14b373..f617c937e 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -43,10 +43,10 @@ supported by the valgrind tool. It can also explicitly be disabled by defining the macro NVALGRIND. See src/libCom/Makefile for a commented-out example.

-

As a matter of policy valgrind.h should never be included by any header file -installed by Base, so its use will remain purely an internal implementation -detail and not be directly visible to application software. -Support modules which choose to use valgrind.h are advised to avoid to do +

As a matter of policy valgrind.h will never be included by any header file +installed by Base, so its use will remain purely an implementation +detail hidden from application software. +Support modules which choose to use valgrind.h are advised to do likewise.

Database Multi-locking

@@ -70,7 +70,7 @@ Locking/unlocking of unrelated lock sets is now completely concurrent.

A Perl script and Makefile rules have been added to allow modules to generate a C header file with a macro defined with an automatically updated identifier. -This is a VCS revision ID (Darcs, Git, Mercurial Subversion and Bazaar are all +This is a VCS revision ID (Darcs, Git, Mercurial, Subversion, and Bazaar are supported) or the date/time of the build if no VCS system is in use.

The makeBaseApp example template has been updated with a new device support From 19680d7869b6b3e75937b5774a5c316308365015 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 1 Mar 2016 10:57:26 -0500 Subject: [PATCH 191/204] libCom/test: fixup epicsAtomicTest Add testBasic to check basic op symantics. Add anon namespace to hide symbols other than MAIN(epicsAtomicTest). Replace using namespace with using for individual functions. For RTEMS skip the tests using threads as the use of RTEMS_NO_TIMESLICE prevents them from being meaningful --- src/libCom/test/epicsAtomicTest.cpp | 112 ++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 16 deletions(-) diff --git a/src/libCom/test/epicsAtomicTest.cpp b/src/libCom/test/epicsAtomicTest.cpp index 1c4dcf20f..2d7c81bb9 100644 --- a/src/libCom/test/epicsAtomicTest.cpp +++ b/src/libCom/test/epicsAtomicTest.cpp @@ -8,8 +8,7 @@ #include "epicsUnitTest.h" #include "testMain.h" -using namespace epics; -using namespace atomic; +namespace { template < class T > struct TestDataIncrDecr { @@ -27,6 +26,7 @@ struct TestDataAddSub { template < class T > static void incr ( void *arg ) { + using epics::atomic::increment; TestDataIncrDecr < T > * const pTestData = reinterpret_cast < TestDataIncrDecr < T > * > ( arg ); increment ( pTestData->m_testValue ); @@ -36,6 +36,8 @@ static void incr ( void *arg ) template < class T > static void decr ( void *arg ) { + using epics::atomic::decrement; + using epics::atomic::increment; TestDataIncrDecr < T > * const pTestData = reinterpret_cast < TestDataIncrDecr < T > * > ( arg ); decrement ( pTestData->m_testValue ); @@ -46,6 +48,8 @@ static void decr ( void *arg ) template < class T > static void add ( void *arg ) { + using epics::atomic::add; + using epics::atomic::increment; TestDataAddSub < T > * const pTestData = reinterpret_cast < TestDataAddSub < T > * > ( arg ); add ( pTestData->m_testValue, TestDataAddSub < T > :: delta ); @@ -55,6 +59,8 @@ static void add ( void *arg ) template < class T > static void sub ( void *arg ) { + using epics::atomic::subtract; + using epics::atomic::increment; TestDataAddSub < T > * const pTestData = reinterpret_cast < TestDataAddSub < T > * > ( arg ); subtract ( pTestData->m_testValue, TestDataAddSub < T > :: delta ); @@ -68,11 +74,6 @@ struct TestDataCAS { size_t m_testIterationsNotSet; }; -int isModulo ( size_t N, size_t n ) -{ - return ( n % N ) == 0u; -} - template < class T > static T trueValue (); template < class T > @@ -104,6 +105,11 @@ inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > () template < class T > static void cas ( void *arg ) { + using epics::atomic::set; + using epics::atomic::increment; + using epics::atomic::decrement; + using epics::atomic::compareAndSwap; + TestDataCAS < T > * const pTestData = reinterpret_cast < TestDataCAS < T > * > ( arg ); /* @@ -123,7 +129,10 @@ static void cas ( void *arg ) template < class T > void testIncrDecr () { - static const size_t N = 100; + using epics::atomic::set; + using epics::atomic::get; + + static const size_t N = 90; static const T NT = static_cast < T > ( N ); const unsigned int stackSize = @@ -137,10 +146,12 @@ void testIncrDecr () testOk ( get ( testData.m_testIterations ) == 0u, "get returns initial incr/decr test thread iterations value that was set" ); for ( size_t i = 0u; i < N; i++ ) { - epicsThreadCreate ( "incr", + epicsThreadMustCreate ( "incr", 50, stackSize, incr < T >, & testData ); - epicsThreadCreate ( "decr", + epicsThreadMustCreate ( "decr", 50, stackSize, decr < T >, & testData ); + if(i%10==0) + testDiag("iteration %u", (unsigned)i); } while ( testData.m_testIterations < 2 * N ) { epicsThreadSleep ( 0.01 ); @@ -154,7 +165,10 @@ void testIncrDecr () template < class T > void testAddSub () { - static const size_t N = 100; + using epics::atomic::set; + using epics::atomic::get; + + static const size_t N = 90; static const T NDT = TestDataAddSub < T > :: delta * static_cast < T > ( N ); @@ -169,9 +183,9 @@ void testAddSub () testOk ( get ( testData.m_testIterations ) == 0u, "get returns initial incr/decr test thread iterations value that was set" ); for ( size_t i = 0u; i < N; i++ ) { - epicsThreadCreate ( "add", + epicsThreadMustCreate ( "add", 50, stackSize, add < T >, & testData ); - epicsThreadCreate ( "sub", + epicsThreadMustCreate ( "sub", 50, stackSize, sub < T >, & testData ); } while ( testData.m_testIterations < 2 * N ) { @@ -186,6 +200,9 @@ void testAddSub () template < class T > void testCAS () { + using epics::atomic::set; + using epics::atomic::get; + static const size_t N = 10; const unsigned int stackSize = @@ -204,7 +221,7 @@ void testCAS () testOk ( get ( testData.m_testValue ) == trueValue < T > (), "set/get a true value" ); for ( size_t i = 0u; i < N; i++ ) { - epicsThreadCreate ( "tns", + epicsThreadMustCreate ( "tns", 50, stackSize, cas < T >, & testData ); } set ( testData.m_testValue, falseValue < T > () ); @@ -326,12 +343,74 @@ static void testClassify() #endif /* __GNUC__ */ } +static +void testBasic() +{ + using epics::atomic::set; + using epics::atomic::get; + using epics::atomic::decrement; + using epics::atomic::increment; + using epics::atomic::add; + using epics::atomic::subtract; + using epics::atomic::compareAndSwap; + + testDiag("Test basic operation symantics"); + + int Int = 0; + size_t Sizet = 0; + void *voidp = NULL; + + set(Int, -42); + set(Sizet, 42); + set(voidp, (void*)&voidp); + + increment(Int); + increment(Sizet); + + testOk1(get(Int)==-41); + testOk1(get(Sizet)==43); + testOk1(get(voidp)==(void*)&voidp); + + decrement(Int); + decrement(Sizet); + + testOk1(get(Int)==-42); + testOk1(get(Sizet)==42); + + add(Int, -2); + subtract(Sizet, 2); + + testOk1(get(Int)==-44); + testOk1(get(Sizet)==40); + + testOk1(compareAndSwap(Int, -34, -10)==-44); + testOk1(compareAndSwap(Sizet, 34, 10)==40); + testOk1(compareAndSwap(voidp, NULL, (void*)&Sizet)==(void*)&voidp); + + testOk1(get(Int)==-44); + testOk1(get(Sizet)==40); + testOk1(get(voidp)==(void*)&voidp); + + testOk1(compareAndSwap(Int, -44, -10)==-44); + testOk1(compareAndSwap(Sizet, 40, 10)==40); + testOk1(compareAndSwap(voidp, (void*)&voidp, (void*)&Sizet)==(void*)&voidp); + + testOk1(get(Int)==-10); + testOk1(get(Sizet)==10); + testOk1(get(voidp)==(void*)&Sizet); +} + +} // namespace + MAIN ( epicsAtomicTest ) { - testPlan ( 31 ); + testPlan ( 50 ); testClassify (); - + testBasic(); +#if defined(__rtems__) + testSkip(31, "Tests assume time sliced thread scheduling"); +#else testIncrDecr < int > (); testIncrDecr < size_t > (); testAddSub < int > (); @@ -339,6 +418,7 @@ MAIN ( epicsAtomicTest ) testCAS < int > (); testCAS < size_t > (); testCAS < EpicsAtomicPtrT > (); +#endif return testDone (); } From 8fea0117aea7f54e6119e0896320befe4a0124b6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 2 Mar 2016 14:20:53 -0600 Subject: [PATCH 192/204] Clean up build rules, remove CapFast & dbst support --- configure/CONFIG_BASE | 45 ++--- configure/CONFIG_COMMON | 4 - configure/RULES.Db | 164 +++++++++---------- documentation/RELEASE_NOTES.html | 6 + src/template/base/top/iocApp/Db/Makefile | 4 - src/template/base/top/supportApp/Db/Makefile | 4 - 6 files changed, 90 insertions(+), 137 deletions(-) diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index 8db74bac7..1957a8f56 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -3,13 +3,12 @@ # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. -# EPICS BASE Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found +# EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* #--------------------------------------------------------------- -# Epics base directories +# EPICS Base directories EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH) EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH) @@ -19,12 +18,12 @@ ifdef T_A endif #--------------------------------------------------------------- -# Epics base Ioc libraries +# EPICS Base Ioc libraries EPICS_BASE_IOC_LIBS += dbRecStd dbCore ca Com #--------------------------------------------------------------- -# Epics base Host libraries +# EPICS Base Host libraries EPICS_BASE_HOST_LIBS += cas gdd EPICS_BASE_HOST_LIBS += ca Com @@ -42,13 +41,11 @@ endif # BASE_TOP #--------------------------------------------------------------- # Base c preprocessor flags -BASE_CPPFLAGS = - # osithread default stack OSITHREAD_USE_DEFAULT_STACK = NO OSITHREAD_DEFAULT_STACK_FLAGS_YES = -DOSITHREAD_USE_DEFAULT_STACK -OSITHREAD_DEFAULT_STACK_FLAGS_NO = -BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK)) + +BASE_CPPFLAGS = $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK)) #--------------------------------------------------------------- # Where to find the installed build tools @@ -59,7 +56,7 @@ TOOLS = $(abspath $(EPICS_BASE_HOST_BIN)) FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1))) #--------------------------------------------------------------- -# Epics base build tools and tool flags +# EPICS Base build tools and tool flags MAKEBPT = $(TOOLS)/makeBpt$(HOSTEXE) DBEXPAND = $(PERL) $(TOOLS)/dbdExpand.pl @@ -70,7 +67,7 @@ CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl) FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) -#------------------------------------------------------- +#--------------------------------------------------------------- # tools for installing libraries and products INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG) INSTALL_PRODUCT = $(INSTALL) @@ -82,7 +79,7 @@ MKMF = $(PERL) $(TOOLS)/mkmf.pl REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl #--------------------------------------------------------------- -# private versions of lex/yacc from EPICS +# Our versions of lex (flex) and yacc (antelope) EYACC = $(TOOLS)/antelope$(HOSTEXE) ELEX = $(TOOLS)/e_flex$(HOSTEXE) -S$(EPICS_BASE)/include/flex.skel.static @@ -90,28 +87,6 @@ YACC = $(EYACC) LEX = $(ELEX) #--------------------------------------------------------------- -# Our use of msi is incompatible with older versions +# The 3.15 version of msi supports new options MSI3_15 = $(EPICS_BASE_HOST_BIN)/msi - -#--------------------------------------------------------------- -# External tools and tool flags - must be in path or defined in application - -ADL2DL ?= adl2dl - -# sch2edif compiler and flags -SCH2EDIF = sch2edif -SCH2EDIF_PATH = -SCH2EDIF_SYSFLAGS = -n -ap -p.+..+$(SCH2EDIF_PATH)+$(CAPFAST_TEMPLATES)/sym+ -SCH2EDIF_FLAGS = - -# e2db and flags -# - again there is an assumption where edb.def is installed. -E2DB ?= e2db -E2DB_SYSFLAGS = -ate -d $(CAPFAST_TEMPLATES)/edb.def -E2DB_FLAGS = - -DBST ?= dbst - - - diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index e74f05e20..2df341581 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -40,10 +40,6 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2) # otherwise override this in os/CONFIG_SITE..Common PERL = perl -CSD -#------------------------------------------------------- -# dbst based database optimization default -DB_OPT = NO - #------------------------------------------------------- # Check configure/RELEASE file for consistency CHECK_RELEASE_YES = checkRelease diff --git a/configure/RULES.Db b/configure/RULES.Db index 620677407..38fb65ae0 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -8,10 +8,12 @@ #************************************************************************* #RULES.Db -# Set db substitutions file suffix +# Set db substitutions and template file suffixes SUBST_SUFFIX ?= .substitutions +TEMPL_SUFFIX ?= .template -##################################################### vpath +#--------------------------------------------------------------- +# vpath vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) @@ -19,12 +21,13 @@ vpath %.dbd $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) vpath %.db $(USR_VPATH) $(SRC_DIRS) $(dir $(DB)) vpath %.vdb $(USR_VPATH) $(SRC_DIRS) $(dir $(DB)) vpath %$(SUBST_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) -vpath %.template $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) +vpath %$(TEMPL_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) vpath bpt%.data $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) vpath %.acf $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) vpath %.acs $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) -##################################################### dbflags dbdflags +#--------------------------------------------------------------- +# dbflags dbdflags DBD_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DBD) $(RELEASE_DBD_DIRS) DB_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DB) $(RELEASE_DB_DIRS) @@ -33,13 +36,15 @@ DBDFLAGS = $(USR_DBDFLAGS) $(CMD_DBDFLAGS) $(addprefix -I,$(DBD_SEARCH_DIRS)) DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) $(CMD_DBFLAGS) $(addprefix -I,$(DB_SEARCH_DIRS)) REGRDDFLAGS = $(DBDFLAGS) $($*_REGRDDFLAGS) $(USR_REGRDDFLAGS) $(CMD_REGRDDFLAGS) -##################################################### Targets +#--------------------------------------------------------------- +# Targets # --------------------------------------------------- # To allow os specific dbd files AND have the -j option work properly, -CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \ - $(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS),$(firstword $(subst -, ,$(target))))) +CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \ + $(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS), \ + $(firstword $(subst -, ,$(target))))) DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type))) # Users add os specific dbd files to a Makefile as follows @@ -86,31 +91,28 @@ SOURCE_DB_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DB_aaa) ) SOURCE_DB_aaa = $(addsuffix /$(file), $(dir) ) COMMONS = $(COMMON_DIR)/*.dbd $(COMMON_DIR)/*.db $(COMMON_DIR)/*.h \ - $(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*.template + $(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*$(TEMPL_SUFFIX) # Remove trailing numbers (to 99) on stem -TEMPLATE1=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \ - $(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \ - $*)))))))))) -TEMPLATE2=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \ - $(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \ - $(TEMPLATE1))))))))))) -TEMPLATE3=$(addsuffix .template,$(addprefix ../,$(TEMPLATE2))) -TEMPLATE_FILENAME=$(firstword $(wildcard $($*_TEMPLATE) $(addprefix ../,$($*_TEMPLATE)) ../$*.template $(TEMPLATE3) ../template)) - -# dbst based database optimization -ifeq '$(DB_OPT)' 'YES' -RAW=.raw -DBS = $(filter %.db,$(DB)) $(addsuffix $(RAW),$(filter %.db,$(DB))) -COMMON_DBS = $(addprefix $(COMMON_DIR)/,$(DBS)) -endif +TEMPLATE1 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \ + $(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \ + $(patsubst %8,%,$(patsubst %9,%,$*)))))))))) +TEMPLATE2 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \ + $(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \ + $(patsubst %8,%,$(patsubst %9,%,$(TEMPLATE1))))))))))) +TEMPLATE3 = $(addsuffix $(TEMPL_SUFFIX),$(addprefix ../,$(TEMPLATE2))) +TEMPLATE_FILENAME = $(firstword $(wildcard $($*_TEMPLATE) \ + $(addprefix ../,$($*_TEMPLATE)) ../$*$(TEMPL_SUFFIX) $(TEMPLATE3) \ + ../template)) INSTALL_DB_INSTALLS = $(addprefix $(INSTALL_DB)/,$(notdir $(DB_INSTALLS))) INSTALL_DBD_INSTALLS = $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD_INSTALLS))) COMMONDEP_TARGET = $(COMMON_DIR)/$(basename $@) -##################################################### acf files +#--------------------------------------------------------------- +# acf files + # An access security configuration file, *.acf, can be created from # an *.acs file (has format of acf file plus #include "filename" lines) @@ -123,7 +125,8 @@ ACF_INCLUDES = -I. $(TARGET_INCLUDES) $(USR_INCLUDES)\ ACFDEPENDS_CMD = $(MKMF) -m $@ $(ACF_INCLUDES) $(COMMONDEP_TARGET) $< ACF_CMD = $(CPP) $(ACF_CPPFLAGS) $(ACF_INCLUDES) $< > $@ -##################################################### dependancies +#--------------------------------------------------------------- +# dependencies HINC += $(addsuffix .h,$(DBDINC_NAME)) COMMON_DBDINC += $(addprefix $(COMMON_DIR)/,$(HINC)) @@ -133,12 +136,12 @@ DBDDEPENDS_FILES += $(addsuffix $(DEP),$(HINC) \ $(patsubst $(COMMON_DIR)/%,%, \ $(filter-out $(COMMON_DIR)/bpt%.dbd,$(COMMON_DBDS)))) -##################################################### +#--------------------------------------------------------------- ifndef T_A DEP = .d -TEMPLATE3+=$(addsuffix .template, $(TEMPLATE2)) +TEMPLATE3 += $(addsuffix $(TEMPL_SUFFIX), $(TEMPLATE2)) COMMON_DIR = . INSTALL_DBDS = @@ -153,18 +156,13 @@ ACTIONS += install ACTIONS += buildInstall ACTIONS += runtests tapfiles -actionArchTargets = $(foreach x, $(ACTIONS),\ $(foreach arch,$(BUILD_ARCHS), $(x)$(DIVIDER)$(arch))) +actionArchTargets = $(foreach action, $(ACTIONS), \ + $(foreach arch, $(BUILD_ARCHS), $(action)$(DIVIDER)$(arch))) +cleanArchTargets = $(foreach arch, $(BUILD_ARCHS), clean$(DIVIDER)$(arch)) -cleanArchTargets = $(foreach arch,$(BUILD_ARCHS), clean$(DIVIDER)$(arch)) -include $(TOP)/configure/CONFIG_APP_INCLUDE all: install -ifeq ($(EPICS_HOST_ARCH),$T_A) -host: install -else -# Do nothing -host: -endif install: buildInstall @@ -172,20 +170,30 @@ buildInstall : build rebuild: clean install -.PHONY: all host $(ACTIONS) +.PHONY: all $(ACTIONS) $(actionArchTargets) $(BUILD_ARCHS): install $(cleanArchTargets): clean .PHONY: $(BUILD_ARCHS) $(actionArchTargets) $(cleanArchTargets) -endif # T_A defined +else + # T_A is defined + ifeq ($(EPICS_HOST_ARCH),$(T_A)) + host: install + else + host: + endif + + .PHONY: host +endif # T_A ifneq (,$(strip $(DBDDEPENDS_FILES))) -include $(DBDDEPENDS_FILES) endif -##################################################### build dependancies, clean rule +#--------------------------------------------------------------- +# build dependancies, clean rule inc : $(COMMON_INC) $(INSTALL_INC) @@ -205,7 +213,8 @@ db_clean : realclean: clean -##################################################### Dependency files +#--------------------------------------------------------------- +# Dependency files %Record.h$(DEP): $(COMMON_DIR)/%Record.dbd @$(RM) $@ @@ -256,19 +265,19 @@ menu%.h$(DEP): ../menu%.dbd @$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ -%.db$(RAW)$(DEP): %$(SUBST_SUFFIX) +%.db$(DEP): %$(SUBST_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@ -%.db$(RAW)$(DEP): ../%$(SUBST_SUFFIX) +%.db$(DEP): ../%$(SUBST_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@ -%.db$(RAW)$(DEP): %.template +%.db$(DEP): %$(TEMPL_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@ -%.db$(RAW)$(DEP): ../%.template +%.db$(DEP): ../%$(TEMPL_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @@ -282,14 +291,8 @@ menu%.h$(DEP): ../menu%.dbd .PRECIOUS: %$(DEP) -##################################################### CapFast filter - -$(COMMON_DIR)/%.edf: ../%.sch $(DEPSCHS) - @$(RM) $@ - @if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi - $(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) -o $@ $< - -##################################################### Substitution files +#--------------------------------------------------------------- +# Substitution files # WARNING: CREATESUBSTITUTIONS script needs output dir on command line @@ -310,25 +313,21 @@ $(INSTALL_DB)/%$(SUBST_SUFFIX): ../%$(SUBST_SUFFIX) .PRECIOUS: $(COMMON_DIR)/%$(SUBST_SUFFIX) -##################################################### Template files +#--------------------------------------------------------------- +# Template files -$(COMMON_DIR)/%.template: $(COMMON_DIR)/%.edf - @$(RM) $@ - $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $< - @$(REPLACEVAR) < $@.VAR > $@ - @$(RM) $@.VAR - -$(INSTALL_DB)/%.template: %.template +$(INSTALL_DB)/%$(TEMPL_SUFFIX): %$(TEMPL_SUFFIX) $(ECHO) "Installing template file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) -$(INSTALL_DB)/%.template: ../%.template +$(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX) $(ECHO) "Installing template file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) -.PRECIOUS: $(COMMON_DIR)/%.template +.PRECIOUS: $(COMMON_DIR)/%$(TEMPL_SUFFIX) -##################################################### INC files +#--------------------------------------------------------------- +# INC files $(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd @$(RM) $(notdir $@) @@ -362,7 +361,8 @@ $(COMMON_DIR)/menu%.h: ../menu%.dbd .PRECIOUS: $(COMMON_DIR)/%.h -##################################################### DBD files +#--------------------------------------------------------------- +# DBD files $(COMMON_DIR)/bpt%.dbd: bpt%.data @$(RM) $(notdir $@) @@ -425,7 +425,8 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file)))) .PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%.dbd -##################################################### HTML files +#--------------------------------------------------------------- +# HTML files $(COMMON_DIR)/%.html: %.dbd.pod $(TOOLS)/dbdToHtml.pl @$(RM) $(notdir $@) @@ -455,32 +456,33 @@ $(COMMON_DIR)/%.html: ../%.pl $(TOOLS)/podToHtml.pl .PRECIOUS: $(COMMON_DIR)/%.html %.html -##################################################### DB files +#--------------------------------------------------------------- +# DB files -$(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf +$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.edf $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $*.VAR $< @$(REPLACEVAR) < $*.VAR > $@ @$(RM) $*.VAR -$(COMMON_DIR)/%.db$(RAW): %$(SUBST_SUFFIX) +$(COMMON_DIR)/%.db: %$(SUBST_SUFFIX) $(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME) @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.db$(RAW): ../%$(SUBST_SUFFIX) +$(COMMON_DIR)/%.db: ../%$(SUBST_SUFFIX) $(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME) @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.db$(RAW): %.template +$(COMMON_DIR)/%.db: %$(TEMPL_SUFFIX) $(ECHO) "Inflating database from $<" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.db$(RAW): ../%.template +$(COMMON_DIR)/%.db: ../%$(TEMPL_SUFFIX) $(ECHO) "Inflating database from $<" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) $< @@ -498,22 +500,6 @@ $(COMMON_DIR)/%.acf: ../%.acs .PRECIOUS: $(COMMON_DIR)/%.acf -# dbst based database optimization -ifeq '$(DB_OPT)' 'YES' - -$(COMMON_DIR)/%.db$(RAW): ../%.db - @$(RM) $@ - $(CP) $< $@ - -$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.db$(RAW) - $(ECHO) "Optimizing database $@" - @$(RM) $@ - $(DBST) . $< -d > $@ - -.PRECIOUS: $(COMMON_DIR)/%.db -.PRECIOUS: $(DB:%=$(COMMON_DIR)/%$(RAW)) -else - $(INSTALL_DB)/%: % $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) @@ -521,7 +507,6 @@ $(INSTALL_DB)/%: % $(INSTALL_DB)/%: ../% $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) -endif $(INSTALL_DB)/%.db: $(COMMON_DIR)/%.db $(ECHO) "Installing created db file $@" @@ -537,8 +522,8 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file)))) .PRECIOUS: $(COMMON_DIR)/%.edf .PRECIOUS: $(COMMON_DBS) -##################################################### register record,device,driver support - +#--------------------------------------------------------------- +# register record,device,driver support %_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd @$(RM) $@ @@ -554,4 +539,3 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file)))) .PRECIOUS: %_registerRecordDeviceDriver.cpp -##################################################### END OF FILE diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index f617c937e..3150fd87a 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,12 @@ --> +

Build support for CapFast and dbst removed

+ +

The build rules associated with the CapFast-related tools sch2edif +and e2db and the database optimization tool dbst have +been removed, along with the DB_OPT build configuration variable.

+

compressRecord buffering order

The compressRecord has a new field BALG which can select between diff --git a/src/template/base/top/iocApp/Db/Makefile b/src/template/base/top/iocApp/Db/Makefile index 983981d40..8eb97279d 100644 --- a/src/template/base/top/iocApp/Db/Makefile +++ b/src/template/base/top/iocApp/Db/Makefile @@ -3,10 +3,6 @@ include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE -#---------------------------------------------------- -# Optimization of db files using dbst (DEFAULT: NO) -#DB_OPT = YES - #---------------------------------------------------- # Create and install (or just install) into /db # databases, templates, substitutions like this diff --git a/src/template/base/top/supportApp/Db/Makefile b/src/template/base/top/supportApp/Db/Makefile index 983981d40..8eb97279d 100644 --- a/src/template/base/top/supportApp/Db/Makefile +++ b/src/template/base/top/supportApp/Db/Makefile @@ -3,10 +3,6 @@ include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE -#---------------------------------------------------- -# Optimization of db files using dbst (DEFAULT: NO) -#DB_OPT = YES - #---------------------------------------------------- # Create and install (or just install) into /db # databases, templates, substitutions like this From 3d7e95b88ac9c5764984bd5fe8a7c5fe7817c441 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 3 Mar 2016 13:39:48 -0600 Subject: [PATCH 193/204] Debug print value of IF flags in default/osdNetIntf.c --- src/libCom/osi/os/default/osdNetIntf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libCom/osi/os/default/osdNetIntf.c b/src/libCom/osi/os/default/osdNetIntf.c index 9c9026f87..c3f1a89cb 100644 --- a/src/libCom/osi/os/default/osdNetIntf.c +++ b/src/libCom/osi/os/default/osdNetIntf.c @@ -151,7 +151,8 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name); continue; } - + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" flags: %x\n", pIfreqList->ifr_name, pIfreqList->ifr_flags) ); + /* * dont bother with interfaces that have been disabled */ From fc4f010972526047c273619b11185f0976f630e7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 3 Mar 2016 17:19:50 -0600 Subject: [PATCH 194/204] Documentation updates for release --- README | 10 +++++----- documentation/KnownProblems.html | 22 +++++++++++++++------- documentation/ReleaseChecklist.html | 22 +++++++++++----------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/README b/README index ce971d630..dcf34b258 100644 --- a/README +++ b/README @@ -2,14 +2,14 @@ EPICS Base - the central core of a control system toolkit --------------------------------------------------------- -Copyright (c) 1991-2003 The University of Chicago, as Operator -of Argonne National Laboratory. +Copyright UChicago Argonne LLC, as Operator of Argonne +National Laboratory. Copyright (c) 1991-2003 The Regents of the University of California, as Operator of Los Alamos National Laboratory. -EPICS Base Versions 3.13.7 and higher are distributed -subject to a Software License Agreement found in the -file LICENSE that is included with this distribution. +EPICS Base is distributed subject to a Software License +Agreement found in the file LICENSE that is included with +this distribution. --------------------------------------------------------- diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html index ede1424cb..2f8a61883 100644 --- a/documentation/KnownProblems.html +++ b/documentation/KnownProblems.html @@ -4,29 +4,37 @@ - Known Problems in R3.16.0 + Known Problems in R3.16.0.1 -

EPICS Base R3.16.0: Known Problems

+

EPICS Base R3.16.0.1: Known Problems

Any patch files linked below should be applied at the root of the -base-3.16.0 tree. Download them, then use the GNU Patch program as +base-3.16.0.1 tree. Download them, then use the GNU Patch program as follows:

-
% cd /path/to/base-3.16.0
+
% cd /path/to/base-3.16.0.1
 % patch -p0 < /path/to/file.patch
-

The following significant problems have been reported with this -version of EPICS Base:

+

The following problems were known by the developers at the time of this +release:

    - +
  • IOCs running on some versions of Cygwin may display warnings at iocInit + about duplicate EPICS CA Address list entries. These warnings might be due + to a bug in Cygwin; they are benign and can be ignored.
  • + +
  • 64-bit Windows builds of the CAS library may not work with some compilers. + The code in src/legacy/gdd is incompatible with the LLP64 model + that Windows uses for its 64-bit ABI.
  • +
diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html index dca8aa25a..62b1b9d0e 100644 --- a/documentation/ReleaseChecklist.html +++ b/documentation/ReleaseChecklist.html @@ -39,7 +39,7 @@

This document describes the procedures and provides a checklist of tasks -that should be performed when creating new releases of EPICS Base.

+that should be performed when creating production releases of EPICS Base.

The Release Process

@@ -141,17 +141,17 @@ relevent roles unless the Release Manager designates otherwise:

Tag the module in Bazaar, using these tag conventions:
  • - R3.16.0-pren + R3.16.1-pren — pre-release tag
  • - R3.16.0-rcn + R3.16.1-rcn — release candidate tag, note the rc is now lower-case
cd ~/base/mirror-3.16
- bzr tag R3.16.0-rcn + bzr tag R3.16.1-rcn
@@ -163,9 +163,9 @@ relevent roles unless the Release Manager designates otherwise:

cd ~/base
bzr export --keywords=publish - --root=base-3.16.0-rcn - -r tag:R3.16.0-rcn - base-3.16.0-rcn.tar.gz + --root=base-3.16.1-rcn + -r tag:R3.16.1-rcn + base-3.16.1-rcn.tar.gz mirror-3.16
This requires that the Bazaar keywords plugin is installed and @@ -280,7 +280,7 @@ relevent roles unless the Release Manager designates otherwise:

Tag the module in Bazaar:
cd ~/base/mirror-3.16
- bzr tag R3.16.0 + bzr tag R3.16.1
@@ -292,9 +292,9 @@ relevent roles unless the Release Manager designates otherwise:

cd ~/base
bzr export --keywords=publish - --root=base-3.16.0 - -r tag:R3.16.0 - base-3.16.0.tar.gz + --root=base-3.16.1 + -r tag:R3.16.1 + base-3.16.1.tar.gz mirror-3.16
This requires that the Bazaar keywords plugin is installed and From 82042e270effef3dd42c2a06b29953327f1ec36f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 3 Mar 2016 17:36:43 -0600 Subject: [PATCH 195/204] CONFIG_BASE_VERSION for R3.16.0.1 --- configure/CONFIG_BASE_VERSION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index a87b2ce72..983d44cd8 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -44,10 +44,10 @@ EPICS_MODIFICATION = 0 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included if zero -EPICS_PATCH_LEVEL = 0 +EPICS_PATCH_LEVEL = 1 # This will end in -DEV between official releases -EPICS_DEV_SNAPSHOT=-DEV +#EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-pre1 #EPICS_DEV_SNAPSHOT=-pre1-DEV #EPICS_DEV_SNAPSHOT=-pre2 @@ -56,7 +56,7 @@ EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-rc1-DEV #EPICS_DEV_SNAPSHOT=-rc2 #EPICS_DEV_SNAPSHOT=-rc2-DEV -#EPICS_DEV_SNAPSHOT= +EPICS_DEV_SNAPSHOT= # No changes should be needed below here From fbf6b6d3e5e82e4935f9ae3b6c8376ae73538ab6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 3 Mar 2016 17:41:58 -0600 Subject: [PATCH 196/204] The usual post-tag updates --- configure/CONFIG_BASE_VERSION | 4 ++-- documentation/RELEASE_NOTES.html | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index 983d44cd8..d41fe95e2 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -47,7 +47,7 @@ EPICS_MODIFICATION = 0 EPICS_PATCH_LEVEL = 1 # This will end in -DEV between official releases -#EPICS_DEV_SNAPSHOT=-DEV +EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-pre1 #EPICS_DEV_SNAPSHOT=-pre1-DEV #EPICS_DEV_SNAPSHOT=-pre2 @@ -56,7 +56,7 @@ EPICS_PATCH_LEVEL = 1 #EPICS_DEV_SNAPSHOT=-rc1-DEV #EPICS_DEV_SNAPSHOT=-rc2 #EPICS_DEV_SNAPSHOT=-rc2-DEV -EPICS_DEV_SNAPSHOT= +#EPICS_DEV_SNAPSHOT= # No changes should be needed below here diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 59e5e5cd5..ea511d5e9 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -3,11 +3,13 @@ - EPICS Base R3.16.0.1 Release Notes + EPICS Base R3.16.0.2 Release Notes -

EPICS Base Release 3.16.0.1

+

EPICS Base Release 3.16.0.2

+ +

This version of EPICS Base has not been released yet.

Changes made on the 3.16 branch since 3.15.3

+

Changes pulled from the 3.15 branch since 3.16.0.1

+ + +

Changes pulled from the 3.14 branch since 3.16.0.1

+ + + +
+ +

Changes in 3.16.0.1 made since 3.15.3

+

Build support for CapFast and dbst removed

The build rules associated with the CapFast-related tools sch2edif @@ -126,7 +139,6 @@ of its CALLBACK objects.

Changes pulled from the 3.15 branch since 3.15.3

-

CA server configuration changes

@@ -183,7 +195,6 @@ dbQuietMacroWarnings=1 VxWorks

Changes pulled from the 3.14 branch since 3.15.3

-

RTEMS NTP Support Issue

From 0fd07d1632ba42e537621a337e970267441beb07 Mon Sep 17 00:00:00 2001 From: Keenan Lang Date: Fri, 4 Mar 2016 16:08:05 -0600 Subject: [PATCH 197/204] Updated iocsh to allow user to select that lines from included scripts not be echoed. --- src/libCom/iocsh/iocsh.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp index d8701532e..6c87aae25 100644 --- a/src/libCom/iocsh/iocsh.cpp +++ b/src/libCom/iocsh/iocsh.cpp @@ -587,6 +587,8 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) wasOkToBlock = epicsThreadIsOkToBlock(); epicsThreadSetOkToBlock(1); for (;;) { + bool echo = true; + /* * Read a line */ @@ -608,14 +610,23 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) while ((c = raw[icin]) && isspace(c)) { icin++; } - + + /* + * @ stops the echoing of a line + */ + if (c == '@') { + echo = false; + icin++; + c = raw[icin]; + } + /* * Ignore comment lines other than to echo * them if they came from a script. This * avoids macLib errors from comments. */ if (c == '#') { - if ((prompt == NULL) && (commandLine == NULL)) + if ((prompt == NULL) && (commandLine == NULL) && echo) puts(raw); continue; } @@ -633,11 +644,20 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) while ((c = line[icin]) && isspace(c)) { icin++; } - + + /* + * @ stops the echoing of a line + */ + if (c == '@') { + echo = false; + icin++; + c = raw[icin]; + } + /* * Echo non-empty lines read from a script */ - if ((prompt == NULL) && *line && (commandLine == NULL)) + if ((prompt == NULL) && *line && (commandLine == NULL) && echo) puts(line); /* From e0b578aff5b2121572617d1816c887d81b09fbb5 Mon Sep 17 00:00:00 2001 From: Keenan Lang Date: Mon, 7 Mar 2016 14:12:04 -0600 Subject: [PATCH 198/204] Eliminated @-sign echo disabling, replaced with ability to disable comment echoing with '#-' --- src/libCom/iocsh/iocsh.cpp | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp index 6c87aae25..c2dde85ed 100644 --- a/src/libCom/iocsh/iocsh.cpp +++ b/src/libCom/iocsh/iocsh.cpp @@ -587,7 +587,6 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) wasOkToBlock = epicsThreadIsOkToBlock(); epicsThreadSetOkToBlock(1); for (;;) { - bool echo = true; /* * Read a line @@ -611,23 +610,15 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) icin++; } - /* - * @ stops the echoing of a line - */ - if (c == '@') { - echo = false; - icin++; - c = raw[icin]; - } - /* * Ignore comment lines other than to echo - * them if they came from a script. This - * avoids macLib errors from comments. + * them if they came from a script (disable echoing + * with '#-'). This avoids macLib errors from comments. */ if (c == '#') { - if ((prompt == NULL) && (commandLine == NULL) && echo) - puts(raw); + if ((prompt == NULL) && (commandLine == NULL)) + if (raw[icin + 1] != '-') + puts(raw); continue; } @@ -646,19 +637,12 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) } /* - * @ stops the echoing of a line + * Echo non-empty lines read from a script. + * Comments delineated with '#-' aren't echoed. */ - if (c == '@') { - echo = false; - icin++; - c = raw[icin]; - } - - /* - * Echo non-empty lines read from a script - */ - if ((prompt == NULL) && *line && (commandLine == NULL) && echo) - puts(line); + if ((prompt == NULL) && *line && (commandLine == NULL)) + if ((c != '#') || (line[icin + 1] != '-')) + puts(line); /* * Ignore lines that became a comment or empty after macro expansion From f0b5b52cef758acc4c0873c3fb1fc211aca875af Mon Sep 17 00:00:00 2001 From: Keenan Lang Date: Mon, 7 Mar 2016 14:14:33 -0600 Subject: [PATCH 199/204] Whitespace --- src/libCom/iocsh/iocsh.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp index c2dde85ed..c5b629ba6 100644 --- a/src/libCom/iocsh/iocsh.cpp +++ b/src/libCom/iocsh/iocsh.cpp @@ -587,7 +587,6 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) wasOkToBlock = epicsThreadIsOkToBlock(); epicsThreadSetOkToBlock(1); for (;;) { - /* * Read a line */ @@ -609,7 +608,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) while ((c = raw[icin]) && isspace(c)) { icin++; } - + /* * Ignore comment lines other than to echo * them if they came from a script (disable echoing @@ -635,7 +634,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) while ((c = line[icin]) && isspace(c)) { icin++; } - + /* * Echo non-empty lines read from a script. * Comments delineated with '#-' aren't echoed. From 92bd217ade5a6ff64462e63892288f2091f85f84 Mon Sep 17 00:00:00 2001 From: Keenan Lang Date: Mon, 7 Mar 2016 14:38:59 -0600 Subject: [PATCH 200/204] Updating example iocsh startup scripts --- .../base/top/exampleBoot/ioc/st.cmd@Common | 8 +-- .../base/top/exampleBoot/ioc/st.cmd@RTEMS | 10 ++-- .../base/top/exampleBoot/ioc/st.cmd@vxWorks | 14 ++--- .../base/top/exampleBoot/nfsCommands@RTEMS | 52 ++++++++--------- .../base/top/exampleBoot/nfsCommands@vxWorks | 58 +++++++++---------- .../base/top/iocBoot/ioc/st.cmd@Common | 4 +- .../base/top/iocBoot/ioc/st.cmd@Cross | 4 +- .../base/top/iocBoot/ioc/st.cmd@RTEMS | 6 +- .../base/top/iocBoot/ioc/st.cmd@vxWorks | 6 +- .../base/top/iocBoot/nfsCommands@RTEMS | 58 +++++++++---------- .../base/top/iocBoot/nfsCommands@vxWorks | 58 +++++++++---------- 11 files changed, 139 insertions(+), 139 deletions(-) diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@Common b/src/template/base/top/exampleBoot/ioc/st.cmd@Common index a2d018e3a..b2dfb4ae4 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@Common +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@Common @@ -1,7 +1,7 @@ #!../../bin/_ARCH_/_APPNAME_ -## You may have to change _APPNAME_ to something else -## everywhere it appears in this file +#- You may have to change _APPNAME_ to something else +#- everywhere it appears in this file < envPaths @@ -16,10 +16,10 @@ dbLoadTemplate "db/user.substitutions" dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" -## Set this to see messages from mySub +#- Set this to see messages from mySub #var mySubDebug 1 -## Run this to trace the stages of iocInit +#- Run this to trace the stages of iocInit #traceIocInit cd "${TOP}/iocBoot/${IOC}" diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS index cc96f84ab..87e9e3b13 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS @@ -1,7 +1,7 @@ -## Example RTEMS startup script +#- Example RTEMS startup script -## You may have to change _APPNAME_ to something else -## everywhere it appears in this file +#- You may have to change _APPNAME_ to something else +#- everywhere it appears in this file #< envPaths @@ -14,10 +14,10 @@ dbLoadTemplate("db/user.substitutions") dbLoadRecords("db/_APPNAME_Version.db", "user=_USER_") dbLoadRecords("db/dbSubExample.db", "user=_USER_") -## Set this to see messages from mySub +#- Set this to see messages from mySub #var mySubDebug 1 -## Run this to trace the stages of iocInit +#- Run this to trace the stages of iocInit #traceIocInit iocInit diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks index 44a9afc67..617ba6111 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks @@ -1,7 +1,7 @@ -## Example vxWorks startup file +#- Example vxWorks startup file -## The following is needed if your board support package doesn't at boot time -## automatically cd to the directory containing its startup script +#- The following is needed if your board support package doesn't at boot time +#- automatically cd to the directory containing its startup script #cd "_TOP_/iocBoot/_IOC_" < cdCommands @@ -9,8 +9,8 @@ cd topbin -## You may have to change _APPNAME_ to something else -## everywhere it appears in this file +#- You may have to change _APPNAME_ to something else +#- everywhere it appears in this file ld 0,0, "_APPNAME_.munch" ## Register all support components @@ -23,10 +23,10 @@ dbLoadTemplate "db/user.substitutions" dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" -## Set this to see messages from mySub +#- Set this to see messages from mySub #mySubDebug = 1 -## Run this to trace the stages of iocInit +#- Run this to trace the stages of iocInit #traceIocInit cd startup diff --git a/src/template/base/top/exampleBoot/nfsCommands@RTEMS b/src/template/base/top/exampleBoot/nfsCommands@RTEMS index 0ba95a6d0..18ae461fa 100644 --- a/src/template/base/top/exampleBoot/nfsCommands@RTEMS +++ b/src/template/base/top/exampleBoot/nfsCommands@RTEMS @@ -1,26 +1,26 @@ -#Instructions for creating and using a real nfsCommands file -# -# in order to use nfs do the following: -# 1) Create hostAdd and nfsMount commands for each nfs server -# 2) In each st.cmd file add the following two commands BEFORE any load commands -# ../nfs.cmd -# cd " -# -# The hostAdd command has the form: -# hostAdd("","xxx.xxx.xxx.xxx") -# -# You can also mount subdirectories as follows: -# nfsMount("", "/xxx/xxx/xxx", "/xxx") -# -# For example assume -# -# host is mercury with inet address 155.77.2.56 -# You want to mount the directory (which is a file system of mercury) -# /home/mercury5/iocinfo -# as -# /iocinfo -# -# The commands would be -# -# hostAdd("mercury","155.77.2.56") -# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") +#- Instructions for creating and using a real nfsCommands file +#- +#- in order to use nfs do the following: +#- 1) Create hostAdd and nfsMount commands for each nfs server +#- 2) In each st.cmd file add the following two commands BEFORE any load commands +#- ../nfs.cmd +#- cd " +#- +#- The hostAdd command has the form: +#- hostAdd("","xxx.xxx.xxx.xxx") +#- +#- You can also mount subdirectories as follows: +#- nfsMount("", "/xxx/xxx/xxx", "/xxx") +#- +#- For example assume +#- +#- host is mercury with inet address 155.77.2.56 +#- You want to mount the directory (which is a file system of mercury) +#- /home/mercury5/iocinfo +#- as +#- /iocinfo +#- +#- The commands would be +#- +#- hostAdd("mercury","155.77.2.56") +#- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") diff --git a/src/template/base/top/exampleBoot/nfsCommands@vxWorks b/src/template/base/top/exampleBoot/nfsCommands@vxWorks index 7cb8232f0..eb302c569 100644 --- a/src/template/base/top/exampleBoot/nfsCommands@vxWorks +++ b/src/template/base/top/exampleBoot/nfsCommands@vxWorks @@ -1,29 +1,29 @@ -#Instructions for creating and using a real nfsCommands file -# -# in order to use nfs do the following: -# 1) Create hostAdd and nfsMount commands for each nfs server -# 2) In each st.cmd file add the following two commands BEFORE any load commands -# ../nfs.cmd -# cd " -# -# The hostAdd command has the form: -# hostAdd("","xxx.xxx.xxx.xxx") -# -# The nfsMount command has the form: -# nfsMount("", "/xxx/xxx/xxx", "/xxx") -# -# You can also mount subdirectories as follows: -# nfsMountAll("") -# -# For example assume -# -# host is mercury with inet address 155.77.2.56 -# You want to mount the directory (which is a file system of mercury) -# /home/mercury5/iocinfo -# as -# /iocinfo -# -# The commands would be -# -# hostAdd("mercury","155.77.2.56") -# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") +#- Instructions for creating and using a real nfsCommands file +#- +#- in order to use nfs do the following: +#- 1) Create hostAdd and nfsMount commands for each nfs server +#- 2) In each st.cmd file add the following two commands BEFORE any load commands +#- ../nfs.cmd +#- cd " +#- +#- The hostAdd command has the form: +#- hostAdd("","xxx.xxx.xxx.xxx") +#- +#- The nfsMount command has the form: +#- nfsMount("", "/xxx/xxx/xxx", "/xxx") +#- +#- You can also mount subdirectories as follows: +#- nfsMountAll("") +#- +#- For example assume +#- +#- host is mercury with inet address 155.77.2.56 +#- You want to mount the directory (which is a file system of mercury) +#- /home/mercury5/iocinfo +#- as +#- /iocinfo +#- +#- The commands would be +#- +#- hostAdd("mercury","155.77.2.56") +#- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") diff --git a/src/template/base/top/iocBoot/ioc/st.cmd@Common b/src/template/base/top/iocBoot/ioc/st.cmd@Common index ba71fbf81..dd0811dfb 100644 --- a/src/template/base/top/iocBoot/ioc/st.cmd@Common +++ b/src/template/base/top/iocBoot/ioc/st.cmd@Common @@ -1,7 +1,7 @@ #!../../bin/_ARCH_/_APPNAME_ -## You may have to change _APPNAME_ to something else -## everywhere it appears in this file +#- You may have to change _APPNAME_ to something else +#- everywhere it appears in this file < envPaths diff --git a/src/template/base/top/iocBoot/ioc/st.cmd@Cross b/src/template/base/top/iocBoot/ioc/st.cmd@Cross index 59e94ea2d..f42a26c1b 100644 --- a/src/template/base/top/iocBoot/ioc/st.cmd@Cross +++ b/src/template/base/top/iocBoot/ioc/st.cmd@Cross @@ -1,7 +1,7 @@ #!../../bin/_ARCH_/_APPNAME_ -## You may have to change _APPNAME_ to something else -## everywhere it appears in this file +#- You may have to change _APPNAME_ to something else +#- everywhere it appears in this file #< envPaths diff --git a/src/template/base/top/iocBoot/ioc/st.cmd@RTEMS b/src/template/base/top/iocBoot/ioc/st.cmd@RTEMS index f7c3d164f..a7d08fbcf 100644 --- a/src/template/base/top/iocBoot/ioc/st.cmd@RTEMS +++ b/src/template/base/top/iocBoot/ioc/st.cmd@RTEMS @@ -1,7 +1,7 @@ -## Example RTEMS startup script +#- Example RTEMS startup script -## You may have to change _APPNAME_ to something else -## everywhere it appears in this file +#- You may have to change _APPNAME_ to something else +#- everywhere it appears in this file #< envPaths diff --git a/src/template/base/top/iocBoot/ioc/st.cmd@vxWorks b/src/template/base/top/iocBoot/ioc/st.cmd@vxWorks index 04db4150f..43287d68b 100644 --- a/src/template/base/top/iocBoot/ioc/st.cmd@vxWorks +++ b/src/template/base/top/iocBoot/ioc/st.cmd@vxWorks @@ -1,7 +1,7 @@ -## Example vxWorks startup file +#- Example vxWorks startup file -## The following is needed if your board support package doesn't at boot time -## automatically cd to the directory containing its startup script +#- The following is needed if your board support package doesn't at boot time +#- automatically cd to the directory containing its startup script #cd "_TOP_/iocBoot/_IOC_" < cdCommands diff --git a/src/template/base/top/iocBoot/nfsCommands@RTEMS b/src/template/base/top/iocBoot/nfsCommands@RTEMS index 62221da7c..dd8811319 100644 --- a/src/template/base/top/iocBoot/nfsCommands@RTEMS +++ b/src/template/base/top/iocBoot/nfsCommands@RTEMS @@ -1,29 +1,29 @@ -#Instructions for creating and using a real nfsCommands file -# -# in order to use nfs do the following: -# 1) Create hostAdd and nfsMount commands for each nfs server -# 2) In each st.cmd file add the following two commands BEFORE any load commands -# ../nfs.cmd -# cd " -# -# The hostAdd command has the form: -# hostAdd("","xxx.xxx.xxx.xxx") -# -# The vxWorks nfsMount command has the form: -# nfsMount("") -# -# You can also mount subdirectories as follows: -# nfsMount("", "/xxx/xxx/xxx", "/xxx") -# -# For example assume -# -# host is mercury with inet address 155.77.2.56 -# You want to mount the directory (which is a file system of mercury) -# /home/mercury5/iocinfo -# as -# /iocinfo -# -# The commands would be -# -# hostAdd("mercury","155.77.2.56") -# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") +#- Instructions for creating and using a real nfsCommands file +#- +#- in order to use nfs do the following: +#- 1) Create hostAdd and nfsMount commands for each nfs server +#- 2) In each st.cmd file add the following two commands BEFORE any load commands +#- ../nfs.cmd +#- cd " +#- +#- The hostAdd command has the form: +#- hostAdd("","xxx.xxx.xxx.xxx") +#- +#- The vxWorks nfsMount command has the form: +#- nfsMount("") +#- +#- You can also mount subdirectories as follows: +#- nfsMount("", "/xxx/xxx/xxx", "/xxx") +#- +#- For example assume +#- +#- host is mercury with inet address 155.77.2.56 +#- You want to mount the directory (which is a file system of mercury) +#- /home/mercury5/iocinfo +#- as +#- /iocinfo +#- +#- The commands would be +#- +#- hostAdd("mercury","155.77.2.56") +#- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") diff --git a/src/template/base/top/iocBoot/nfsCommands@vxWorks b/src/template/base/top/iocBoot/nfsCommands@vxWorks index 62221da7c..dd8811319 100644 --- a/src/template/base/top/iocBoot/nfsCommands@vxWorks +++ b/src/template/base/top/iocBoot/nfsCommands@vxWorks @@ -1,29 +1,29 @@ -#Instructions for creating and using a real nfsCommands file -# -# in order to use nfs do the following: -# 1) Create hostAdd and nfsMount commands for each nfs server -# 2) In each st.cmd file add the following two commands BEFORE any load commands -# ../nfs.cmd -# cd " -# -# The hostAdd command has the form: -# hostAdd("","xxx.xxx.xxx.xxx") -# -# The vxWorks nfsMount command has the form: -# nfsMount("") -# -# You can also mount subdirectories as follows: -# nfsMount("", "/xxx/xxx/xxx", "/xxx") -# -# For example assume -# -# host is mercury with inet address 155.77.2.56 -# You want to mount the directory (which is a file system of mercury) -# /home/mercury5/iocinfo -# as -# /iocinfo -# -# The commands would be -# -# hostAdd("mercury","155.77.2.56") -# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") +#- Instructions for creating and using a real nfsCommands file +#- +#- in order to use nfs do the following: +#- 1) Create hostAdd and nfsMount commands for each nfs server +#- 2) In each st.cmd file add the following two commands BEFORE any load commands +#- ../nfs.cmd +#- cd " +#- +#- The hostAdd command has the form: +#- hostAdd("","xxx.xxx.xxx.xxx") +#- +#- The vxWorks nfsMount command has the form: +#- nfsMount("") +#- +#- You can also mount subdirectories as follows: +#- nfsMount("", "/xxx/xxx/xxx", "/xxx") +#- +#- For example assume +#- +#- host is mercury with inet address 155.77.2.56 +#- You want to mount the directory (which is a file system of mercury) +#- /home/mercury5/iocinfo +#- as +#- /iocinfo +#- +#- The commands would be +#- +#- hostAdd("mercury","155.77.2.56") +#- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") From 5c93eb60491995602785a685dec487724313e9fd Mon Sep 17 00:00:00 2001 From: Keenan Lang Date: Tue, 15 Mar 2016 16:04:57 -0500 Subject: [PATCH 201/204] Updated Changelog --- documentation/RELEASE_NOTES.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index ea511d5e9..7070412e8 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -31,6 +31,12 @@

Changes in 3.16.0.1 made since 3.15.3

+

Echoless comments

+ +

The way comments are parsed by the iocsh interpreter has changed. The +interpreter can be selectively disabled from echoing comments coming from +a script by starting those lines with '#-' rather than just '#'.

+

Build support for CapFast and dbst removed

The build rules associated with the CapFast-related tools sch2edif From 7d94d05bb71520a27e8d41cd764a5e54a56b02ce Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 19 Mar 2016 17:15:08 -0400 Subject: [PATCH 202/204] iocInit: special clear link set in doCloseLinks() --- src/ioc/misc/iocInit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 3006fbd05..ac3aba1a6 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -647,6 +647,7 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, /* free link, but don't split lockset like dbDbRemoveLink() */ free(plink->value.pv_link.pvt); plink->type = PV_LINK; + plink->lset = NULL; } } From 45db78981ceea313e90bc065c593cad44ec209a5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 20 Mar 2016 09:43:40 -0400 Subject: [PATCH 203/204] iocInit: no need to break DB_LINK when isolated This is being done to free the link private struct, but dbDbRemoveLink() is not used to avoid the overhead of splitting every lockset just before the PDB is free'd. No reason to do this for non-isolated until scans threads are stopped. --- src/ioc/misc/iocInit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index ac3aba1a6..ba0ea345d 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -643,7 +643,7 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, } dbCaRemoveLink(NULL, plink); - } else if (plink->type == DB_LINK) { + } else if (iocBuildMode==buildIsolated && plink->type == DB_LINK) { /* free link, but don't split lockset like dbDbRemoveLink() */ free(plink->value.pv_link.pvt); plink->type = PV_LINK; From 7a1766279c0e4291745d2bc1bc3d23bea0e042ad Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 Mar 2016 13:42:49 -0400 Subject: [PATCH 204/204] dbLock: dbLockerAlloc() accept const array of non-const pointers --- src/ioc/db/dbLock.c | 4 ++-- src/ioc/db/dbLock.h | 2 +- src/ioc/db/dbLockPvt.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index c36301d35..d0f266795 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -328,7 +328,7 @@ int dbLockUpdateRefs(dbLocker *locker, int update) } void dbLockerPrepare(struct dbLocker *locker, - struct dbCommon **precs, + struct dbCommon * const *precs, size_t nrecs) { size_t i; @@ -348,7 +348,7 @@ void dbLockerPrepare(struct dbLocker *locker, dbLockUpdateRefs(locker, 1); } -dbLocker *dbLockerAlloc(dbCommon **precs, +dbLocker *dbLockerAlloc(dbCommon * const *precs, size_t nrecs, unsigned int flags) { diff --git a/src/ioc/db/dbLock.h b/src/ioc/db/dbLock.h index 9f2714057..1d6388ed3 100644 --- a/src/ioc/db/dbLock.h +++ b/src/ioc/db/dbLock.h @@ -27,7 +27,7 @@ typedef struct dbLocker dbLocker; epicsShareFunc void dbScanLock(struct dbCommon *precord); epicsShareFunc void dbScanUnlock(struct dbCommon *precord); -epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon **precs, +epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon * const *precs, size_t nrecs, unsigned int flags); diff --git a/src/ioc/db/dbLockPvt.h b/src/ioc/db/dbLockPvt.h index 8e19f3f31..f90d00617 100644 --- a/src/ioc/db/dbLockPvt.h +++ b/src/ioc/db/dbLockPvt.h @@ -96,7 +96,7 @@ epicsShareFunc void dbLockDecRef(lockSet *ls); * nrecs must be <=DBLOCKER_NALLOC. */ void dbLockerPrepare(struct dbLocker *locker, - struct dbCommon **precs, + struct dbCommon * const *precs, size_t nrecs); void dbLockerFinalize(dbLocker *);