diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h index 7080ed7cb..bd1a6c8a2 100644 --- a/src/ioc/db/dbJLink.h +++ b/src/ioc/db/dbJLink.h @@ -39,6 +39,7 @@ typedef struct jlink { struct jlif *pif; /* Link methods */ struct jlink *parent; /* NULL for top-level links */ int parseDepth; /* Used by parser, unused afterwards */ + unsigned debug:1; /* Set to request debug output to console */ /* Link types extend or embed this structure for private storage */ } jlink; diff --git a/src/std/link/Makefile b/src/std/link/Makefile index 5ad9777a8..9b6abd705 100644 --- a/src/std/link/Makefile +++ b/src/std/link/Makefile @@ -14,5 +14,6 @@ DBD += links.dbd dbRecStd_SRCS += lnkConst.c dbRecStd_SRCS += lnkCalc.c dbRecStd_SRCS += lnkState.c +dbRecStd_SRCS += lnkDebug.c HTMLS += links.html diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index 6b48c13fb..bf13bf7a9 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -15,6 +15,10 @@ The following additional link types are available in this release: =item * L +=item * L + +=item * L + =back =head2 Using JSON Links @@ -200,3 +204,35 @@ link is initialized if it doesn't already exist. {state:"!simEnable"} =cut + + +link(debug, lnkDebugIf) +variable(lnkDebug_debug, int) + +=head3 Debug Link C<"debug"> + +The debug link type exists to enable debugging of other link types; it provides +no functionality itself other than to turn on the debug flag for the child link +that is its only parameter and pass all link operations down to that link. + +=head4 Example + + {debug:{state:"redBeam"}} + +=cut + + +link(trace, lnkTraceIf) + +=head3 Trace Link C<"trace"> + +The trace link type is a relative of the debug link type that also traces the +operation of its child link. At creation it turns on the debug flag of its child +link, then it prints the method arguments and return values of all link +operations before / after passing control down to the child link. + +=head4 Example + + {trace:{state:"redBeam"}} + +=cut diff --git a/src/std/link/lnkDebug.c b/src/std/link/lnkDebug.c new file mode 100644 index 000000000..9ede69cb2 --- /dev/null +++ b/src/std/link/lnkDebug.c @@ -0,0 +1,1044 @@ +/*************************************************************************\ +* Copyright (c) 2018 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. +\*************************************************************************/ +/* lnkDebug.c */ + +/* Usage + * {debug:{...:...}} + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccessDefs.h" +#include "dbLink.h" +#include "dbJLink.h" +#include "dbStaticLib.h" +#include "errlog.h" +#include "epicsTime.h" + +#include "epicsExport.h" + +/* This is for debugging the debug link-type */ +int lnkDebug_debug; +epicsExportAddress(int, lnkDebug_debug); + +#define IFDEBUG(n) if (lnkDebug_debug >= (n)) + +typedef struct debug_link { + jlink jlink; /* embedded object */ + short dbfType; + unsigned trace:1; + const jlif *child_jlif; + const lset *child_lset; + jlif jlif; + lset lset; + struct link child_link; +} debug_link; + + +/********************* Delegating jlif Routines *********************/ + +static void delegate_free(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + struct link *plink = &dlink->child_link; + + if (dlink->trace) + printf("Link trace: Calling %s::free_jlink(%p)\n", + pif->name, pjlink); + + pif->free_jlink(pjlink); + plink->type = 0; + plink->value.json.jlink = NULL; + + if (dlink->trace) + printf("Link trace: %s::free_jlink(%p) returned\n", + pif->name, pjlink); +} + +static jlif_result delegate_null(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_null(%p)\n", + pif->name, pjlink); + + res = pif->parse_null(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_null(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_boolean(jlink *pjlink, int val) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_boolean(%p, %d)\n", + pif->name, pjlink, val); + + res = pif->parse_boolean(pjlink, val); + + if (dlink->trace) + printf("Link trace: %s::parse_boolean(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_integer(jlink *pjlink, long long num) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_integer(%p, %lld)\n", + pif->name, pjlink, num); + + res = pif->parse_integer(pjlink, num); + + if (dlink->trace) + printf("Link trace: %s::parse_integer(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_double(jlink *pjlink, double num) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_double(%p, %g)\n", + pif->name, pjlink, num); + + res = pif->parse_double(pjlink, num); + + if (dlink->trace) + printf("Link trace: %s::parse_double(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_string(jlink *pjlink, const char *val, size_t len) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_string(%p, \"%.*s\")\n", + pif->name, pjlink, (int) len, val); + + res = pif->parse_string(pjlink, val, len); + + if (dlink->trace) + printf("Link trace: %s::parse_string(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_key_result delegate_start_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_key_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_start_map(%p)\n", + pif->name, pjlink); + + res = pif->parse_start_map(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_start_map(%p) returned %s\n", + pif->name, pjlink, jlif_key_result_name[res]); + + return res; +} + +static jlif_result delegate_map_key(jlink *pjlink, const char *key, size_t len) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_map_key(%p, \"%.*s\")\n", + pif->name, pjlink, (int) len, key); + + res = pif->parse_map_key(pjlink, key, len); + + if (dlink->trace) + printf("Link trace: %s::parse_map_key(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_end_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_end_map(%p)\n", + pif->name, pjlink); + + res = pif->parse_end_map(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_end_map(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_start_array(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_(%p)\n", + pif->name, pjlink); + + res = pif->parse_start_array(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_start_array(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_end_array(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_end_array(%p)\n", + pif->name, pjlink); + + res = pif->parse_end_array(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_end_array(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static void delegate_start_child(jlink *pjlink, jlink *child) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::start_child(%p, %p)\n", + pif->name, pjlink, child); + + pif->start_child(pjlink, child); + + if (dlink->trace) + printf("Link trace: %s::start_child(%p) returned\n", + pif->name, pjlink); +} + +static void delegate_end_child(jlink *pjlink, jlink *child) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::end_child(%p, %p)\n", + pif->name, pjlink, child); + + pif->end_child(pjlink, child); + + if (dlink->trace) + printf("Link trace: %s::end_child(%p) returned\n", + pif->name, pjlink); +} + +static struct lset* delegate_get_lset(const jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + + if (dlink->trace) + printf("Link trace: NOT calling %s::get_lset(%p)\n", + dlink->child_jlif->name, pjlink); + + return &dlink->lset; +} + +static void delegate_report(const jlink *pjlink, int level, int indent) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::report(%p, %d, %d)\n", + pif->name, pjlink, level, indent); + + pif->report(pjlink, level, indent); + + if (dlink->trace) + printf("Link trace: %s::report(%p) returned\n", + pif->name, pjlink); +} + +static long delegate_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::map_children(%p, %p, %p)\n", + pif->name, pjlink, rtn, ctx); + + res = pif->map_children(pjlink, rtn, ctx); + + if (dlink->trace) + printf("Link trace: %s::map_children(%p) returned %ld\n", + pif->name, pjlink, res); + + return res; +} + + +/********************* Delegating lset Routines *********************/ + +static void delegate_openLink(struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::openLink(%p = jlink %p)\n", + dlink->child_jlif->name, clink, clink->value.json.jlink); + + clset->openLink(clink); + + if (dlink->trace) + printf("Link trace: %s::openLink(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static void delegate_removeLink(struct dbLocker *locker, struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::removeLink(%p, %p)\n", + dlink->child_jlif->name, locker, clink); + + clset->removeLink(locker, clink); + + if (dlink->trace) + printf("Link trace: %s::removeLink(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static long delegate_loadScalar(struct link *plink, short dbrType, void *pbuffer) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadScalar(%p, %s, %p)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer); + + res = clset->loadScalar(clink, dbrType, pbuffer); + + if (dlink->trace) + printf("Link trace: %s::loadScalar(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static long delegate_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size, + epicsUInt32 *plen) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadLS(%p, %p, %u)\n", + dlink->child_jlif->name, clink, pbuffer, size); + + res = clset->loadLS(clink, pbuffer, size, plen); + + if (dlink->trace) { + printf("Link trace: %s::loadLS(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Loaded: %u byte(s) \"%s\"\n", *plen, pbuffer); + } + + return res; +} + +static long delegate_loadArray(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadArray(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l); + + res = clset->loadArray(clink, dbrType, pbuffer, pnRequest); + + if (dlink->trace) { + printf("Link trace: %s::loadArray(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Loaded: %ld element(s)\n", pnRequest ? *pnRequest : 1l); + } + + return res; +} + +static int delegate_isConnected(const struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + int res; + + if (dlink->trace) + printf("Link trace: Calling %s::isConnected(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->isConnected(clink); + + if (dlink->trace) + printf("Link trace: %s::isConnected(%p) returned %d (%s)\n", + dlink->child_jlif->name, clink, res, + res == 0 ? "No" : res == 1 ? "Yes" : "Bad value"); + + return res; +} + +static int delegate_getDBFtype(const struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + int res; + + if (dlink->trace) + printf("Link trace: Calling %s::getDBFtype(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getDBFtype(clink); + + if (dlink->trace) + printf("Link trace: %s::getDBFtype(%p) returned %d (%s)\n", + dlink->child_jlif->name, clink, res, + res == -1 ? "Link disconnected" : dbGetFieldTypeString(res)); + + return res; +} + +static long delegate_getElements(const struct link *plink, long *pnElements) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getElements(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getElements(clink, pnElements); + + if (dlink->trace) { + printf("Link trace: %s::getElements(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Result: %ld element(s)\n", *pnElements); + } + + return res; +} + +static long delegate_getValue(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getValue(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l); + + res = clset->getValue(clink, dbrType, pbuffer, pnRequest); + + if (dlink->trace) { + printf("Link trace: %s::getValue(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: %ld element(s)\n", pnRequest ? *pnRequest : 1l); + } + + return res; +} + +static long delegate_getControlLimits(const struct link *plink, + double *lo, double *hi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getControlLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getControlLimits(clink, lo, hi); + + if (dlink->trace) { + printf("Link trace: %s::getControlLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi); + } + + return res; +} + +static long delegate_getGraphicLimits(const struct link *plink, + double *lo, double *hi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getGraphicLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getGraphicLimits(clink, lo, hi); + + if (dlink->trace) { + printf("Link trace: %s::getGraphicLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi); + } + + return res; +} + +static long delegate_getAlarmLimits(const struct link *plink, + double *lolo, double *lo, double *hi, double *hihi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getAlarmLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getAlarmLimits(clink, lolo, lo, hi, hihi); + + if (dlink->trace) { + printf("Link trace: %s::getAlarmLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lolo = %g, Lo = %g, Hi = %g, Hihi = %g\n", + *lolo, *lo, *hi, *hihi); + } + + return res; +} + +static long delegate_getPrecision(const struct link *plink, short *precision) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getPrecision(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getPrecision(clink, precision); + + if (dlink->trace) { + printf("Link trace: %s::getPrecision(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: prec = %d\n", *precision); + } + + return res; +} + +static long delegate_getUnits(const struct link *plink, char *units, int unitsSize) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getUnits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getUnits(clink, units, unitsSize); + + if (dlink->trace) { + printf("Link trace: %s::getUnits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Units = '%s'\n", units); + } + + return res; +} + +static long delegate_getAlarm(const struct link *plink, epicsEnum16 *stat, + epicsEnum16 *sevr) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getAlarm(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getAlarm(clink, stat, sevr); + + if (dlink->trace) { + printf("Link trace: %s::getAlarm(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got:%s%s%s%s\n", + stat ? " Status = " : "", + stat && (*stat < ALARM_NSEV) ? + epicsAlarmConditionStrings[*stat] : "Bad-status", + sevr ? " Severity = " : "", + sevr && (*sevr < ALARM_NSTATUS) ? + epicsAlarmSeverityStrings[*sevr] : "Bad-severity" + ); + } + + return res; +} + +static long delegate_getTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getTimeStamp(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getTimeStamp(clink, pstamp); + + if (dlink->trace) { + printf("Link trace: %s::getTimeStamp(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) { + char timeStr[32]; + + epicsTimeToStrftime(timeStr, sizeof(timeStr), + "%Y-%m-%d %H:%M:%S.%09f", pstamp); + printf(" Got: Timestamp = '%s'\n", timeStr); + } + } + + return res; +} + +static long delegate_putValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::putValue(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, nRequest); + + res = clset->putValue(clink, dbrType, pbuffer, nRequest); + + if (dlink->trace) + printf("Link trace: %s::putValue(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static long delegate_putAsync(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::putAsync(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, nRequest); + + res = clset->putAsync(clink, dbrType, pbuffer, nRequest); + + if (dlink->trace) + printf("Link trace: %s::putAsync(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static void delegate_scanForward(struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::scanForward(%p)\n", + dlink->child_jlif->name, clink); + + clset->scanForward(clink); + + if (dlink->trace) + printf("Link trace: %s::scanForward(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static long delegate_doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::doLocked(%p, %p, %p)\n", + dlink->child_jlif->name, clink, rtn, priv); + + res = clset->doLocked(clink, rtn, priv); + + if (dlink->trace) + printf("Link trace: %s::doLocked(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + + +/************************ Debug jlif Routines ***********************/ + +static jlink* lnkDebug_alloc(short dbfType) +{ + debug_link *dlink; + + IFDEBUG(10) + printf("lnkDebug_alloc(%s)\n", dbGetFieldTypeString(dbfType)); + + dlink = calloc(1, sizeof(struct debug_link)); + if (!dlink) { + errlogPrintf("lnkDebug: calloc() failed.\n"); + return NULL; + } + + dlink->dbfType = dbfType; + + IFDEBUG(10) + printf("lnkDebug_alloc -> debug@%p\n", dlink); + + return &dlink->jlink; +} + +static jlink* lnkTrace_alloc(short dbfType) +{ + debug_link *dlink; + + IFDEBUG(10) + printf("lnkTrace_alloc(%s)\n", dbGetFieldTypeString(dbfType)); + + dlink = calloc(1, sizeof(struct debug_link)); + if (!dlink) { + errlogPrintf("lnkTrace: calloc() failed.\n"); + return NULL; + } + + dlink->dbfType = dbfType; + dlink->trace = 1; + + IFDEBUG(10) + printf("lnkTrace_alloc -> debug@%p\n", dlink); + + return &dlink->jlink; +} + +static void lnkDebug_free(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_free(debug@%p)\n", dlink); + + dbJLinkFree(dlink->child_link.value.json.jlink); + + free(dlink); +} + +static jlif_key_result lnkDebug_start_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_start_map(debug@%p)\n", dlink); + + switch (dlink->dbfType) { + case DBF_INLINK: + return jlif_key_child_inlink; + case DBF_OUTLINK: + return jlif_key_child_outlink; + case DBF_FWDLINK: + return jlif_key_child_outlink; + } + return jlif_stop; +} + +static jlif_result lnkDebug_end_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_end_map(debug@%p)\n", dlink); + + return jlif_continue; +} + +static void lnkDebug_start_child(jlink *parent, jlink *child) +{ + debug_link *dlink = CONTAINER(parent, struct debug_link, jlink); + const jlif *pif = child->pif; + const jlif delegate_jlif = { + pif->name, + pif->alloc_jlink, /* Never used */ + delegate_free, + pif->parse_null ? delegate_null : NULL, + pif->parse_boolean ? delegate_boolean : NULL, + pif->parse_integer ? delegate_integer : NULL, + pif->parse_double ? delegate_double : NULL, + pif->parse_string ? delegate_string : NULL, + pif->parse_start_map ? delegate_start_map : NULL, + pif->parse_map_key ? delegate_map_key : NULL, + pif->parse_end_map ? delegate_end_map : NULL, + pif->parse_start_array ? delegate_start_array : NULL, + pif->parse_end_array ? delegate_end_array : NULL, + pif->end_child ? delegate_end_child : NULL, + delegate_get_lset, + pif->report ? delegate_report : NULL, + pif->map_children ? delegate_map_children : NULL, + pif->start_child ? delegate_start_child : NULL + }; + + IFDEBUG(10) + printf("lnkDebug_start_child(debug@%p, %s@%p)\n", dlink, + child->pif->name, child); + + dlink->child_jlif = pif; + memcpy(&dlink->jlif, &delegate_jlif, sizeof(jlif)); + + child->debug = 1; + child->pif = &dlink->jlif; /* Replace Child's interface table */ + + IFDEBUG(15) + printf("lnkDebug_start_child: pif %p => %p\n", pif, child->pif); + + if (dlink->trace) + printf("Link trace: %s::alloc_jlink(%s) returned %p\n", + pif->name, dbGetFieldTypeString(dlink->dbfType), child); +} + +static void lnkDebug_end_child(jlink *parent, jlink *child) +{ + debug_link *dlink = CONTAINER(parent, struct debug_link, jlink); + struct link *plink = &dlink->child_link; + const lset *plset = dlink->child_jlif->get_lset(child); + lset delegate_lset = { + plset->isConstant, + plset->isVolatile, + plset->openLink ? delegate_openLink : NULL, + plset->removeLink ? delegate_removeLink : NULL, + plset->loadScalar ? delegate_loadScalar : NULL, + plset->loadLS ? delegate_loadLS : NULL, + plset->loadArray ? delegate_loadArray : NULL, + plset->isConnected ? delegate_isConnected : NULL, + plset->getDBFtype ? delegate_getDBFtype : NULL, + plset->getElements ? delegate_getElements : NULL, + plset->getValue ? delegate_getValue : NULL, + plset->getControlLimits ? delegate_getControlLimits : NULL, + plset->getGraphicLimits ? delegate_getGraphicLimits : NULL, + plset->getAlarmLimits ? delegate_getAlarmLimits : NULL, + plset->getPrecision ? delegate_getPrecision : NULL, + plset->getUnits ? delegate_getUnits : NULL, + plset->getAlarm ? delegate_getAlarm : NULL, + plset->getTimeStamp ? delegate_getTimeStamp : NULL, + plset->putValue ? delegate_putValue : NULL, + plset->putAsync ? delegate_putAsync : NULL, + plset->scanForward ? delegate_scanForward : NULL, + plset->doLocked ? delegate_doLocked : NULL + }; + + IFDEBUG(10) + printf("lnkDebug_end_child(debug@%p, %s@%p)\n", dlink, + child->pif->name, child); + + plink->type = JSON_LINK; + plink->value.json.string = NULL; + plink->value.json.jlink = child; + + dlink->child_lset = plset; + memcpy(&dlink->lset, &delegate_lset, sizeof(lset)); + + IFDEBUG(15) + printf("lnkDebug_end_child: lset %p => %p\n", plset, &dlink->lset); +} + +static struct lset* lnkDebug_get_lset(const jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_get_lset(debug@%p)\n", pjlink); + + return &dlink->lset; +} + +static void lnkDebug_report(const jlink *pjlink, int level, int indent) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_report(debug@%p)\n", dlink); + + if (dlink->trace) + printf("%*s'trace':\n", indent, ""); + else + printf("%*s'debug':\n", indent, ""); + + if (dlink->child_link.type == JSON_LINK) { + dbJLinkReport(dlink->child_link.value.json.jlink, level, indent + 2); + } +} + +long lnkDebug_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_map_children(debug@%p)\n", dlink); + + if (dlink->child_link.type == JSON_LINK) { + return dbJLinkMapChildren(&dlink->child_link, rtn, ctx); + } + return 0; +} + + +/************************* Interface Tables *************************/ + +static jlif lnkDebugIf = { + "debug", lnkDebug_alloc, lnkDebug_free, + NULL, NULL, NULL, NULL, NULL, + lnkDebug_start_map, NULL, lnkDebug_end_map, + NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset, + lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child +}; +epicsExportAddress(jlif, lnkDebugIf); + +static jlif lnkTraceIf = { + "trace", lnkTrace_alloc, lnkDebug_free, + NULL, NULL, NULL, NULL, NULL, + lnkDebug_start_map, NULL, lnkDebug_end_map, + NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset, + lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child +}; +epicsExportAddress(jlif, lnkTraceIf);