Expose callback queue status

This allows tools like iocStats to monitor the queue status of
the callback queues. This fixes lp:1786540.
This commit is contained in:
Martin Konrad
2018-08-10 10:17:13 -04:00
parent 94ebd0fe48
commit 59ec8d897d
9 changed files with 205 additions and 7 deletions

View File

@@ -54,6 +54,7 @@ typedef struct cbQueueSet {
epicsEventId semWakeUp;
epicsRingPointerId queue;
int queueOverflow;
int queueOverflows;
int shutdown;
int threadsConfigured;
int threadsRunning;
@@ -99,10 +100,55 @@ int callbackSetQueueSize(int size)
fprintf(stderr, "Callback system already initialized\n");
return -1;
}
callbackQueueSize = size;
epicsAtomicSetIntT(&callbackQueueSize, size);
return 0;
}
int callbackQueueStatus(const int reset, callbackQueueStats *result)
{
if (!callbackIsInit) return -1;
int ret;
if (result) {
result->size = epicsAtomicGetIntT(&callbackQueueSize);
int prio;
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
epicsRingPointerId qId = callbackQueue[prio].queue;
result->numUsed[prio] = epicsRingPointerGetUsed(qId);
result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId);
result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows);
}
ret = 0;
} else {
ret = -2;
}
if (reset) {
int prio;
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue);
}
}
return ret;
}
void callbackQueuePrintStatus(const int reset)
{
callbackQueueStats stats;
if (callbackQueueStatus(reset, &stats) == -1) {
fprintf(stderr, "Callback system not initialized, yet. Please run "
"iocInit before using this command.\n");
return;
}
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
int prio;
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
double qusage = 100.0 * stats.numUsed[prio] / stats.size;
printf("%8s %15d %10d %6d %6.1f %11d\n", threadNamePrefix[prio],
stats.maxUsed[prio], stats.numUsed[prio], stats.size,
qusage, stats.numOverflow[prio]);
}
}
int callbackParallelThreads(int count, const char *prio)
{
if (callbackIsInit) {
@@ -240,7 +286,7 @@ void callbackInit(void)
epicsThreadId tid;
callbackQueue[i].semWakeUp = epicsEventMustCreate(epicsEventEmpty);
callbackQueue[i].queue = epicsRingPointerLockedCreate(callbackQueueSize);
callbackQueue[i].queue = epicsRingPointerLockedCreate(epicsAtomicGetIntT(&callbackQueueSize));
if (callbackQueue[i].queue == 0)
cantProceed("epicsRingPointerLockedCreate failed for %s\n",
threadNamePrefix[i]);
@@ -290,6 +336,7 @@ int callbackRequest(CALLBACK *pcallback)
if (!pushOK) {
epicsInterruptContextMessage(fullMessage[priority]);
mySet->queueOverflow = TRUE;
epicsAtomicIncrIntT(&mySet->queueOverflows);
return S_db_bufFull;
}
epicsEventSignal(mySet->semWakeUp);

View File

@@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK;
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
typedef struct callbackQueueStats {
int size;
int numUsed[NUM_CALLBACK_PRIORITIES];
int maxUsed[NUM_CALLBACK_PRIORITIES];
int numOverflow[NUM_CALLBACK_PRIORITIES];
} callbackQueueStats;
#define callbackSetCallback(PFUN, PCALLBACK) \
( (PCALLBACK)->callback = (PFUN) )
#define callbackSetPriority(PRIORITY, PCALLBACK) \
@@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback);
epicsShareFunc void callbackRequestProcessCallbackDelayed(
CALLBACK *pCallback, int Priority, void *pRec, double seconds);
epicsShareFunc int callbackSetQueueSize(int size);
epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result);
void callbackQueuePrintStatus(const int reset);
epicsShareFunc int callbackParallelThreads(int count, const char *prio);
#ifdef __cplusplus

View File

