diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index d91e74bd9..b9ba24961 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -15,6 +15,21 @@ 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

+

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 diff --git a/src/libCom/error/errlog.c b/src/libCom/error/errlog.c index c8ec6ce64..05df1bc0d 100644 --- a/src/libCom/error/errlog.c +++ b/src/libCom/error/errlog.c @@ -81,11 +81,12 @@ static struct { int msgNeeded; int sevToLog; int toConsole; + FILE *console; int missedMessages; char *pbuffer; } pvtData; - + /* * vsnprintf with truncation message */ @@ -114,17 +115,24 @@ epicsShareFunc int errlogPrintf(const char *pFormat, ...) ("errlogPrintf called from interrupt level\n"); return 0; } - isOkToBlock = epicsThreadIsOkToBlock(); + errlogInit(0); + isOkToBlock = epicsThreadIsOkToBlock(); + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { va_start(pvar, pFormat); - nchar = vfprintf(stderr, pFormat, pvar); + nchar = vfprintf(pvtData.console, pFormat, pvar); va_end (pvar); - fflush(stderr); + fflush(pvtData.console); } - if (pvtData.atExit) return nchar; + + if (pvtData.atExit) + return nchar; + pbuffer = msgbufGetFree(isOkToBlock); - if (!pbuffer) return 0; + if (!pbuffer) + return 0; + va_start(pvar, pFormat); nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); va_end(pvar); @@ -144,19 +152,23 @@ epicsShareFunc int errlogVprintf( ("errlogVprintf called from interrupt level\n"); return 0; } + errlogInit(0); - if (pvtData.atExit) return 0; + if (pvtData.atExit) + return 0; isOkToBlock = epicsThreadIsOkToBlock(); + pbuffer = msgbufGetFree(isOkToBlock); if (!pbuffer) { - vfprintf(stderr, pFormat, pvar); - fflush(stderr); + vfprintf(pvtData.console, pFormat, pvar); + fflush(pvtData.console); return 0; } + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { - fprintf(stderr, "%s", pbuffer); - fflush(stderr); + fprintf(pvtData.console, "%s", pbuffer); + fflush(pvtData.console); } msgbufSetSize(nchar); return nchar; @@ -178,6 +190,7 @@ epicsShareFunc int errlogPrintfNoConsole( const char *pFormat, ...) ("errlogPrintfNoConsole called from interrupt level\n"); return 0; } + errlogInit(0); va_start(pvar, pFormat); nchar = errlogVprintfNoConsole(pFormat, pvar); @@ -196,15 +209,21 @@ epicsShareFunc int errlogVprintfNoConsole( ("errlogVprintfNoConsole called from interrupt level\n"); return 0; } + errlogInit(0); - if (pvtData.atExit) return 0; + if (pvtData.atExit) + return 0; + pbuffer = msgbufGetFree(1); - if (!pbuffer) return 0; + if (!pbuffer) + return 0; + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); msgbufSetSize(nchar); return nchar; } - + + epicsShareFunc int errlogSevPrintf( const errlogSevEnum severity,const char *pFormat, ...) { @@ -217,16 +236,20 @@ epicsShareFunc int errlogSevPrintf( ("errlogSevPrintf called from interrupt level\n"); return 0; } + errlogInit(0); - if (pvtData.sevToLog>severity) return 0; + if (pvtData.sevToLog > severity) + return 0; + isOkToBlock = epicsThreadIsOkToBlock(); if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { - fprintf(stderr, "sevr=%s ", errlogGetSevEnumString(severity)); + fprintf(pvtData.console, "sevr=%s ", errlogGetSevEnumString(severity)); va_start(pvar, pFormat); - vfprintf(stderr, pFormat, pvar); + vfprintf(pvtData.console, pFormat, pvar); va_end(pvar); - fflush(stderr); + fflush(pvtData.console); } + va_start(pvar, pFormat); nchar = errlogSevVprintf(severity, pFormat, pvar); va_end(pvar); @@ -246,11 +269,16 @@ epicsShareFunc int errlogSevVprintf( ("errlogSevVprintf called from interrupt level\n"); return 0; } + errlogInit(0); - if (pvtData.atExit) return 0; + if (pvtData.atExit) + return 0; + isOkToBlock = epicsThreadIsOkToBlock(); pnext = msgbufGetFree(isOkToBlock); - if (!pnext) return 0; + if (!pnext) + return 0; + nchar = sprintf(pnext, "sevr=%s ", errlogGetSevEnumString(severity)); pnext += nchar; totalChar += nchar; nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pFormat, pvar); @@ -262,12 +290,14 @@ epicsShareFunc int errlogSevVprintf( msgbufSetSize(totalChar); return nchar; } - + + epicsShareFunc char * epicsShareAPI errlogGetSevEnumString( const errlogSevEnum severity) { errlogInit(0); - if (severity > 3) return "unknown"; + if (severity > 3) + return "unknown"; return errlogSevEnumString[severity]; } @@ -290,7 +320,9 @@ epicsShareFunc void epicsShareAPI errlogAddListener( listenerNode *plistenerNode; errlogInit(0); - if (pvtData.atExit) return; + if (pvtData.atExit) + return; + plistenerNode = callocMustSucceed(1,sizeof(listenerNode), "errlogAddListener"); epicsMutexMustLock(pvtData.listenerLock); @@ -306,7 +338,9 @@ epicsShareFunc void epicsShareAPI errlogRemoveListener( listenerNode *plistenerNode; errlogInit(0); - if (!pvtData.atExit) epicsMutexMustLock(pvtData.listenerLock); + if (!pvtData.atExit) + epicsMutexMustLock(pvtData.listenerLock); + plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); while (plistenerNode) { if (plistenerNode->listener==listener) { @@ -316,9 +350,13 @@ epicsShareFunc void epicsShareAPI errlogRemoveListener( } plistenerNode = (listenerNode *)ellNext(&plistenerNode->node); } - if (!pvtData.atExit) epicsMutexUnlock(pvtData.listenerLock); + + if (!pvtData.atExit) + epicsMutexUnlock(pvtData.listenerLock); + if (!plistenerNode) { - fprintf(stderr, "errlogRemoveListener did not find listener\n"); + fprintf(pvtData.console, + "errlogRemoveListener did not find listener\n"); } } @@ -328,9 +366,16 @@ epicsShareFunc int epicsShareAPI eltc(int yesno) pvtData.toConsole = yesno; return 0; } - + +epicsShareFunc int errlogSetConsole(FILE *stream) +{ + errlogInit(0); + pvtData.console = stream ? stream : stderr; + return 0; +} + epicsShareFunc void errPrintf(long status, const char *pFileName, - int lineno, const char *pformat, ...) + int lineno, const char *pformat, ...) { va_list pvar; char *pnext; @@ -343,30 +388,43 @@ epicsShareFunc void errPrintf(long status, const char *pFileName, epicsInterruptContextMessage("errPrintf called from interrupt level\n"); return; } + errlogInit(0); isOkToBlock = epicsThreadIsOkToBlock(); - if (status == 0) status = errno; + if (status == 0) + status = errno; + if (status > 0) { errSymLookup(status, name, sizeof(name)); } + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { - if (pFileName) fprintf(stderr, "filename=\"%s\" line number=%d\n", - pFileName, lineno); - if (status>0) fprintf(stderr, "%s ", name); + if (pFileName) + fprintf(pvtData.console, "filename=\"%s\" line number=%d\n", + pFileName, lineno); + if (status > 0) + fprintf(pvtData.console, "%s ", name); + va_start(pvar, pformat); - vfprintf(stderr, pformat, pvar); + vfprintf(pvtData.console, pformat, pvar); va_end(pvar); - fputc('\n', stderr); - fflush(stderr); + fputc('\n', pvtData.console); + fflush(pvtData.console); } - if (pvtData.atExit) return; + + if (pvtData.atExit) + return; + pnext = msgbufGetFree(isOkToBlock); - if (!pnext) return; + if (!pnext) + return; + if (pFileName) { nchar = sprintf(pnext,"filename=\"%s\" line number=%d\n", pFileName, lineno); pnext += nchar; totalChar += nchar; } + if (status > 0) { nchar = sprintf(pnext,"%s ",name); pnext += nchar; totalChar += nchar; @@ -382,7 +440,8 @@ epicsShareFunc void errPrintf(long status, const char *pFileName, totalChar++ ; /*include the \n */ msgbufSetSize(totalChar); } - + + static void exitHandler(void *pvt) { pvtData.atExit = 1; @@ -417,6 +476,7 @@ static void errlogInitPvt(void *arg) ellInit(&pvtData.listenerList); ellInit(&pvtData.msgQueue); pvtData.toConsole = TRUE; + pvtData.console = stderr; pvtData.waitForWork = epicsEventMustCreate(epicsEventEmpty); pvtData.listenerLock = epicsMutexMustCreate(); pvtData.msgQueueLock = epicsMutexMustCreate(); @@ -436,7 +496,8 @@ static void errlogInitPvt(void *arg) pvtData.errlogInitFailed = FALSE; } } - + + epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize) { static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT; @@ -445,31 +506,42 @@ epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize) if (pvtData.atExit) return 0; - if (bufsize < BUFFER_SIZE) bufsize = BUFFER_SIZE; + if (bufsize < BUFFER_SIZE) + bufsize = BUFFER_SIZE; config.bufsize = bufsize; - if (maxMsgSize < MAX_MESSAGE_SIZE) maxMsgSize = MAX_MESSAGE_SIZE; + + if (maxMsgSize < MAX_MESSAGE_SIZE) + maxMsgSize = MAX_MESSAGE_SIZE; config.maxMsgSize = maxMsgSize; - epicsThreadOnce(&errlogOnceFlag, errlogInitPvt, (void *)&config); + + epicsThreadOnce(&errlogOnceFlag, errlogInitPvt, &config); if (pvtData.errlogInitFailed) { fprintf(stderr,"errlogInit failed\n"); exit(1); } return 0; } + epicsShareFunc int epicsShareAPI errlogInit(int bufsize) { return errlogInit2(bufsize, MAX_MESSAGE_SIZE); } + epicsShareFunc void epicsShareAPI errlogFlush(void) { int count; + errlogInit(0); - if (pvtData.atExit) return; + if (pvtData.atExit) + return; + /*If nothing in queue dont wake up errlogThread*/ epicsMutexMustLock(pvtData.msgQueueLock); count = ellCount(&pvtData.msgQueue); epicsMutexUnlock(pvtData.msgQueueLock); - if (count <= 0) return; + if (count <= 0) + return; + /*must let errlogThread empty queue*/ epicsMutexMustLock(pvtData.flushLock); epicsEventSignal(pvtData.flush); @@ -490,25 +562,32 @@ static void errlogThread(void) while ((pmessage = msgbufGetSend(&noConsoleMessage))) { epicsMutexMustLock(pvtData.listenerLock); if (pvtData.toConsole && !noConsoleMessage) { - fprintf(stderr,"%s",pmessage); - fflush(stderr); + fprintf(pvtData.console,"%s",pmessage); + fflush(pvtData.console); } + plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); while (plistenerNode) { (*plistenerNode->listener)(plistenerNode->pPrivate, pmessage); plistenerNode = (listenerNode *)ellNext(&plistenerNode->node); } + epicsMutexUnlock(pvtData.listenerLock); msgbufFreeSend(); } - if (pvtData.atExit) break; - if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK) continue; + + if (pvtData.atExit) + break; + if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK) + continue; + epicsThreadSleep(.2); /*just wait an extra .2 seconds*/ epicsEventSignal(pvtData.waitForFlush); } epicsEventSignal(pvtData.waitForExit); } - + + static msgNode *msgbufGetNode(void) { char *pbuffer = pvtData.pbuffer; @@ -517,7 +596,8 @@ static msgNode *msgbufGetNode(void) if (ellCount(&pvtData.msgQueue) == 0 ) { pnextFree = pbuffer; /* Reset if empty */ - } else { + } + else { msgNode *pfirst = (msgNode *)ellFirst(&pvtData.msgQueue); msgNode *plast = (msgNode *)ellLast(&pvtData.msgQueue); char *plimit = pbuffer + pvtData.buffersize; @@ -534,6 +614,7 @@ static msgNode *msgbufGetNode(void) return 0; /* No room */ } } + pnextSend = (msgNode *)pnextFree; pnextSend->message = pnextFree + sizeof(msgNode); pnextSend->length = 0; @@ -557,17 +638,19 @@ static char *msgbufGetFree(int noConsoleMessage) pvtData.missedMessages = 0; ellAdd(&pvtData.msgQueue, &pnextSend->node); } + pvtData.pnextSend = pnextSend = msgbufGetNode(); if (pnextSend) { pnextSend->noConsoleMessage = noConsoleMessage; pnextSend->length = 0; return pnextSend->message; /* NB: msgQueueLock is still locked */ } + ++pvtData.missedMessages; epicsMutexUnlock(pvtData.msgQueueLock); return 0; } - + static void msgbufSetSize(int size) { msgNode *pnextSend = pvtData.pnextSend; @@ -578,10 +661,7 @@ static void msgbufSetSize(int size) epicsEventSignal(pvtData.waitForWork); } -/*errlogThread is the only task that calls msgbufGetSend and msgbufFreeSend*/ -/*Thus errlogThread is the ONLY task that removes messages from msgQueue */ -/*This is why each can lock and unlock msgQueue */ -/*This is necessary to prevent other tasks from waiting for errlogThread */ + static char * msgbufGetSend(int *noConsoleMessage) { msgNode *pnextSend; @@ -589,9 +669,11 @@ static char * msgbufGetSend(int *noConsoleMessage) epicsMutexMustLock(pvtData.msgQueueLock); pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); epicsMutexUnlock(pvtData.msgQueueLock); - if (!pnextSend) return(0); + if (!pnextSend) + return 0; + *noConsoleMessage = pnextSend->noConsoleMessage; - return(pnextSend->message); + return pnextSend->message; } static void msgbufFreeSend(void) @@ -601,7 +683,7 @@ static void msgbufFreeSend(void) epicsMutexMustLock(pvtData.msgQueueLock); pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); if (!pnextSend) { - fprintf(stderr, "errlog: msgbufFreeSend logic error\n"); + fprintf(pvtData.console, "errlog: msgbufFreeSend logic error\n"); epicsThreadSuspendSelf(); } ellDelete(&pvtData.msgQueue, &pnextSend->node); diff --git a/src/libCom/error/errlog.h b/src/libCom/error/errlog.h index f08d4a936..24902d57a 100644 --- a/src/libCom/error/errlog.h +++ b/src/libCom/error/errlog.h @@ -11,6 +11,7 @@ #define INCerrlogh #include +#include #include "shareLib.h" #include "compilerDependencies.h" @@ -46,7 +47,7 @@ epicsShareFunc int errlogSevPrintf( epicsShareFunc int errlogSevVprintf( const errlogSevEnum severity,const char *pformat,va_list pvar); epicsShareFunc int epicsShareAPI errlogMessage( - const char *message); + const char *message); epicsShareFunc char * epicsShareAPI errlogGetSevEnumString( const errlogSevEnum severity); @@ -60,6 +61,8 @@ epicsShareFunc void epicsShareAPI errlogRemoveListener( errlogListener listener); epicsShareFunc int epicsShareAPI eltc(int yesno); +epicsShareFunc int errlogSetConsole(FILE *stream); + epicsShareFunc int epicsShareAPI errlogInit(int bufsize); epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize); epicsShareFunc void epicsShareAPI errlogFlush(void);