diff --git a/devIocStats/devIocStatsAnalog.c b/devIocStats/devIocStatsAnalog.c index 62d3c53..36aa7c4 100644 --- a/devIocStats/devIocStatsAnalog.c +++ b/devIocStats/devIocStatsAnalog.c @@ -283,13 +283,28 @@ static validGetParms statsGetParms[] = { {"cbHighQueueOverruns", statsCbHighQOverruns, QUEUE_TYPE}, {NULL, NULL, 0}}; -aStats devAiStats = {6, NULL, ai_init, ai_init_record, ai_ioint_info, - ai_read, NULL}; +/* DEVSUPFUN is a bare `long (*)()`; under C23 empty parens mean "no + * arguments" rather than "unspecified arguments" (the C17-and-earlier + * meaning these DSET tables were written against), so these need an + * explicit cast to avoid -Wincompatible-pointer-types. */ +aStats devAiStats = {6, + NULL, + (DEVSUPFUN)ai_init, + (DEVSUPFUN)ai_init_record, + (DEVSUPFUN)ai_ioint_info, + (DEVSUPFUN)ai_read, + NULL}; epicsExportAddress(dset, devAiStats); -aStats devAoStats = {6, NULL, NULL, ao_init_record, NULL, ao_write, NULL}; +aStats devAoStats = {6, NULL, NULL, (DEVSUPFUN)ao_init_record, + NULL, (DEVSUPFUN)ao_write, NULL}; epicsExportAddress(dset, devAoStats); -aStats devAiClusts = { - 6, NULL, ai_clusts_init, ai_clusts_init_record, NULL, ai_clusts_read, NULL}; +aStats devAiClusts = {6, + NULL, + (DEVSUPFUN)ai_clusts_init, + (DEVSUPFUN)ai_clusts_init_record, + NULL, + (DEVSUPFUN)ai_clusts_read, + NULL}; epicsExportAddress(dset, devAiClusts); static memInfo meminfo = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; diff --git a/devIocStats/devIocStatsString.c b/devIocStats/devIocStatsString.c index 188ca63..c9a03c7 100644 --- a/devIocStats/devIocStatsString.c +++ b/devIocStats/devIocStatsString.c @@ -185,11 +185,17 @@ static validGetStrParms statsGetStrParms[] = { {"pwd2", statsPwd2, STATIC_TYPE}, {NULL, NULL, 0}}; -sStats devStringinStats = { - 5, NULL, stringin_init, stringin_init_record, NULL, stringin_read}; -sStats devStringinEnvVar = {5, NULL, NULL, envvar_init_record, - NULL, envvar_read}; -sStats devStringinEpics = {5, NULL, NULL, epics_init_record, NULL, epics_read}; +/* DEVSUPFUN is a bare `long (*)()`; under C23 empty parens mean "no + * arguments" rather than "unspecified arguments" (the C17-and-earlier + * meaning these DSET tables were written against), so these need an + * explicit cast to avoid -Wincompatible-pointer-types. */ +sStats devStringinStats = {5, NULL, (DEVSUPFUN)stringin_init, + (DEVSUPFUN)stringin_init_record, NULL, + (DEVSUPFUN)stringin_read}; +sStats devStringinEnvVar = {5, NULL, NULL, (DEVSUPFUN)envvar_init_record, + NULL, (DEVSUPFUN)envvar_read}; +sStats devStringinEpics = {5, NULL, NULL, (DEVSUPFUN)epics_init_record, NULL, + (DEVSUPFUN)epics_read}; epicsExportAddress(dset, devStringinStats); epicsExportAddress(dset, devStringinEnvVar); epicsExportAddress(dset, devStringinEpics); diff --git a/devIocStats/devIocStatsWaveform.c b/devIocStats/devIocStatsWaveform.c index e349e6b..9a5447b 100644 --- a/devIocStats/devIocStatsWaveform.c +++ b/devIocStats/devIocStatsWaveform.c @@ -116,8 +116,13 @@ static validGetWfmParms statsGetWfmParms[] = { {"pwd", statsPwd, STATIC_TYPE}, {NULL, NULL, 0}}; -wStats devWaveformStats = { - 5, NULL, waveform_init, waveform_init_record, NULL, waveform_read}; +/* DEVSUPFUN is a bare `long (*)()`; under C23 empty parens mean "no + * arguments" rather than "unspecified arguments" (the C17-and-earlier + * meaning this DSET table was written against), so these need an + * explicit cast to avoid -Wincompatible-pointer-types. */ +wStats devWaveformStats = {5, NULL, (DEVSUPFUN)waveform_init, + (DEVSUPFUN)waveform_init_record, NULL, + (DEVSUPFUN)waveform_read}; epicsExportAddress(dset, devWaveformStats); /* ---------------------------------------------------------------------- */ diff --git a/devIocStats/os/RTEMS/devIocStatsOSD.h b/devIocStats/os/RTEMS/devIocStatsOSD.h index 00d5787..940b8bf 100644 --- a/devIocStats/os/RTEMS/devIocStatsOSD.h +++ b/devIocStats/os/RTEMS/devIocStatsOSD.h @@ -54,6 +54,26 @@ #include #include +/* osdCpuUsage.c walks _Objects_Information_table[]/Objects_Information + * directly; older RTEMS pulled these in transitively via rtems.h, current + * RTEMS no longer does, so include the Score header explicitly. */ +#include + +/* osdCpuUsage.c reads Thread_Control::cpu_time_used (a Timestamp_Control) + * via the portable _Timestamp_Get_*() accessors rather than assuming its + * representation (struct timespec vs. int64_t sbintime_t is a per- + * architecture/config choice -- that's the whole point of the wrapper). */ +#include + +/* osdWorkspaceUsage.c uses _Workspace_Area directly; same transitive- + * include story as objectimpl.h above. */ +#include + +/* osdSuspTasks.c walks _RTEMS_tasks_Information.Objects directly; this is + * the Classic API task object information, declared here rather than + * anywhere Score-level. */ +#include + #undef malloc #undef free @@ -88,6 +108,12 @@ void bsp_reset(); \ bsp_reset(); \ } +#elif RTEMS_VERSION_INT >= VERSION_INT(6, 0, 0, 0) +/* rtemsReboot() no longer exists; current RTEMS's bsp_reset() takes a + * (source, code) pair instead (see ). */ +#include +#include +#define reboot(x) bsp_reset(RTEMS_FATAL_SOURCE_APPLICATION, (x)) #else #define reboot(x) rtemsReboot() #endif diff --git a/devIocStats/os/RTEMS/osdClustInfo.c b/devIocStats/os/RTEMS/osdClustInfo.c index 589d0f3..89e2c7d 100644 --- a/devIocStats/os/RTEMS/osdClustInfo.c +++ b/devIocStats/os/RTEMS/osdClustInfo.c @@ -56,6 +56,37 @@ #include +#if RTEMS_LIBBSD_STACK + +/* The legacy network stack's global `struct mbstat mbstat` doesn't exist + * under libbsd. libbsd's portable memstat API (memstat_sysctl_all(), the + * same mechanism `netstat -m`/`vmstat -z` use) looked like the right + * replacement and is what epics-modules/iocStats#61's RTEMS 6 port does + * too -- but memstat_sysctl_all() crashes on real hardware here: it walks + * into UMA's per-CPU counter stats (uma_vm_zone_stats() -> + * _bsd_counter_u64_alloc(), freebsd/sys/kern/subr_counter.c), which faults + * with a data abort (confirmed via addr2line against the crash PC/LR). + * Whatever's missing/broken in this RTEMS-libbsd port's counter(9) + * support, #61 was apparently never actually run on hardware either (it + * also has a row-index bug independent of this). Until that's root-caused, + * report "not available" rather than crash the IOC. */ + +int devIocStatsInitClusterInfo(void) { return 0; } + +int devIocStatsGetClusterInfo(int pool, clustInfo *pval) { + (void)pool; + (void)pval; + return -1; +} + +int devIocStatsGetClusterUsage(int pool, int *pval) { + (void)pool; + (void)pval; + return -1; +} + +#else /* RTEMS_LIBBSD_STACK */ + /* This would otherwise need _KERNEL to be defined... */ extern struct mbstat mbstat; @@ -86,3 +117,5 @@ int devIocStatsGetClusterUsage(int pool, int *pval) { return 0; } + +#endif /* RTEMS_LIBBSD_STACK */ diff --git a/devIocStats/os/RTEMS/osdCpuUsage.c b/devIocStats/os/RTEMS/osdCpuUsage.c index 4963b8a..608ea58 100644 --- a/devIocStats/os/RTEMS/osdCpuUsage.c +++ b/devIocStats/os/RTEMS/osdCpuUsage.c @@ -65,9 +65,12 @@ typedef char *objName; #if defined(RTEMS_ENABLE_NANOSECOND_CPU_USAGE_STATISTICS) || \ (__RTEMS_MAJOR__ > 4) || (__RTEMS_MAJOR__ == 4 && __RTEMS_MINOR__ > 9) +/* cpu_time_used is a Timestamp_Control; its representation (struct + * timespec vs. int64_t) is a per-architecture/config choice, so go + * through the portable accessor rather than assume struct-timespec + * layout. */ #define CPU_ELAPSED_TIME(tc) \ - ((double)(tc)->cpu_time_used.tv_sec + \ - ((double)tc->cpu_time_used.tv_nsec / 1E9)) + ((double)_Timestamp_Get_as_nanoseconds(&(tc)->cpu_time_used) / 1E9) #else #define CPU_ELAPSED_TIME(tc) ((double)(tc)->ticks_executed) #endif @@ -99,8 +102,15 @@ static void cpu_ticks(double *total, double *idle) { obj = _Objects_Information_table[x][1]; if (obj) { - for (y = 1; y <= obj->maximum; y++) { - tc = (Thread_Control *)obj->local_table[y]; + /* obj->maximum_id is an encoded Objects_Id (API/class/node/index), + * not a plain count -- _Objects_Get_maximum_index() extracts the + * actual maximum index, matching what RTEMS's own internals use. */ + for (y = 1; y <= (int)_Objects_Get_maximum_index(obj); y++) { + /* local_table is indexed from 0, but the object's index-within- + * class (y) is 1-based (RTEMS stores objects at + * id_index - OBJECTS_INDEX_MINIMUM). Indexing with y directly + * reads one entry past the end of the array on every pass. */ + tc = (Thread_Control *)obj->local_table[y - OBJECTS_INDEX_MINIMUM]; if (tc) { *total += CPU_ELAPSED_TIME(tc); RTEMS_OBJ_GET_NAME(tc, name); diff --git a/devIocStats/os/RTEMS/osdIFErrors.c b/devIocStats/os/RTEMS/osdIFErrors.c index e01e729..b905729 100644 --- a/devIocStats/os/RTEMS/osdIFErrors.c +++ b/devIocStats/os/RTEMS/osdIFErrors.c @@ -40,20 +40,37 @@ #include -/* This would otherwise need _KERNEL to be defined... */ -extern struct ifnet *ifnet; +/* The kernel-internal `struct ifnet` linked list (walked via if_next) is + * gone under libbsd -- ifnet is now a CK_STAILQ and if_ierrors/if_oerrors + * are behind the if_get_counter KPI, neither meant for use outside the + * kernel proper. getifaddrs()/struct if_data is the portable, userspace- + * visible way to get the same per-interface error counts: each interface + * contributes one AF_LINK entry whose ifa_data is its struct if_data. */ +#include +#include +#include int devIocStatsInitIFErrors(void) { return 0; } int devIocStatsGetIFErrors(ifErrInfo *pval) { - struct ifnet *ifp; + struct ifaddrs *addrs, *ifa; - /* add all interfaces' errors */ pval->ierrors = 0; pval->oerrors = 0; - for (ifp = ifnet; ifp != NULL; ifp = ifp->if_next) { - pval->ierrors += ifp->if_ierrors; - pval->oerrors += ifp->if_oerrors; + + if (getifaddrs(&addrs) < 0) { + return -1; + } + + for (ifa = addrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_LINK && + ifa->ifa_data != NULL) { + struct if_data *ifd = (struct if_data *)ifa->ifa_data; + pval->ierrors += ifd->ifi_ierrors; + pval->oerrors += ifd->ifi_oerrors; + } } + + freeifaddrs(addrs); return 0; } diff --git a/devIocStats/os/RTEMS/osdSuspTasks.c b/devIocStats/os/RTEMS/osdSuspTasks.c index 9679d53..89c5a9b 100644 --- a/devIocStats/os/RTEMS/osdSuspTasks.c +++ b/devIocStats/os/RTEMS/osdSuspTasks.c @@ -45,17 +45,21 @@ int devIocStatsInitSuspTasks(void) { return 0; } int devIocStatsGetSuspTasks(int *pval) { Objects_Control *o; Objects_Id id = OBJECTS_ID_INITIAL_INDEX; - Objects_Id nid; int n = 0; - Objects_Locations l; - /* count all suspended (LOCAL -- cannot deal with remote ones ATM) tasks */ - while ((o = _Objects_Get_next(&_RTEMS_tasks_Information, id, &l, &nid))) { - if ((RTEMS_ALREADY_SUSPENDED == rtems_task_is_suspended(nid))) { + /* count all suspended (LOCAL -- cannot deal with remote ones ATM) tasks. + * _Objects_Get_next()'s signature/semantics changed: no more separate + * Objects_Locations out-param (it now just returns NULL when nothing + * local is found), the object information comes first, and id doubles + * as both the search-from input and the next-id output. It locks the + * object allocator mutex when it finds something, which the caller must + * release -- see cpukit/libcsupport/src/resource_snapshot.c for the + * reference usage this follows. */ + while ((o = _Objects_Get_next(id, &_RTEMS_tasks_Information.Objects, &id))) { + if ((RTEMS_ALREADY_SUSPENDED == rtems_task_is_suspended(o->id))) { n++; } - _Thread_Enable_dispatch(); - id = nid; + _Objects_Allocator_unlock(); } *pval = n; return 0; diff --git a/devIocStats/os/RTEMS/osdWorkspaceUsage.c b/devIocStats/os/RTEMS/osdWorkspaceUsage.c index 9f7518a..f83f2eb 100644 --- a/devIocStats/os/RTEMS/osdWorkspaceUsage.c +++ b/devIocStats/os/RTEMS/osdWorkspaceUsage.c @@ -41,7 +41,18 @@ int devIocStatsGetWorkspaceUsage(memInfo *pval) { _RTEMS_Unlock_allocator(); #endif /* RTEMS_PROTECTED_HEAP */ #if (__RTEMS_MAJOR__ > 4) || (__RTEMS_MAJOR__ == 4 && __RTEMS_MINOR__ > 9) - pval->numBytesTotal = Configuration.work_space_size; + /* rtems_configuration_get_work_space_size() (the public accessor for + * the global `Configuration` object, which a support library can't + * reach directly -- it's only declared when the application itself + * includes ) reports the compile-time *configured* + * workspace size. Hardware-verified on a real BBB (RTEMS_INIT=new) + * that this does NOT match the actual runtime-negotiated workspace + * region: Free.total+Used.total came back ~227MB while this reported + * ~624KB. Derive the total from the same heap-info call that already + * produced Free/Used instead -- it's the only value guaranteed + * self-consistent with them, regardless of how this port sizes the + * workspace at boot. */ + pval->numBytesTotal = info.Free.total + info.Used.total; #else pval->numBytesTotal = _Configuration_Table->work_space_size; #endif