From 2ec624aad6e6623e70540617600c4068dd7f2ada Mon Sep 17 00:00:00 2001 From: Sang Woo Kim Date: Fri, 17 Jul 2026 01:32:18 +0900 Subject: [PATCH] selRecord: fix the precision reported for the A-L and LA-LL fields get_precision() walks pvalue and plvalue over the A-L and LA-LL fields, then compares paddr->pfield against &pvalue and &plvalue -- the addresses of the two local pointer variables, not of the fields they point at. Those are stack addresses, so neither test has ever matched and every A-L or LA-LL precision request falls through to recGblGetPrec(). The line just above gets it right: prec->val is a field and needs the &, whereas pvalue and plvalue are already pointers. Both tests date from e87551df24, the commit that added the loop, and have been dead ever since, so every release since R3.12 has this. They carried a (void *) cast until 6d85a36397 removed it, which is why the compiler was never shown a double ** to complain about. That the fields were meant to be covered is not in doubt: this record's get_graphic_double() and get_control_double() both name A-L and LA-LL. This changes what those fields report. recGblGetPrec() substitutes 15 for a DOUBLE precision outside 0 to 15, so today A-L and LA-LL answer 15 for a PREC of 17 while VAL answers 17. They now answer PREC unclamped, which is what VAL has always done. Add selTest, reading the precision of VAL, A, L, LA, LL and HIHI from a record with PREC=17. The four A-L and LA-LL cases report 15 before this fix; VAL and HIHI pin the two paths either side of them. --- modules/database/src/std/rec/selRecord.c | 4 +- modules/database/test/std/rec/Makefile | 7 +++ modules/database/test/std/rec/selTest.c | 68 ++++++++++++++++++++++++ modules/database/test/std/rec/selTest.db | 3 ++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 modules/database/test/std/rec/selTest.c create mode 100644 modules/database/test/std/rec/selTest.db diff --git a/modules/database/src/std/rec/selRecord.c b/modules/database/src/std/rec/selRecord.c index b173abe34..5125be1f3 100644 --- a/modules/database/src/std/rec/selRecord.c +++ b/modules/database/src/std/rec/selRecord.c @@ -156,8 +156,8 @@ static long get_precision(const DBADDR *paddr, long *precision) pvalue = &prec->a; plvalue = &prec->la; for(i=0; ipfield==&pvalue - || paddr->pfield==&plvalue){ + if(paddr->pfield==pvalue + || paddr->pfield==plvalue){ return(0); } } diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index c5eca0dbe..504bc3648 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -93,6 +93,13 @@ testHarness_SRCS += seqTest.c TESTFILES += ../seqTest.db TESTS += seqTest +TESTPROD_HOST += selTest +selTest_SRCS += selTest.c +selTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += selTest.c +TESTFILES += ../selTest.db +TESTS += selTest + TESTPROD_HOST += longoutTest longoutTest_SRCS += longoutTest.c longoutTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp diff --git a/modules/database/test/std/rec/selTest.c b/modules/database/test/std/rec/selTest.c new file mode 100644 index 000000000..f7011a4fa --- /dev/null +++ b/modules/database/test/std/rec/selTest.c @@ -0,0 +1,68 @@ +/*************************************************************************\ +* SPDX-License-Identifier: EPICS +* 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 "errlog.h" +#include "dbAccess.h" + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static +void testPrecEqual(const char *pv, long expected){ + DBADDR addr; + struct { + DBRprecision + epicsFloat64 value; + } buf; + long options = DBR_PRECISION; + long nRequest = 1; + + if (dbNameToAddr(pv, &addr) || + dbGetField(&addr, DBR_DOUBLE, &buf, &options, &nRequest, NULL)) { + testFail("Can't read the precision of %s", pv); + return; + } + testOk(buf.precision.dp == expected, "%s precision (%ld) == %ld", + pv, buf.precision.dp, expected); +} + +/* + * PREC gives the precision of VAL and of the A-L and LA-LL fields. + * The remaining DOUBLE fields get theirs from recGblGetPrec(), which + * substitutes 15 for a PREC outside the range it can print. + * */ +static +void testSelPrecision(void){ + testPrecEqual("sel0.VAL", 17); + testPrecEqual("sel0.A", 17); + testPrecEqual("sel0.L", 17); + testPrecEqual("sel0.LA", 17); + testPrecEqual("sel0.LL", 17); + testPrecEqual("sel0.HIHI", 15); +} + +MAIN(selTest) { + testPlan(6); + + testdbPrepare(); + + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("selTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + testSelPrecision(); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/selTest.db b/modules/database/test/std/rec/selTest.db new file mode 100644 index 000000000..4d143d8fd --- /dev/null +++ b/modules/database/test/std/rec/selTest.db @@ -0,0 +1,3 @@ +record(sel, "sel0"){ + field(PREC, "17") +}