@@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
scanOnceSetQueueSize(args[0].ival);
}
/* scanOnceQueueStatus */
static const iocshArg scanOnceQueueStatusArg0 = { "reset",iocshArgInt};
static const iocshArg * const scanOnceQueueStatusArgs[1] =
{&scanOnceQueueStatusArg0};
static const iocshFuncDef scanOnceQueueStatusFuncDef =
{"scanOnceQueueStatus",1,scanOnceQueueStatusArgs};
static void scanOnceQueueStatusCallFunc(const iocshArgBuf *args)
{
scanOnceQueuePrintStatus(args[0].ival);
}
/* scanppl */
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
@@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
callbackSetQueueSize(args[0].ival);
}
/* callbackQueueStatus */
static const iocshArg callbackQueueStatusArg0 = { "reset", iocshArgInt};
static const iocshArg * const callbackQueueStatusArgs[1] =
{&callbackQueueStatusArg0};
static const iocshFuncDef callbackQueueStatusFuncDef =
{"callbackQueueStatus",1,callbackQueueStatusArgs};
static void callbackQueueStatusCallFunc(const iocshArgBuf *args)
{
callbackQueuePrintStatus(args[0].ival);
}
/* callbackParallelThreads */
static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt};
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
@@ -441,12 +463,14 @@ void dbIocRegister(void)
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc);
iocshRegister(&scanOnceQueueStatusFuncDef,scanOnceQueueStatusCallFunc);
iocshRegister(&scanpplFuncDef,scanpplCallFunc);
iocshRegister(&scanpelFuncDef,scanpelCallFunc);
iocshRegister(&postEventFuncDef,postEventCallFunc);
iocshRegister(&scanpiolFuncDef,scanpiolCallFunc);
iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc);
iocshRegister(&callbackQueueStatusFuncDef,callbackQueueStatusCallFunc);
iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc);
/* Needed before callback system is initialized */

View File

@@ -24,6 +24,7 @@
#include "cantProceed.h"
#include "dbDefs.h"
#include "ellLib.h"
#include "epicsAtomic.h"
#include "epicsEvent.h"
#include "epicsMutex.h"
#include "epicsPrint.h"
@@ -63,6 +64,7 @@ static volatile enum ctl scanCtl;
static int onceQueueSize = 1000;
static epicsEventId onceSem;
static epicsRingBytesId onceQ;
static int onceQOverruns = 0;
static epicsThreadId onceTaskId;
static void *exitOnce;
@@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
if (!pushOK) {
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
newOverflow = FALSE;
epicsAtomicIncrIntT(&onceQOverruns);
} else {
newOverflow = TRUE;
}
@@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size)
return 0;
}
int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result)
{
if (!onceQ) return -1;
int ret;
if (result) {
result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry);
result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry);
result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry);
result->numOverflow = epicsAtomicGetIntT(&onceQOverruns);
ret = 0;
} else {
ret = -2;
}
if (reset) {
epicsRingBytesResetHighWaterMark(onceQ);
}
return ret;
}
void scanOnceQueuePrintStatus(const int reset)
{
scanOnceQueueStats stats;
if (scanOnceQueueStatus(reset, &stats) == -1) {
fprintf(stderr, "scanOnce system not initialized, yet. Please run "
"iocInit before using this command.\n");
return;
}
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
double qusage = 100.0 * stats.numUsed / stats.size;
printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, stats.numUsed, stats.size, qusage,
epicsAtomicGetIntT(&onceQOverruns));
}
static void initOnce(void)
{
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {

View File

@@ -42,6 +42,13 @@ struct dbCommon;
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
typedef void (*once_complete)(void *usr, struct dbCommon*);
typedef struct scanOnceQueueStats {
int size;
int numUsed;
int maxUsed;
int numOverflow;
} scanOnceQueueStats;
epicsShareFunc long scanInit(void);
epicsShareFunc void scanRun(void);
epicsShareFunc void scanPause(void);
@@ -57,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan);
epicsShareFunc int scanOnce(struct dbCommon *);
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
epicsShareFunc int scanOnceSetQueueSize(int size);
epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result);
void scanOnceQueuePrintStatus(const int reset);
/*print periodic lists*/
epicsShareFunc int scanppl(double rate);

View File

@@ -21,6 +21,7 @@
#include <stdio.h>
#define epicsExportSharedSymbols
#include "epicsAtomic.h"
#include "epicsSpin.h"
#include "dbDefs.h"
#include "epicsRingBytes.h"
@@ -38,6 +39,7 @@ typedef struct ringPvt {
volatile int nextPut;
volatile int nextGet;
int size;
int highWaterMark;
volatile char buffer[1]; /* actually larger */
}ringPvt;
@@ -47,6 +49,7 @@ epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size)
if(!pring)
return NULL;
pring->size = size + SLOP;
pring->highWaterMark = 0;
pring->nextGet = 0;
pring->nextPut = 0;
pring->lock = 0;
@@ -131,8 +134,13 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut(
if (pring->lock) epicsSpinUnlock(pring->lock);
return 0;
}
if (nbytes)
if (nbytes) {
memcpy ((void *)&pring->buffer[nextPut], value, nbytes);
int curUsed = pring->size - SLOP - freeCount;
if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) {
epicsAtomicSetIntT(&pring->highWaterMark, curUsed);
}
}
nextPut += nbytes;
}
else {
@@ -143,8 +151,13 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut(
}
topCount = size - nextPut;
copyCount = (nbytes > topCount) ? topCount : nbytes;
if (copyCount)
if (copyCount) {
memcpy ((void *)&pring->buffer[nextPut], value, copyCount);
int curUsed = pring->size - SLOP - freeCount;
if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) {
epicsAtomicSetIntT(&pring->highWaterMark, curUsed);
}
}
nextPut += copyCount;
if (nextPut == size) {
int nLeft = nbytes - copyCount;
@@ -224,3 +237,15 @@ epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id)
{
return (epicsRingBytesFreeBytes(id) <= 0);
}
epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id)
{
ringPvt *pring = (ringPvt *)id;
return epicsAtomicGetIntT(&pring->highWaterMark);
}
epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id)
{
ringPvt *pring = (ringPvt *)id;
epicsAtomicSetIntT(&pring->highWaterMark, epicsRingBytesUsedBytes(id));
}

