tstRTR0Timer.cpp revision 0e382f5deb3d16f198285292f9427563d55fc6b7
/* $Id$ */
/** @file
* IPRT R0 Testcase - Timers.
*/
/*
* Copyright (C) 2009-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "tstRTR0Timer.h"
#include "tstRTR0Common.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct
{
/** Array of nano second timestamp of the first few shots. */
/** The number of shots. */
/** The shot at which action is to be taken. */
/** The RC of whatever operation performed in the handler. */
int volatile rc;
/** Set if it's a periodic test. */
bool fPeriodic;
/** Test specific stuff. */
union
{
/** tstRTR0TimerCallbackU32ChangeInterval parameters. */
struct
{
/** The interval change step. */
/** The current timer interval. */
/** The minimum interval. */
/** The maximum interval. */
/** Direction flag; false = decrement, true = increment. */
bool fDirection;
/** The number of steps between each change. */
} ChgInt;
/** tstRTR0TimerCallbackSpecific parameters. */
struct
{
/** The expected CPU. */
/** Set if this failed. */
bool fFailed;
} Specific;
} u;
typedef TSTRTR0TIMERS1 *PTSTRTR0TIMERS1;
/**
* Per cpu state for an omni timer test.
*/
typedef struct TSTRTR0TIMEROMNI1
{
/** When we started receiving timer callbacks on this CPU. */
/** When we received the last tick on this timer. */
/** The number of ticks received on this CPU. */
typedef TSTRTR0TIMEROMNI1 *PTSTRTR0TIMEROMNI1;
/**
* Callback which increments a 32-bit counter.
*
* @param pTimer The timer.
* @param iTick The current tick.
* @param pvUser The user argument.
*/
{
if (iCpu < RTCPUSET_MAX_CPUS)
{
{
RTR0TESTR0_CHECK_MSG(iCountedTick == 1, ("iCountedTick=%u iCpu=%d idCpu=%u\n", iCountedTick, iCpu, idCpu));
}
}
}
/**
* Callback which increments a 32-bit counter.
*
* @param pTimer The timer.
* @param iTick The current tick.
* @param pvUser The user argument.
*/
static DECLCALLBACK(void) tstRTR0TimerCallbackSpecific(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
{
RTR0TESTR0_CHECK_MSG(pState->u.Specific.idCpu == idCpu, ("idCpu=%u, expected %u\n", idCpu, pState->u.Specific.idCpu));
else
}
/**
* Callback which changes the interval at each invocation.
*
* The changes are governed by TSTRTR0TIMERS1::ChangeInterval. The callback
* calls RTTimerStop at iActionShot.
*
* @param pTimer The timer.
* @param iTick The current tick.
* @param pvUser The user argument.
*/
static DECLCALLBACK(void) tstRTR0TimerCallbackChangeInterval(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
{
else
{
{
{
}
}
else
{
{
}
}
}
}
/**
* Callback which increments destroy the timer when it fires.
*
* @param pTimer The timer.
* @param iTick The current tick.
* @param pvUser The user argument.
*/
static DECLCALLBACK(void) tstRTR0TimerCallbackDestroyOnce(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
{
else
}
/**
* Callback which increments restarts a timer once.
*
* @param pTimer The timer.
* @param iTick The current tick.
* @param pvUser The user argument.
*/
static DECLCALLBACK(void) tstRTR0TimerCallbackRestartOnce(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
{
else
}
/**
* Callback which increments a 32-bit counter.
*
* @param pTimer The timer.
* @param iTick The current tick.
* @param pvUser The user argument.
*/
static DECLCALLBACK(void) tstRTR0TimerCallbackU32Counter(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
{
else
}
#ifdef SOME_UNUSED_FUNCTION
/**
* Checks that the interval between two timer shots are within the specified
* range.
*
* @returns 0 if ok, 1 if bad.
* @param iShot The shot number (for bitching).
* @param uPrevTS The time stamp of the previous shot (ns).
* @param uThisTS The timer stamp of this shot (ns).
* @param uMin The minimum interval (ns).
* @param uMax The maximum interval (ns).
*/
static int tstRTR0TimerCheckShotInterval(uint32_t iShot, uint64_t uPrevTS, uint64_t uThisTS, uint32_t uMin, uint32_t uMax)
{
RTR0TESTR0_CHECK_MSG_RET(uDelta >= uMin, ("iShot=%u uDelta=%lld uMin=%u\n", iShot, uDelta, uMin), 1);
RTR0TESTR0_CHECK_MSG_RET(uDelta <= uMax, ("iShot=%u uDelta=%lld uMax=%u\n", iShot, uDelta, uMax), 1);
return 0;
}
#endif
/**
* Checks that the interval between timer shots are within a certain range.
*
* @returns Number of violations (i.e. 0 is ok).
* @param pState The state.
* @param uStartNsTS The start time stamp (ns).
* @param uMin The minimum interval (ns).
* @param uMax The maximum interval (ns).
*/
static int tstRTR0TimerCheckShotIntervals(PTSTRTR0TIMERS1 pState, uint64_t uStartNsTS, uint32_t uMin, uint32_t uMax)
{
{
}
return cBadShots;
}
/**
* Service request callback function.
*
* @returns VBox status code.
* @param pSession The caller's session.
* @param u64Arg 64-bit integer argument.
* @param pReqHdr The request header. Input / Output. Optional.
*/
{
/*
* Common parameter and state variables.
*/
{
RTR0TESTR0_CHECK_MSG(cNsSysHz > UINT32_C(1000) && cNsSysHz < UINT32_C(1000000000), ("%u", cNsSysHz));
RTR0TESTR0_CHECK_MSG(cNsMaxHighResHz > UINT32_C(1) && cNsMaxHighResHz < UINT32_C(1000000000), ("%u", cNsMaxHighResHz));
return VINF_SUCCESS;
}
/*
* The big switch.
*/
switch (uOperation)
{
{
/* Create a one-shot timer and take one shot. */
RTR0TESTR0_CHECK_RC_BREAK(RTTimerCreateEx(&pTimer, 0, fFlags, tstRTR0TimerCallbackU32Counter, &State),
do /* break loop */
{
RTThreadSleep(5);
/* check that it is restartable. */
RTThreadSleep(5);
/* check that it respects the timeout value and can be cancelled. */
RTThreadSleep(1);
/* Check some double starts and stops (shall not assert). */
RTThreadSleep(1);
} while (0);
break;
}
#if 1 /* might have to disable this for some host... */
{
/* Create a one-shot timer and restart it in the callback handler. */
{
RTR0TESTR0_CHECK_RC_BREAK(RTTimerCreateEx(&pTimer, 0, fFlags, tstRTR0TimerCallbackRestartOnce, &State),
State.iActionShot = 0;
do /* break loop */
{
RTThreadSleep(5);
} while (0);
}
break;
}
#endif
#if 1 /* might have to disable this for some host... */
{
/* Create a one-shot timer and destroy it in the callback handler. */
{
RTR0TESTR0_CHECK_RC_BREAK(RTTimerCreateEx(&pTimer, 0, fFlags, tstRTR0TimerCallbackDestroyOnce, &State),
State.iActionShot = 0;
do /* break loop */
{
for (uint32_t i = 0; i < 1000 && (ASMAtomicUoReadU32(&State.cShots) < 1 || State.rc == VERR_IPE_UNINITIALIZED_STATUS); i++)
RTThreadSleep(5);
} while (0);
}
break;
}
#endif
{
{
State.iActionShot = 0;
if (rc == VERR_NOT_SUPPORTED)
{
RTR0TestR0Info("specific timer are not supported, skipping\n");
break;
}
{
RTR0TESTR0_CHECK_RC_BREAK(RTTimerStart(pTimer, (i & 2 ? cNsSysHz : cNsSysHz / 2) * (i & 1)), VINF_SUCCESS);
RTThreadSleep(5);
("cShots=%u iCpu=%u i=%u iCurCpu=%u cNsElapsed=%'llu\n",
}
if (RTR0TestR0HaveErrors())
break;
}
break;
}
{
/* Create a periodic timer running at 10 HZ. */
RTR0TESTR0_CHECK_RC_BREAK(RTTimerCreateEx(&pTimer, u10HzAsNs, fFlags, tstRTR0TimerCallbackU32Counter, &State),
{
RTThreadSleep(10);
break;
}
break;
}
{
/* create, start, stop & destroy high res timers a number of times. */
for (uint32_t i = 0; i < 40; i++)
{
RTR0TESTR0_CHECK_RC_BREAK(RTTimerCreateEx(&pTimer, cNsSysHz, fFlags, tstRTR0TimerCallbackU32Counter, &State),
for (uint32_t j = 0; j < 10; j++)
{
RTThreadSleep(1);
}
}
break;
}
{
/* Initialize the test parameters, using the u64Arg value for selecting variations. */
{
}
else
{
}
State.u.ChgInt.cNsChangeStep = (State.u.ChgInt.cNsMaxInterval - State.u.ChgInt.cNsMinInterval) / 10;
RTR0TESTR0_CHECK_MSG_BREAK(State.u.ChgInt.cNsMinInterval > 1000, ("%u\n", State.u.ChgInt.cNsMinInterval));
RTR0TESTR0_CHECK_MSG_BREAK(State.u.ChgInt.cNsMaxInterval > State.u.ChgInt.cNsMinInterval, ("max=%u min=%u\n", State.u.ChgInt.cNsMaxInterval, State.u.ChgInt.cNsMinInterval));
/* create the timer and check if RTTimerChangeInterval is supported. */
RTR0TESTR0_CHECK_RC_BREAK(RTTimerCreateEx(&pTimer, cNsSysHz, fFlags, tstRTR0TimerCallbackChangeInterval, &State),
if (rc == VERR_NOT_SUPPORTED)
{
RTR0TestR0Info("RTTimerChangeInterval not supported, skipped");
break;
}
/* do the test. */
RTR0TESTR0_CHECK_RC_BREAK(RTTimerStart(pTimer, u64Arg & 2 ? State.u.ChgInt.cNsCurInterval : 0), VINF_SUCCESS);
for (uint32_t k = 0;
k < 1000
k++)
RTThreadSleep(10);
RTR0TESTR0_CHECK_MSG_BREAK(rc == VERR_TIMER_SUSPENDED || rc == VINF_SUCCESS, ("rc = %Rrc (RTTimerStop)\n", rc));
break;
}
{
{
State.iActionShot = 0;
if (rc == VERR_NOT_SUPPORTED)
{
RTR0TestR0Info("specific timer are not supported, skipping\n");
break;
}
{
RTR0TESTR0_CHECK_RC_BREAK(RTTimerStart(pTimer, (i & 2 ? cNsSysHz : cNsSysHz / 2) * (i & 1)), VINF_SUCCESS);
RTThreadSleep(5);
("cShots=%u iCpu=%u i=%u iCurCpu=%u cNsElapsed=%'llu\n",
}
if (RTR0TestR0HaveErrors())
break;
}
break;
}
{
/* Create a periodic timer running at max host frequency, but no more than 1000 Hz. */
cNsInterval *= 2;
PTSTRTR0TIMEROMNI1 paStates = (PTSTRTR0TIMEROMNI1)RTMemAllocZ(sizeof(paStates[0]) * RTCPUSET_MAX_CPUS);
if (rc == VERR_NOT_SUPPORTED)
{
}
{
/* reset the state */
{
}
/* run it for 1 second. */
RTThreadSleep(2);
uint64_t u64MaxStop = 0;
{
{
}
}
RTR0TESTR0_CHECK_MSG(cNsElapsed <= cNsElapsedX + 100000, ("%llu, %llu", cNsElapsed, cNsElapsedX)); /* the fudge factor is time drift */
/* Check tick counts. ASSUMES no cpu on- or offlining.
This only catches really bad stuff. */
{
("min=%u, ticks=%u, avg=%u max=%u, iCpu=%u, interval=%'u, elapsed=%'llu/%'llu\n",
}
else
}
break;
}
}
/* The error indicator is the '!' in the message buffer. */
return VINF_SUCCESS;
}