/* wvTmrLib.c - timer library (System Viewer) */ /* * Copyright (c) 1993-1995, 1998, 2004-2010, 2013, 2023 * Wind River Systems, Inc. * * The right to copy, distribute, modify or otherwise make use * of this software may be licensed only pursuant to the terms * of an applicable Wind River license agreement. */ /* modification history -------------------- 01y,16oct23,kch Fixed warnings when time_t is 64-bit. 01x,30oct13,elp checked wdStart() return value. 01w,28jan13,wzc WIND00399576 - initialize value when enabling timestamp. 01v,06sep10,tcr Share timestamp with Cafe 01v,15nov10,tcr Allow Cafe to use timestamp 01u,14jun10,vhe Fixed VSB/CDF compatibility (CQ:WIND00216392); cleanup 01t,21jan10,wzc Adapt SV to vxWorks 64bit. 01s,27jul09,jpb Fixed compiler warning. 01r,27aug08,jpb Renamed VSB header file 01q,18jun08,jpb Renamed _WRS_VX_SMP to _WRS_CONFIG_SMP. Added include path for kernel configurations options set in vsb. 01p,25sep07,tcr fix incorrect timestamp calculation (WIND00105916) 01o,17sep07,tcr WIND00103129: NULL-ptr deref in traceTimestampGet() on SMP 01n,08apr07,mmi make traceTimestampGet () SMP safe 01m,29apr07,cjj included private/tickLibP.h for vxAbsTicks made pRolloverCount a volatile like vxAbsTicks 01l,28jun06,tcr add support for POSIX trace timer: Remove i960 support 01k,22sep05,tcr Fix SPR 112648: System Viewer wvTmrRegister() reference entry incorrect 01j,20apr04,tcr change references to WindView to 'System Viewer' 01i,05oct98,jmp doc: fixed SEE ALSO section. 01h,28may98,dgp clean up man pages for WV 2.0 beta release 01g,15apr98,cjtc WV2.0 i960 port 01f,03feb95,rhp docn improvements from last printed man pages 01e,01feb95,rhp library man page: add ref to User's Guide 01d,05apr94,smb and more documentation changes 01c,07mar94,smb more documentation changes 01b,17jan94,smb documentation changes 01a,10dec93,smb created */ /* DESCRIPTION This library allows a Wind River System Viewer timestamp timer to be registered. When this timer is enabled, events are tagged with a timestamp as they are logged. Seven routines are required for System Viewer: a timestamp routine, a timestamp routine that guarantees interrupt lockout, a routine that enables the timer driver, a routine that disables the timer driver, a routine that specifies the routine to run when the timer hits a rollover, a routine that returns the period of the timer, and a routine that returns the frequency of the timer. INTERNAL This library now allows System Viewer, the POSIX trace system, and Cafe to share the timestamp driver. System Viewer requires that when the timestamp is connected, the rollover interrupt will be connected to the routine nominated by System Viewer. When POSIX trace is involved, this cannot be done directly, because trace needs to count rollovers. When System Viever calls the connect routine, we actually connect a function in this library, but note the function that System Viewer requested. When the interrupt is handled, the function here counts the interrupt, and then passes control on to the function specified by System Viewer. INCLUDE FILES: SEE ALSO: wvLib, .I Wind River System Viewer User's Guide */ /* includes */ #include #include #include #include #include #include #include #include /* defines */ #define TEN_EXP9 (1000000000L) #define MAX_CLIENTS 3 #define WV_TMR_INDEX 0 #define TRACE_TMR_INDEX 1 #define CAFE_TMR_INDEX 2 /* typedefs */ /* locals */ LOCAL struct ts_desc ts_descs[MAX_CLIENTS]; LOCAL WDOG_ID rolloverWd = NULL; /* globals */ struct timespec traceTimerStartValue = {0,0}; /* forward declarations */ /******************************************************************************* * * localRolloverHandler - handler for all rollover interrupts * * This function uses the supplied ts_desc structure to determine which * timestamp has rolled over. Since a timestamp source may be shared with * several clients, it then calls the handlers of any client which uses the same * source. If the interrupt source was a watchdog, it is restarted. * * RETURNS: N/A * * \NOMANUAL * */ LOCAL void localRolloverHandler ( struct ts_desc * ts_desc ) { int i; unsigned int (*ts)(void); /* get identification of which timestamp source */ ts = ts_desc->ts; /* call rollover function for all enabled clients using this source */ for (i = 0; i < MAX_CLIENTS; i++) { if (ts == ts_descs [i].ts && ts_descs [i].client_handler != NULL && ts_descs [i].enabled) { /* call handler */ ts_descs [i].client_handler((_Vx_usr_arg_t)(ts_descs + i)); } } if (ts_desc->useWd) (void)wdStart (rolloverWd, 1, (FUNCPTR)localRolloverHandler, (_Vx_usr_arg_t)ts_desc); } /******************************************************************************* * * localEnable - enable a timestamp for a client * * This function enables a timestamp source for a client. If the source is * already enabled, then it just enables the client. Otherwise, it enables both * the source and client * * RETURNS: N/A * * \NOMANUAL * */ LOCAL STATUS localEnable ( struct ts_desc * ts /* timestamp to enable */ ) { int enabledCount = 0; unsigned int i; STATUS res = OK; if (ts->enabled) return OK; /* count enabled clients which use the same underlying function */ for (i = 0; i < MAX_CLIENTS; i++) { if (ts_descs [i].ts == ts->ts && ts_descs [i].enabled) { enabledCount++; } } /* try not to enable a sourcce if it's already done */ if (enabledCount == 0) res = ts->source_enable(); /* clear time stamp data */ ts->lastRollover0 = 0; ts->lastRollover1 = 0; ts->lastTimestamp = ts->ts(); ts->enabled = TRUE; /* if wd for this time source, start it now */ if (ts->useWd) { if (rolloverWd == NULL) return ERROR; res = wdStart (rolloverWd, 1, (FUNCPTR)localRolloverHandler, (_Vx_usr_arg_t)ts); } return res; } /******************************************************************************* * * localDisable - disable a timestamp for a client * * This function disables a timestamp source for a client. If the requesting * client is the only one for this timestamp source, then the source is disabled. * * RETURNS: N/A * * \NOMANUAL * */ LOCAL STATUS localDisable ( struct ts_desc * ts /* timestamp to disable */ ) { int enabledCount = 0; int wdClients = 0; unsigned int i; STATUS res = OK; if (!ts->enabled) return OK; /* count enabled clients using this source */ for (i = 0; i < MAX_CLIENTS; i++) { if (ts_descs [i].ts == ts->ts && ts_descs [i].enabled) { enabledCount++; if (ts_descs [i].useWd) wdClients++; } } if (enabledCount == 1) res = ts->source_disable(); ts->enabled = FALSE; if (!ts->useWd) return res; /* client uses watchdog: If there are no others, using it, cancel it */ if (wdClients == 1) { if (rolloverWd == NULL) return ERROR; wdCancel (rolloverWd); } return res; } #ifdef _WRS_CONFIG_SV_INSTRUMENTATION /******************************************************************************* * * localWvEnable - enable a timestamp for System Viewer * * This function enables a previously-registered timestamp source for System * Viewer. * * RETURNS: N/A * * \NOMANUAL * */ LOCAL void localWvEnable (void) { localEnable (ts_descs + WV_TMR_INDEX); } /******************************************************************************* * * localWvDisable - diable a timestamp for System Viewer * * This function disables a previously-registered timestamp source for a System * Viewer. * * RETURNS: N/A * * \NOMANUAL * */ LOCAL void localWvDisable (void) { localDisable (ts_descs + WV_TMR_INDEX); } #endif /* _WRS_CONFIG_SV_INSTRUMENTATION */ /******************************************************************************* * * localCafeEnable - enable a timestamp for Cafe * * This function enables a previously-registered timestamp source for Cafe * * RETURNS: N/A * * \NOMANUAL * */ LOCAL void localCafeEnable (void) { localEnable (ts_descs + CAFE_TMR_INDEX); } /******************************************************************************* * * localCafeDisable - disable a timestamp for Cafe * * This function disables a previously-registered timestamp source for Cafe * * RETURNS: N/A * * \NOMANUAL * */ LOCAL void localCafeDisable (void) { localDisable (ts_descs + CAFE_TMR_INDEX); } /******************************************************************************* * * traceTmrMuxEnable - enable a timestamp for POSIX trace * * This function enables a previously-registered timestamp source for POSIX * tracing * * RETURNS: N/A * * \NOMANUAL * */ void traceTmrMuxEnable (void) { localEnable (ts_descs + TRACE_TMR_INDEX); } /******************************************************************************* * * traceTmrMuxDisable - disable a timestamp for POSIX trace * * This function disables a previously-registered timestamp source for POSIX * tracing * * RETURNS: N/A * * \NOMANUAL * */ void traceTmrMuxDisable (void) { localDisable (ts_descs + TRACE_TMR_INDEX); } /******************************************************************************* * * localConnect - connect a rollover handler for a client * * This function is called by a client to attach a handler, which will be called * later when a timestamp rolls over. It notes the requested handler in the * supplied ts_desc structure, and connects the local handler. * * It returns the result of calling the rollover connect function for the * requested timestamp. * * RETURNS: OK or ERROR * * \NOMANUAL * */ LOCAL STATUS localConnect ( void (*client_handler)(_Vx_usr_arg_t), struct ts_desc * ts ) { STATUS result; ts->client_handler = client_handler; ts->useWd = FALSE; /* interrupt will call nominated local function with ts_desc */ result = ts->source_connect((void (*)(_Vx_usr_arg_t))localRolloverHandler, (_Vx_usr_arg_t)ts); return result; } #ifdef _WRS_CONFIG_SV_INSTRUMENTATION /******************************************************************************* * * wvRolloverConnect - connect a rollover handler for System Viewer * * This function attempts to connect a rollover handler to the System Viewer * timestamp source * * RETURNS: OK or ERROR * * \NOMANUAL * */ LOCAL STATUS wvRolloverConnect ( void (*handler)(_Vx_usr_arg_t), struct ts_desc *ts ) { STATUS res; res = localConnect(handler, ts_descs + WV_TMR_INDEX); if (res == ERROR) ts_descs [WV_TMR_INDEX].client_handler = NULL; return res; } #endif /* _WRS_CONFIG_SV_INSTRUMENTATION */ /******************************************************************************* * * rolloverHandler - count rollover for a timestamp source * * This function records a rollover for a client * * RETURNS: OK or ERROR * * \NOMANUAL * */ LOCAL void rolloverHandler ( _Vx_usr_arg_t arg ) { struct ts_desc * ts = (struct ts_desc *)arg; unsigned int p = ts->period (); int key = INT_CPU_LOCK (); ts->lastRollover0 += p; ts->lastTimestamp = ts->ts(); /* ensure both lastRollover0 and lastTimestamp have been updated */ VX_MEM_BARRIER_RW(); ts->lastRollover1 += p; INT_CPU_UNLOCK (key); } #ifdef _WRS_CONFIG_SV_INSTRUMENTATION /******************************************************************************* * * wvTmrRegister - register a timestamp timer * * This routine registers a timestamp routine for each of the following: * .iP * a timestamp routine, which returns a timestamp when called (must be called * with interrupts locked). * .iP * a timestamp routine, which returns a timestamp when called (locks interrupts). * .iP * an enable-timer routine, which enables the timestamp timer. * .iP * a disable-timer routine, which disables the timestamp timer. * .iP * a connect-to-timer routine, which connects a handler to be run when the timer * rolls over; this routine should return ERROR if the system clock tick is to be * used. * .iP * a period-of-timer routine, which returns the period of the timer. * .iP * a frequency-of-timer routine, which returns the frequency of the timer. * .LP * * If any of these routines is set to NULL, the behavior of instrumented code * is undefined. * * RETURNS: N/A * * ERRNO: */ void wvTmrRegister ( UINTFUNCPTR wvTmrRtn, /* timestamp function */ UINTFUNCPTR wvTmrLockRtn, /* int-locked timestamp function */ FUNCPTR wvTmrEnable, /* enable timestamp */ FUNCPTR wvTmrDisable, /* disable timestamp */ FUNCPTR wvTmrConnect, /* connect rollover */ UINTFUNCPTR wvTmrPeriod, /* period of timestamp */ UINTFUNCPTR wvTmrFreq /* frequency of timestamp */ ) { ts_descs [WV_TMR_INDEX].ts = wvTmrRtn; ts_descs [WV_TMR_INDEX].tsLock = wvTmrLockRtn; ts_descs [WV_TMR_INDEX].source_enable = wvTmrEnable; ts_descs [WV_TMR_INDEX].source_disable = wvTmrDisable; ts_descs [WV_TMR_INDEX].period = wvTmrPeriod; ts_descs [WV_TMR_INDEX].frequency = wvTmrFreq; ts_descs [WV_TMR_INDEX].source_connect = wvTmrConnect; ts_descs [WV_TMR_INDEX].enable = (FUNCPTR)localWvEnable; ts_descs [WV_TMR_INDEX].disable = (FUNCPTR)localWvDisable; ts_descs [WV_TMR_INDEX].connect = (FUNCPTR)wvRolloverConnect; ts_descs [WV_TMR_INDEX].enabled = FALSE; if (rolloverWd == NULL) rolloverWd = wdCreate (); /* use our rollover connect function, having saved the one the client requested into source_connect */ _func_tmrConnect = (FUNCPTR)wvRolloverConnect; /* because this is WV tmr, connect to global funcptrs */ _func_tmrDisable = localWvDisable; _func_tmrEnable = localWvEnable; _func_tmrFreq = (FUNCPTR)wvTmrFreq; _func_tmrPeriod = (FUNCPTR)wvTmrPeriod; _func_tmrStamp = (FUNCPTR)wvTmrRtn; _func_tmrStampLock = (FUNCPTR)wvTmrLockRtn; } #endif /*_WRS_CONFIG_SV_INSTRUMENTATION */ /******************************************************************************* * * cafeTmrRegister - register a timestamp driver for Cafe analysis system * * This routine registers a timestamp routine for each of the following: * * .iP * a timestamp routine, which returns a timestamp when called * .iP * a timestamp routine, which returns a timestamp when called. Interrupts are * lockded while the timestamp is read * .iP * an enable-timer routine, which enables the timestamp timer. * .iP * a disable-timer routine, which disables the timestamp timer. * .iP * a connect-to-timer routine, which connects a handler to be run when the timer * rolls over; this routine should return ERROR if the system clock tick is to be * used. * .iP * a period-of-timer routine, which returns the period of the timer. * .iP * a frequency-of-timer routine, which returns the frequency of the timer. * .LP * * If any of these routines is set to NULL, the behavior of instrumented code * is undefined. * * RETURNS: Pointer to TS_DESC structure containing cached function pointers and * values used by timestamp functions for Cafe * * ERRNO: * */ TS_DESC * cafeTmrRegister ( UINTFUNCPTR tmrRtn, /* timestamp function */ UINTFUNCPTR tmrLockRtn, /* int-locked timestamp function */ FUNCPTR tmrEnable, /* enable timestamp */ FUNCPTR tmrDisable, /* disable timestamp */ FUNCPTR tmrConnect, /* connect rollover */ UINTFUNCPTR tmrPeriod, /* period of timestamp */ UINTFUNCPTR tmrFreq /* frequency of timestamp */ ) { ts_descs [CAFE_TMR_INDEX].ts = tmrRtn; ts_descs [CAFE_TMR_INDEX].tsLock = tmrLockRtn; ts_descs [CAFE_TMR_INDEX].source_enable = tmrEnable; ts_descs [CAFE_TMR_INDEX].source_disable = tmrDisable; ts_descs [CAFE_TMR_INDEX].period = tmrPeriod; ts_descs [CAFE_TMR_INDEX].frequency = tmrFreq; ts_descs [CAFE_TMR_INDEX].source_connect = tmrConnect; ts_descs [CAFE_TMR_INDEX].enable = (FUNCPTR)localCafeEnable; ts_descs [CAFE_TMR_INDEX].disable = (FUNCPTR)localCafeDisable; ts_descs [CAFE_TMR_INDEX].connect = NULL; /* no external rollover handler */ if (rolloverWd == NULL) rolloverWd = wdCreate (); if (localConnect (rolloverHandler, ts_descs + CAFE_TMR_INDEX) != OK) { /* source uses sysClk as rollover */ ts_descs [CAFE_TMR_INDEX].useWd = TRUE; } return ts_descs + CAFE_TMR_INDEX; } /******************************************************************************* * * traceTmrRegister - register a timestamp driver for POSIX trace * * This routine registers a timestamp routine for each of the following: * * .iP * a timestamp routine, which returns a timestamp when called (must be called * with interrupts locked). * .iP * an enable-timer routine, which enables the timestamp timer. * .iP * a disable-timer routine, which disables the timestamp timer. * .iP * a connect-to-timer routine, which connects a handler to be run when the timer * rolls over; this routine should return ERROR if the system clock tick is to be * used. * .iP * a period-of-timer routine, which returns the period of the timer. * .iP * a frequency-of-timer routine, which returns the frequency of the timer. * .LP * * If any of these routines is set to NULL, the behavior of instrumented code * is undefined. * * RETURNS: N/A * * ERRNO: * * NOMANUAL */ void traceTmrRegister ( UINTFUNCPTR tmrRtn, FUNCPTR tmrEnable, FUNCPTR tmrDisable, FUNCPTR tmrConnect, UINTFUNCPTR tmrPeriod, UINTFUNCPTR tmrFreq ) { ts_descs [TRACE_TMR_INDEX].ts = tmrRtn; ts_descs [TRACE_TMR_INDEX].tsLock = tmrRtn; ts_descs [TRACE_TMR_INDEX].source_enable = tmrEnable; ts_descs [TRACE_TMR_INDEX].source_disable = tmrDisable; ts_descs [TRACE_TMR_INDEX].period = tmrPeriod; ts_descs [TRACE_TMR_INDEX].frequency = tmrFreq; ts_descs [TRACE_TMR_INDEX].source_connect = tmrConnect; ts_descs [TRACE_TMR_INDEX].enabled = FALSE; ts_descs [TRACE_TMR_INDEX].enable = (FUNCPTR)traceTmrMuxEnable; ts_descs [TRACE_TMR_INDEX].disable = (FUNCPTR)traceTmrMuxDisable; ts_descs [TRACE_TMR_INDEX].connect = NULL; /* no external rollover handler */ if (rolloverWd == NULL) rolloverWd = wdCreate (); if (localConnect (rolloverHandler, ts_descs + TRACE_TMR_INDEX) != OK) { /* source uses sysClk as rollover */ ts_descs [TRACE_TMR_INDEX].useWd = TRUE; } } /******************************************************************************* * * ticksToTime - convert system-specific time to struct timespec * * This function takes system clock ticks, and the high-res timer value, and * converts to seconds and nanoseconds. It also applies an offset, which could be * derived from time-of-day at system startup * * \INTERNAL * The nowTicks variable (64bit) will contain the time since boot, in units of the * high-resolution clock, so it's a bit like the Intel TSC. At a clockrate * of (say) 4GHz, it will overflow in 146 years. * * \NOMANUAL */ LOCAL STATUS ticksToTime ( UINT64 ticks, /* extended tick count */ struct ts_desc * ts_desc, /* timestamp selection */ const struct timespec * offset, /* offset */ struct timespec * now /* result */ ) { UINT64 nowTicks; /* timestamp in clock ticks */ UINT64 nowFracSec; /* fraction of second, high-res ticks */ UINT32 tsFreq; UINT32 tsPeriod; tsFreq = ts_desc->frequency (); tsPeriod = ts_desc->period (); now->tv_sec = (time_t) (ticks / tsFreq); nowFracSec = ticks % tsFreq; /* Convert from ticks to nsec */ now->tv_nsec = (long) (nowFracSec * TEN_EXP9 / tsFreq); now->tv_sec += offset->tv_sec; now->tv_nsec += offset->tv_nsec; while (now->tv_nsec >= TEN_EXP9) { now->tv_nsec -= TEN_EXP9; now->tv_sec++; } return OK; } /******************************************************************************* * * wvTmrTimestamp64ToTime - convert system-specific time to struct timespec * * This function converts a 64bit timestamp into struct timespec * */ STATUS wvTmrTimestamp64ToTime ( UINT64 ticks, /* extended tick count */ struct ts_desc * ts_desc, /* timestamp selection */ struct timespec * now /* result */ ) { struct timespec offset = {0,0}; return (ticksToTime (ticks, ts_desc, &offset, now)); } /******************************************************************************* * * wvTmrTimestamp64Get - get an extended timestamp * * This function reads a 64bit timestamp from the requested timestamp source * * RETURNS: OK ,or ERROR * */ STATUS wvTmrTimestamp64Get ( TS_DESC * ts, /* timestamp to get timestamp from */ unsigned long long * result /* 64bit timestamp result */ ) { unsigned long long rollover0; unsigned long long rollover1; unsigned int timestamp; unsigned int timestampSave; if (ts == NULL) return ERROR; do { rollover0 = ts->lastRollover0; timestampSave = ts->lastTimestamp; timestamp = ts->ts(); rollover1 = ts->lastRollover1; } while (rollover0 != rollover1); if (timestamp < timestampSave) rollover0 += ts->period(); *result = rollover0 + timestamp; return OK; } /******************************************************************************* * * wvTmrFrequency64Get - get timestamp frequency * * This function retrieves 64bit frequency from the requested timestamp source * * RETURNS: OK ,or ERROR * */ STATUS wvTmrFrequency64Get ( TS_DESC * ts, /* timestamp to get timestamp from */ unsigned long long * result /* 64bit frequency result */ ) { if (ts == NULL) return ERROR; *result = ts->frequency(); return OK; } /******************************************************************************* * * wvTmrEnable - enable timestamp source * * This function enables the source of the timestamp * * RETURNS: OK ,or ERROR * */ STATUS wvTmrEnable ( TS_DESC * ts /* timestamp to enable */ ) { if (ts == NULL) return ERROR; return ts->enable(); } /******************************************************************************* * * wvTmrDisable - disable timestamp source * * This function disables the source of the timestamp * * RETURNS: OK ,or ERROR * */ STATUS wvTmrDisable ( TS_DESC * ts /* timestamp to enable */ ) { if (ts == NULL) return ERROR; return ts->disable(); } /******************************************************************************* * * traceTimestampGet - get a timestamp in 'struct timespec' form * * This function uses the timestamp driver and the tick count to get the time * * The timestamp driver provides high resolution, but the result has to be * normalised. To do this we use the timestamp frequency and period, and the * number of times the counter has rolled over since startup. * The frequency and period are known from the driver, the number of rollovers is * updated during the rollover interrupt. * * NOMANUAL */ STATUS traceTimestampGet ( struct timespec * pTimestamp /* destination for timestamp */ ) { unsigned long long tmrTimestamp; if (wvTmrTimestamp64Get (ts_descs + TRACE_TMR_INDEX, &tmrTimestamp) != OK) return ERROR; ticksToTime (tmrTimestamp, ts_descs + TRACE_TMR_INDEX, &traceTimerStartValue, pTimestamp); return OK; } /******************************************************************************* * * traceTmrResolutionGet - get resolution of timestamp source, in nanoseconds * * Write the resolution of the timestamp source into the supplied timespec. If * the timestamp resolution cannot be obtained (maybe the timestamp driver is not * available) then the timespec structure will be filled with zeroes. * * RETURNS: OK, or ERROR. * */ STATUS traceTmrResolutionGet ( struct timespec * pTimestamp /* destination for resolution */ ) { UINT32 freq; long resolution; int result = OK; if (ts_descs [TRACE_TMR_INDEX].frequency != NULL) { freq = (ts_descs [TRACE_TMR_INDEX].frequency)(); resolution = TEN_EXP9 / freq; pTimestamp->tv_nsec = resolution % TEN_EXP9; pTimestamp->tv_sec = resolution / TEN_EXP9; } else { pTimestamp->tv_nsec = 0; pTimestamp->tv_sec = 0; result = ERROR; } return (result); }