View File

@@ -24,6 +24,7 @@ extern "C" {
#include "shareLib.h"
typedef void *epicsRingBytesId;
typedef void const *epicsRingBytesIdConst;
epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes);
/* Same, but secured by a spinlock */
@@ -39,6 +40,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id);
epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id);
epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id);
epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id);
epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id);
epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id);
#ifdef __cplusplus
}

View File

@@ -90,3 +90,15 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id)
voidPointer *pvoidPointer = reinterpret_cast<voidPointer*>(id);
return((pvoidPointer->isFull()) ? 1 : 0);
}
epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id)
{
voidPointer const *pvoidPointer = reinterpret_cast<voidPointer const*>(id);
return(pvoidPointer->getHighWaterMark());
}
epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id)
{
voidPointer *pvoidPointer = reinterpret_cast<voidPointer*>(id);
pvoidPointer->resetHighWaterMark();
}

View File

@@ -23,6 +23,7 @@
* epicsRingPointerLocked uses a spinlock.
*/
#include "epicsAtomic.h"
#include "epicsSpin.h"
#include "shareLib.h"
@@ -40,18 +41,22 @@ public: /* Functions */
int getSize() const;
bool isEmpty() const;
bool isFull() const;
int getHighWaterMark() const;
void resetHighWaterMark();
private: /* Prevent compiler-generated member functions */
/* default constructor, copy constructor, assignment operator */
epicsRingPointer();
epicsRingPointer(const epicsRingPointer &);
epicsRingPointer& operator=(const epicsRingPointer &);
int getUsedNoLock() const;
private: /* Data */
epicsSpinId lock;
volatile int nextPush;
volatile int nextPop;
int size;
int highWaterMark;
T * volatile * buffer;
};
@@ -59,6 +64,7 @@ extern "C" {
#endif /*__cplusplus */
typedef void *epicsRingPointerId;
typedef void const *epicsRingPointerIdConst;
epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size);
/* Same, but secured by a spinlock */
@@ -74,6 +80,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id)
epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id);
epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id);
epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id);
epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id);
epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id);
/* This routine was incorrectly named in previous releases */
#define epicsRingPointerSize epicsRingPointerGetSize
@@ -95,7 +103,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id);
template <class T>
inline epicsRingPointer<T>::epicsRingPointer(int sz, bool locked) :
lock(0), nextPush(0), nextPop(0), size(sz+1), buffer(new T* [sz+1])
lock(0), nextPush(0), nextPop(0), size(sz+1), highWaterMark(0),
buffer(new T* [sz+1])
{
if (locked)
lock = epicsSpinCreate();
@@ -121,6 +130,10 @@ inline bool epicsRingPointer<T>::push(T *p)
}
buffer[next] = p;
nextPush = newNext;
int used = getUsedNoLock();
if (used > epicsAtomicGetIntT(&highWaterMark)) {
epicsAtomicSetIntT(&highWaterMark, used);
}
if (lock) epicsSpinUnlock(lock);
return(true);
}
@@ -161,12 +174,19 @@ inline int epicsRingPointer<T>::getFree() const
return n;
}
template <class T>
inline int epicsRingPointer<T>::getUsedNoLock() const
{
int n = nextPush - nextPop;
if (n < 0) n += size;
return n;
}
template <class T>
inline int epicsRingPointer<T>::getUsed() const
{
if (lock) epicsSpinLock(lock);
int n = nextPush - nextPop;
if (n < 0) n += size;
int n = getUsedNoLock();
if (lock) epicsSpinUnlock(lock);
return n;
}
@@ -196,6 +216,18 @@ inline bool epicsRingPointer<T>::isFull() const
return((count == 0) || (count == size));
}
template <class T>
inline int epicsRingPointer<T>::getHighWaterMark() const
{
return epicsAtomicGetIntT(&highWaterMark);
}
template <class T>
inline void epicsRingPointer<T>::resetHighWaterMark()
{
epicsAtomicSetIntT(&highWaterMark, getUsed());
}
#endif /* __cplusplus */
#endif /* INCepicsRingPointerh */