1665 lines
55 KiB
C
1665 lines
55 KiB
C
/* OS/2 Version
|
|
* Timer.c - Source file for a statistical
|
|
* dll package that exports four
|
|
* entry points:
|
|
* a) TimerOpen
|
|
* b) TimerInit
|
|
* c) TimerRead
|
|
* d) TimerClose
|
|
* e) TimerReport
|
|
* f) TimerQueryPerformanceCounter
|
|
* g) TimerConvertTicsToUSec
|
|
*
|
|
* Entry point a) opens a timer object
|
|
* and returns a handle to the timer. This
|
|
* handle is an index into an array of timer
|
|
* objects (structs) that are allocated at
|
|
* the time of the initialization of the dll.
|
|
* This ensures that allocation is done once only.
|
|
* Each application program will call this
|
|
* this function so that it has its own set
|
|
* of timers to use with TimerInit and TimerRead.
|
|
* The units of the time returned by TimerRead
|
|
* is also made available as a parameter to
|
|
* this call.
|
|
*
|
|
* Entry point b) is called by the application
|
|
* before commencing a timing operation. This
|
|
* function is called with a handle to a timer
|
|
* object that was opened. This function has to
|
|
* to be called before a call to TimerRead. The
|
|
* current time is stored in the timer object.
|
|
*
|
|
* Entry point c) is called each time the time
|
|
* since the previous call to TimerInit is
|
|
* desired. This call also uses the handle to
|
|
* a timer that has been previosly opened. The
|
|
* current time is obtained form the lowlevel
|
|
* timer and this and the time at TimerInit time
|
|
* are used, along with the clock frequency and
|
|
* the return time units and the elapsed time
|
|
* is obtained and returned as a ULONG.
|
|
*
|
|
* Entry point d) is called whenever an opened
|
|
* timer is not needed any longer. This call
|
|
* resets the timer and makes this timer as
|
|
* available to future calls to TimerOpen.
|
|
*
|
|
* Entry point e) returns the time obtained during
|
|
* the last call to TimerInit, TimerRead and the
|
|
* last returned time.
|
|
*
|
|
* Entry point f) accepts pointers to 2 64 bit
|
|
* vars. Upon return, the first will contain the
|
|
* the current timer tic count and the second,
|
|
* if not NULL, will point to the timer freq.
|
|
*
|
|
* Entry point g) accepts Elapsed Tics as ULONG,
|
|
* Frequency as a ULONG and returns the time in
|
|
* microsecs. as a ULONG.
|
|
*
|
|
* The dll initialization routine does the
|
|
* following:
|
|
* a) Obtains the timer overhead for calibration
|
|
* purposes.
|
|
* b) Allocates memory for a large number of
|
|
* timer objects (this will be system dep).
|
|
* c) Initializes each timer objects "Units'
|
|
* element to a "TIMER_FREE" indicator.
|
|
* d) Determines the lowlevel timer's frequency.
|
|
*
|
|
* TimerRead uses an external asm routine to perform
|
|
* its computation for elapsed time.
|
|
*
|
|
* Created - Paramesh Vaidyanathan (vaidy)
|
|
* Initial Version - October 18, '90
|
|
*
|
|
* Modified to include f). - Feb. 14, 1992. (vaidy).
|
|
*/
|
|
|
|
char *COPYRIGHT = "Copyright Microsoft Corporation, 1991";
|
|
|
|
#ifdef SLOOP
|
|
#define INCL_DOSINFOSEG
|
|
#define INCL_DOSDEVICES
|
|
#define INCL_DOSPROCESS
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (OS2386) || defined (SLOOP))
|
|
#include <os2.h>
|
|
#include <stdio.h>
|
|
#define INCL_DOS
|
|
#define INCL_DOSPROFILE
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (SLOOP))
|
|
#include <math.h>
|
|
#endif
|
|
|
|
#if (defined (NTNAT) || defined (WIN32) || defined (OS2SS))
|
|
#include <nt.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#ifdef WIN16
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "timing.h"
|
|
/*****************************END OF INCLUDES*************************/
|
|
#define ITER_FOR_OVERHEAD 250
|
|
#define SUCCESS_OK 0
|
|
#define ONE_MILLION 1000000L
|
|
#define MICROSEC_FACTOR 1000000
|
|
#define TIMER_FREQ 1193167L /* clock frequency - Hz */
|
|
/*********************************************************************/
|
|
Timer pTimer [MAX_TIMERS]; /* array of timer struct */
|
|
|
|
BOOL bTimerInit = FALSE; /* TRUE indicates low level timer exists */
|
|
ULONG ulTimerOverhead = 50000L; /* timer overhead stored here */
|
|
BOOL bCalibrated = FALSE; /* TRUE subtracts overhead also */
|
|
ULONG ulFreq; /* timer frequency */
|
|
LONG aScaleValues[] = {1000000000L, 1000000L, 1000L, 1L, 10L, 1000L};
|
|
/* this is the table for scaling the units */
|
|
ULONG ulElapsedTime = 0L;
|
|
/********************* internal unexported routines ***************/
|
|
ULONG CalibrateTimerForOverhead (VOID);
|
|
/*****************DEFINE VARIBLES AND PROTOTYPE FNS. FOR PLATFORMS*****/
|
|
#if (defined (OS2386) || defined (WIN16)) /* use for Win with new timer */
|
|
extern VOID FAR PASCAL ReturnElapsedTime (PQWORD,
|
|
PQWORD,
|
|
ULONG far *,
|
|
_TimerUnits,
|
|
ULONG far *,
|
|
PQWORD);
|
|
#define MICROSECOND_INDEX 3 /* used as a hack. VTD 386 has introduced
|
|
some wierd problem if a unit that isn't
|
|
MICROSECONDS, is used */
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (OS2386))
|
|
APIRET APIENTRY DosTmrQueryFreq(PULONG pulTmrFreq);
|
|
APIRET APIENTRY DosTmrQueryTime(PQWORD pqwTmrTime);
|
|
SHORT GetTimerFreq (VOID);
|
|
int TIMING_INIT (VOID);
|
|
int mult64_32 (QWORD, ULONG, PQWORD);
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (SLOOP))
|
|
SHORT GetTimerFreq (VOID);
|
|
#define BIT0 0x1L /* for the division of 64 by 32 */
|
|
#define BIT31 0x80000000L /* - do - */
|
|
int Div64By32 (QWORD, ULONG, PQWORD);
|
|
int Sub64_64 (PQWORD, QWORD, QWORD);
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (OS2386) || defined (WIN16) || defined (SLOOP))
|
|
/* QWORD defined in the timing.h file for WIN platform */
|
|
QWORD qwCurrent,
|
|
qwPrev,
|
|
qwResult,
|
|
qwTemp;
|
|
#endif
|
|
|
|
#ifdef WIN16
|
|
USHORT FAR PASCAL PerfStartTime (VOID);
|
|
ULONG FAR PASCAL PerfGetTime (VOID);
|
|
#define RESERVED 0
|
|
#define INITCOUNT 50
|
|
#define SYNCERR 4 /* Max error between clocks, millisec */
|
|
#define MICROSYNCERR SYNCERR * 1000 /* Max error in microseconds */
|
|
#define SYNCCOUNT 50 /* Number of times to try to sync clks */
|
|
#define TIMER_SCALE .83810588 /* = 12 / 14.318 MHz */
|
|
#define MAX_MILLISECS 1800000L /* One Half Hour: 1800000L */
|
|
#define WRAP_8253 (ULONG)(0X10000 * TIMER_SCALE)
|
|
/* 44 milliseconds is the maximum acceptable period between system timer
|
|
updates */
|
|
#define MAX_SYSTEM_TIMER_INTERVAL 440 /* doc is wrong: value is
|
|
in tenths of milliseconds */
|
|
#define TIME_INTERVAL_LOW 40
|
|
#define TIME_INTERVAL_HIGH 48
|
|
|
|
ULONG ulMilliSecStart = 0;
|
|
ULONG ulCurrentMilliSec = 0L;
|
|
ULONG ulPrevSysTime;
|
|
/* previous time, to keep from falling back */
|
|
ULONG ulPrevRetTime;
|
|
/* previous time, to keep from falling back */
|
|
BOOL bInitialized = FALSE;
|
|
ULONG ulOverhead;
|
|
ULONG ulNumResyncs = 0L;
|
|
ULONG ulCurrentTime = 0L;
|
|
/* Return Values */
|
|
#define TIMER_ERROR (ULONG)~0
|
|
#define OK 0
|
|
#define SYSTEM_TIMER_ERROR (USHORT)~0
|
|
|
|
USHORT PASCAL FAR Start8253( void );
|
|
PASCAL FAR Stop8253( void );
|
|
VOID PASCAL FAR IntOn( void );
|
|
VOID PASCAL FAR IntOff( void );
|
|
VOID PASCAL FAR Int3( void );
|
|
VOID FAR PASCAL ReSynchronize( VOID );
|
|
#endif
|
|
|
|
#if (defined (NTNAT) || defined (OS2SS) || defined (WIN32))
|
|
|
|
#if !defined(WIN32)
|
|
NTSTATUS
|
|
NtQueryPerformanceCounter (
|
|
OUT PLARGE_INTEGER PerformanceCount,
|
|
OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
|
|
);
|
|
#endif
|
|
|
|
LARGE_INTEGER PerfFreq;
|
|
LARGE_INTEGER CountCurrent;
|
|
|
|
SHORT GetTimerFreq (VOID);
|
|
#endif
|
|
|
|
|
|
#ifdef WIN16
|
|
int FAR PASCAL LibMain(HANDLE, WORD, WORD, LPSTR);
|
|
VOID WinDllInit (VOID);
|
|
VOID FAR PASCAL WEP (int);
|
|
extern BOOL WinVTDAddr (VOID); // returns address of VTD
|
|
extern VOID WinVTDTime (PQWORD); // returns the time
|
|
BOOL fWinDllInitDone = FALSE; // flag to ensure that init has
|
|
// been done.
|
|
BOOL fUseApiTimer = FALSE; // if VTD not available
|
|
// use API Timer.
|
|
#endif
|
|
|
|
#if (defined (OS2SS) || defined (SLOOP))
|
|
int FAR PASCAL TIMING_INIT (VOID);
|
|
#endif
|
|
|
|
#ifdef SLOOP
|
|
int mult64_32 (QWORD, ULONG, PQWORD);
|
|
HFILE hDevHandle;
|
|
#endif
|
|
|
|
/****************** internal unexported routines end***************/
|
|
|
|
/*
|
|
* Function - TimerOpen (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT far * - address to which to return handle of
|
|
* the timer object.
|
|
* (b) TimerUnits - units in which to return time from
|
|
* TimerRead. It is one of the enum
|
|
* types defined in the header file.
|
|
*
|
|
* Returns - SHORT - 0 if handle was returned successfully
|
|
* Else, an error code which may be one of:
|
|
*
|
|
* TIMERERR_NOT_AVAILABLE
|
|
* TIMERERR_NO_MORE_HANDLES
|
|
* TIMERERR_INVALID_UNITS
|
|
*
|
|
* Obtains the handle to a timer object after opening it.
|
|
* Should precede any calls to timer manipulation. Checks
|
|
* for validity of timer units.
|
|
*/
|
|
|
|
SHORT FAR PASCAL TimerOpen (phTimerHandle, TimerUnits)
|
|
SHORT far * phTimerHandle;
|
|
_TimerUnits TimerUnits;
|
|
{
|
|
SHORT csTemp;
|
|
|
|
|
|
#ifdef WIN16
|
|
/* Perform DLL initialization if hasn't been done */
|
|
|
|
if (!fWinDllInitDone)
|
|
WinDllInit();
|
|
|
|
#endif
|
|
|
|
if ((TimerUnits < KILOSECONDS)
|
|
|| (TimerUnits > NANOSECONDS)) /* out of the enum range */
|
|
return (TIMERERR_INVALID_UNITS);
|
|
|
|
if (!bTimerInit) /* set during dll initialization */
|
|
return (TIMERERR_NOT_AVAILABLE);
|
|
|
|
/* else check if any timers are not in use and return the first
|
|
available timer handle....actually the index into the
|
|
timer object array */
|
|
for (csTemp = 0; csTemp < MAX_TIMERS; csTemp++) {
|
|
if (pTimer [csTemp].TUnits == TIMER_FREE) {
|
|
*phTimerHandle = csTemp; /* found a free timer. Return
|
|
the handle */
|
|
pTimer [csTemp].ulHi = pTimer[csTemp].ulLo = 0L;
|
|
pTimer [csTemp].TUnits =
|
|
TimerUnits; /* set the units for timer */
|
|
return (SUCCESS_OK);
|
|
}
|
|
}
|
|
/* if exec reached here, all timers are being used */
|
|
return (TIMERERR_NO_MORE_HANDLES);
|
|
}
|
|
|
|
/*
|
|
* Function - TimerInit (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT - hTimerHandle
|
|
*
|
|
* Returns - SHORT - 0 if call successful
|
|
* Else, an error code if handle invalid:
|
|
*
|
|
* TIMERERR_INVALID_HANDLE
|
|
*
|
|
* Calls the low-level timer and sets the ulHi and ulLo of the
|
|
* chosen timer to the time returned by the timer. Should be
|
|
* called after opening the timer with TimerOpen.
|
|
*/
|
|
|
|
SHORT FAR PASCAL TimerInit (SHORT hTimerHandle)
|
|
{
|
|
|
|
#if (defined (NTNAT) || defined (OS2SS) || defined (WIN32))
|
|
NTSTATUS NtStatus;
|
|
#endif
|
|
|
|
|
|
#ifdef WIN16
|
|
// Perform DLL initialization if hasn't been done
|
|
//
|
|
if (!fWinDllInitDone) {
|
|
WinDllInit();
|
|
}
|
|
#endif
|
|
|
|
if ((hTimerHandle > MAX_TIMERS - 1) ||
|
|
(pTimer [hTimerHandle].TUnits == TIMER_FREE))
|
|
/* this timer has not been opened or does not exist. Return error */
|
|
return (TIMERERR_INVALID_HANDLE);
|
|
|
|
/* otherwise get the time from the low-level timer into
|
|
the structure */
|
|
|
|
#if (defined (OS2286) || defined (OS2386))
|
|
DosTmrQueryTime (&qwTemp); /* use the perfview timer */
|
|
#elif (defined(WIN16))
|
|
if (!fUseApiTimer) /* VTD timer available */
|
|
WinVTDTime (&qwTemp);
|
|
else {
|
|
qwTemp.ulLo = PerfGetTime (); /* use the old timer */
|
|
qwTemp.ulHi = 0L; /* clear this fella */
|
|
}
|
|
#endif
|
|
|
|
#ifdef SLOOP
|
|
/* issue a DosDevIOCtl to read the time */
|
|
DosDevIOCtl (&qwTemp, &qwTemp, 0x40, 0x88, hDevHandle);
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (OS2386) || defined (WIN16) || defined (SLOOP))
|
|
pTimer [hTimerHandle].ulLo = qwTemp.ulLo; // get it into the timer
|
|
pTimer [hTimerHandle].ulHi = qwTemp.ulHi; // structure
|
|
/* this timer structure has all the information needed to compute
|
|
the elapsed time. So return success, if there was no problem */
|
|
#endif
|
|
|
|
#if (defined (OS2SS) || defined (NTNAT) || defined (WIN32))
|
|
#if !defined(WIN32)
|
|
NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
|
|
#else
|
|
QueryPerformanceCounter( &CountCurrent ) ;
|
|
#endif
|
|
pTimer [hTimerHandle].ulLo = CountCurrent.LowPart;
|
|
pTimer [hTimerHandle].ulHi = CountCurrent.HighPart;
|
|
/* this timer structure has all the information needed to compute
|
|
the elapsed time. So return success, if there was no problem */
|
|
#endif
|
|
|
|
return (SUCCESS_OK);
|
|
}
|
|
|
|
/*
|
|
* Function - TimerRead (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT - hTimerHandle
|
|
*
|
|
* Returns - ULONG - elapsed time since last call to TimerInit
|
|
* if call successful.
|
|
*
|
|
* Else, an error code if handle invalid or output
|
|
* overflow. The error code will be the same:
|
|
*
|
|
* TIMERERR_OVERFLOW (max possible ULONG)
|
|
*
|
|
* Calls the low-level timer. Uses the ulLo and ulHi from the
|
|
* timer's structure and subtracts the current time from the
|
|
* saved time. Uses ReturnElapsedTime (an external ASM proc)
|
|
* to return the elapsed time taking into account the clock
|
|
* frequency and the units for this timer. Each call to this
|
|
* returns the time from the previous TimerInit.
|
|
*
|
|
* The user should interpret the return value sensibly to check
|
|
* if the result is an error or a real value.
|
|
*/
|
|
|
|
ULONG FAR PASCAL TimerRead (SHORT hTimerHandle)
|
|
{
|
|
|
|
#if (defined (NTNAT) || defined (OS2SS) || defined (WIN32))
|
|
NTSTATUS NtStatus;
|
|
LARGE_INTEGER ElapsedTime, CountPrev, LargeOverhead;
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (SLOOP))
|
|
QWORD qwSubtractedTime;
|
|
#endif
|
|
|
|
#ifdef WIN16
|
|
|
|
// Perform DLL initialization if hasn't been done
|
|
//
|
|
if (!fWinDllInitDone) {
|
|
WinDllInit();
|
|
}
|
|
#endif
|
|
|
|
if ((hTimerHandle > MAX_TIMERS - 1)
|
|
|| (pTimer [hTimerHandle].TUnits == TIMER_FREE))
|
|
/* this timer has not been opened or does not exist.
|
|
Return TIMERERR_OVERFLOW ie. 0xffffffff, the max. possible
|
|
ULONG. The user should interpret such a result sensibly */
|
|
return (TIMERERR_OVERFLOW);
|
|
|
|
/* let us handle a few cases at a time. First let us get the
|
|
286 and 386 times computed first */
|
|
|
|
#if (defined (OS2286) || defined (OS2386))
|
|
DosTmrQueryTime (&qwCurrent); /* use the Perfview timer */
|
|
|
|
#elif (defined (WIN16))
|
|
if (!fUseApiTimer) /* VTD Timer available */
|
|
WinVTDTime (&qwCurrent);
|
|
else { /* use the old Api Timer */
|
|
qwCurrent.ulLo = PerfGetTime ();
|
|
qwCurrent.ulHi = 0L;
|
|
}
|
|
ulFreq = TIMER_FREQ;
|
|
|
|
#elif (defined (SLOOP))
|
|
/* issue a DosDevIOCtl to read the time */
|
|
DosDevIOCtl (&qwCurrent, &qwCurrent, 0x40, 0x88, hDevHandle);
|
|
ulFreq = TIMER_FREQ; /* hardcode the frequency */
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (OS2386) || defined (WIN16) || defined (SLOOP))
|
|
/* since we are going to be using separate routines for comptuing the
|
|
64 bit differences, let us store the values into QWORDS. */
|
|
qwPrev.ulLo = pTimer [hTimerHandle].ulLo;
|
|
qwPrev.ulHi = pTimer [hTimerHandle].ulHi;
|
|
|
|
/* use the special purposes asm functions to do the 64-32 computation */
|
|
|
|
#if (defined (OS2386) || defined (WIN16))
|
|
/* call the asm routine ReturnElapsedTime */
|
|
|
|
if (!bCalibrated)
|
|
ulTimerOverhead = 0; /* so that overhead will not
|
|
be used while calibrating */
|
|
#ifdef WIN16
|
|
if (fUseApiTimer) /* just subtract the prev. time from the current */
|
|
ulElapsedTime = qwCurrent. ulLo - qwPrev.ulLo;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
/* MICROSECOND_INDEX = 3, i.e. The units is MICRO_SECONDS */
|
|
|
|
ReturnElapsedTime (&qwPrev, &qwCurrent, &ulFreq,
|
|
MICROSECOND_INDEX,
|
|
&ulTimerOverhead, &qwResult);
|
|
|
|
/* the 386 model allows the difference of the 2 quad words
|
|
to be divided by the freq. and scaled approproiately */
|
|
|
|
if (!bCalibrated)
|
|
ulTimerOverhead = 50000L; /* set this back to the old high. This
|
|
is basically for OS/2 386. */
|
|
|
|
/* if the high 32 bits of the returned QWORD is not zero,
|
|
there has been an overflow - CHECK */
|
|
|
|
if (qwResult.ulHi)
|
|
return (TIMERERR_OVERFLOW);
|
|
|
|
#ifdef WIN16
|
|
if (!fUseApiTimer) { /* return time here only if VTD.386 installed */
|
|
#endif
|
|
|
|
/* everything is just fine, return the elapsed time as a ULONG after
|
|
scaling. Ovrehead calibration has already been done by the
|
|
ReturnElapsedTime routine. */
|
|
|
|
if (pTimer [hTimerHandle].TUnits < 3)
|
|
qwResult.ulLo /= aScaleValues [pTimer [hTimerHandle].TUnits];
|
|
else
|
|
qwResult.ulLo *= aScaleValues [pTimer [hTimerHandle].TUnits];
|
|
|
|
ulElapsedTime = qwResult.ulLo; // this is used by TimerReport. So stuff
|
|
// it in.
|
|
if ((ULONG) qwResult.ulLo > 0L)
|
|
return (qwResult.ulLo);
|
|
else
|
|
return (0L);
|
|
#ifdef WIN16
|
|
} /* close paren. for if (!fUseApiTimer) */
|
|
#endif
|
|
/* we are done as far as the 32 bit OS/2 platform or Win 3.x (VTD only)
|
|
are concerned */
|
|
|
|
#elif (defined (OS2286) || defined (SLOOP))
|
|
|
|
/* THIS STUFF IS MESSY. Have to use 16 bit registers and do 64
|
|
bit arithmetic */
|
|
|
|
Sub64_64 (&qwSubtractedTime, qwCurrent, qwPrev);
|
|
|
|
/* on return, qwResult will contain the difference of the 2
|
|
64 bit numbers */
|
|
|
|
/* multiply this number by 1000000 to convert to us equivalent...
|
|
this is how DosTmrQueryTime works */
|
|
|
|
mult64_32 (qwSubtractedTime, ONE_MILLION, &qwResult);
|
|
|
|
/* divide this result by the frequency */
|
|
/* in this case, call the Div64by32 routine to do the division */
|
|
|
|
Div64By32 (qwResult, ulFreq, &qwTemp);
|
|
|
|
/* the result of this division should fit into 32 bits */
|
|
|
|
ulElapsedTime = qwTemp.ulLo;
|
|
|
|
/* OK, we now have the result of dividing the clock tics by
|
|
frequency, in ulElapsedTime */
|
|
|
|
#endif /* end of second if (defined(OS2386) || defined (WIN16)) */
|
|
#endif /* end of ifdef OS2386, and elif OS2286 or WIN */
|
|
|
|
#if (defined (NTNAT) || defined (WIN32) || defined (OS2SS))
|
|
|
|
#if !defined(WIN32)
|
|
NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
|
|
#else
|
|
QueryPerformanceCounter( &CountCurrent ) ;
|
|
#endif
|
|
CountPrev.LowPart = pTimer [hTimerHandle].ulLo;
|
|
CountPrev.HighPart = (LONG) pTimer [hTimerHandle].ulHi;
|
|
ElapsedTime.LowPart = CountCurrent.LowPart;
|
|
ElapsedTime.HighPart = (LONG) CountCurrent.HighPart;
|
|
|
|
/* everything is just fine, convert to double, subtract the times,
|
|
divide by the frequency, convert to MICROSECONDS and return
|
|
the elapsed time as a ULONG */
|
|
/* convert to us., divide the count by the clock frequency that
|
|
has already been obtained */
|
|
|
|
ElapsedTime.QuadPart -= CountPrev.QuadPart ;
|
|
|
|
ElapsedTime.QuadPart *= MICROSEC_FACTOR ;
|
|
|
|
ElapsedTime.QuadPart /= PerfFreq.LowPart ;
|
|
|
|
// if the timer is not calibrated, set ulElapsedTime to be the
|
|
// low part of ElapsedTime. This is because, we do not have to
|
|
// do to any arithmetic to this before returning the value.
|
|
|
|
if (!bCalibrated)
|
|
ulElapsedTime = ElapsedTime.LowPart;
|
|
|
|
#endif
|
|
|
|
#ifndef OS2386
|
|
/* this code is common for all platforms but OS2386. For Win3.x
|
|
if VTD.386 has been installed, the code below should not matter,
|
|
since we should have returned the time by now.
|
|
|
|
The elapsed time will be scaled, overhead subtracted
|
|
and the time returned */
|
|
|
|
/* we have ulElapsedTime. Scale it and do the needful */
|
|
/* divide or multiply by the scale factor */
|
|
|
|
#ifdef WIN16
|
|
if (fUseApiTimer) {
|
|
#endif
|
|
if (bCalibrated) {
|
|
#ifdef WIN32 // Applications like the PROBE call TimerRead repeatedly
|
|
// without calling TimerInit, for more than 70 minutes. This
|
|
// screws up things. So treat everything as 64 bit numbers
|
|
// until the very end.
|
|
|
|
if ((ElapsedTime.LowPart < ulTimerOverhead) &&
|
|
(!ElapsedTime.HighPart)) { // low part is lower than overhead
|
|
// and high part is zero..then make
|
|
// elapsed time 0. We don't want
|
|
// negative numbers.
|
|
ElapsedTime.HighPart = 0L;
|
|
ElapsedTime.LowPart = 0L;
|
|
}
|
|
|
|
else { // subtract the overhead in tics before converting
|
|
// to time units
|
|
LargeOverhead.HighPart = 0L;
|
|
LargeOverhead.LowPart = ulTimerOverhead;
|
|
ElapsedTime.QuadPart -= LargeOverhead.QuadPart ;
|
|
|
|
}
|
|
|
|
|
|
if (pTimer [hTimerHandle].TUnits <= MICROSECONDS) {
|
|
|
|
ElapsedTime.QuadPart /= aScaleValues [pTimer [hTimerHandle].TUnits] ;
|
|
}
|
|
|
|
else {
|
|
|
|
ElapsedTime.QuadPart *= aScaleValues [pTimer [hTimerHandle].TUnits] ;
|
|
}
|
|
|
|
// scaling is done. Now get the time back into 32 bits. This
|
|
// should fit.
|
|
|
|
ulElapsedTime = ElapsedTime.LowPart;
|
|
|
|
|
|
#else // for non WIN32 platforms and non OS2386 platforms
|
|
|
|
// subtract the overhead before converting tics to time units.
|
|
|
|
if (ulElapsedTime > ulTimerOverhead)
|
|
ulElapsedTime -= ulTimerOverhead;
|
|
else
|
|
ulElapsedTime = 0L;
|
|
|
|
if (pTimer [hTimerHandle].TUnits <= MICROSECONDS)
|
|
ulElapsedTime /= aScaleValues [pTimer [hTimerHandle].TUnits];
|
|
|
|
else
|
|
ulElapsedTime *= aScaleValues [pTimer [hTimerHandle].TUnits];
|
|
#endif
|
|
|
|
}
|
|
|
|
if ((LONG) ulElapsedTime < 0L) /* if this guy is -ve, return a 0 */
|
|
return (0L);
|
|
|
|
return (ulElapsedTime);
|
|
|
|
#ifdef WIN16
|
|
}
|
|
#endif
|
|
#endif /* for all platforms except OS2386 and WIN30 */
|
|
}
|
|
|
|
/*
|
|
* Function - TimerClose (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT - hTimerHandle
|
|
*
|
|
* Returns - SHORT - 0 if call successful
|
|
* Else, an error code if handle invalid:
|
|
*
|
|
* TIMERERR_INVALID_HANDLE
|
|
*
|
|
* Releases the timer for use by future TimerOpen calls.
|
|
* Resets the elements of the timer structure, setting the
|
|
* Timer's Units element to TIMER_FREE.
|
|
*/
|
|
|
|
SHORT FAR PASCAL TimerClose (SHORT hTimerHandle)
|
|
{
|
|
|
|
#ifdef WIN16
|
|
|
|
// Perform DLL initialization if hasn't been done
|
|
//
|
|
if (!fWinDllInitDone) {
|
|
WinDllInit();
|
|
}
|
|
|
|
#endif
|
|
|
|
if ((hTimerHandle > MAX_TIMERS - 1) ||
|
|
(pTimer [hTimerHandle].TUnits == TIMER_FREE))
|
|
/* error condition, wrong handle */
|
|
return (TIMERERR_INVALID_HANDLE);
|
|
|
|
/* otherwise, set the TimerUnits of this timer to TIMER_FREE,
|
|
reset the other elements to zero and return */
|
|
|
|
pTimer [hTimerHandle].TUnits = TIMER_FREE;
|
|
pTimer [hTimerHandle].ulLo = 0L;
|
|
pTimer [hTimerHandle].ulHi = 0L;
|
|
return (SUCCESS_OK);
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
Added this routine TimerReport to report individual
|
|
times. Bob Day requested that such a routine be
|
|
created. It just maintains the time from the last
|
|
TimerInit and TimerRead and also the last time returned.
|
|
This routine copies this to a user specified buffer.
|
|
|
|
Accepts - PSZ - a pointer to a buffer to print the data out
|
|
SHORT - timer handle
|
|
|
|
Returns - TRUE if Timer exists and is open
|
|
- FALSE if Timer not opened
|
|
|
|
*******************************************************************/
|
|
|
|
BOOL FAR PASCAL TimerReport (PSZ pszReportString, SHORT hTimerHandle)
|
|
{
|
|
|
|
#ifdef WIN16
|
|
// Perform DLL initialization if hasn't been done
|
|
//
|
|
if (!fWinDllInitDone) {
|
|
WinDllInit();
|
|
}
|
|
#endif
|
|
|
|
if (pTimer [hTimerHandle].TUnits == TIMER_FREE)
|
|
return (FALSE);
|
|
|
|
/* stored value is in pTimer[hTimerHandle].ulLo and .ulHi */
|
|
|
|
#if (defined (OS2286) || defined (SLOOP))
|
|
sprintf (pszReportString,
|
|
"Init Count (tics) %lx:%lx Current Count (tics) %lx:%lx Returned Time %lu ",
|
|
pTimer [hTimerHandle].ulHi,
|
|
pTimer [hTimerHandle].ulLo, qwCurrent.ulLo, qwCurrent.ulHi,
|
|
ulElapsedTime);
|
|
|
|
#elif (defined (OS2386))
|
|
sprintf (pszReportString,
|
|
"Init Count (tics) %lu:%lu Current Count (tics) %lu:%lu Returned Time %lu ",
|
|
pTimer [hTimerHandle].ulHi,
|
|
pTimer [hTimerHandle].ulLo, qwResult.ulLo, qwResult.ulHi,
|
|
qwResult.ulLo);
|
|
|
|
#elif (defined (WIN32))
|
|
wsprintf (pszReportString,
|
|
"Init Count (tics) %lu:%lu Current Count (tics) %lu:%lu Returned Time %lu ",
|
|
pTimer [hTimerHandle].ulHi,
|
|
pTimer [hTimerHandle].ulLo, CountCurrent.HighPart,
|
|
CountCurrent.LowPart,
|
|
ulElapsedTime);
|
|
|
|
#elif (defined (WIN16))
|
|
wsprintf ((LPSTR) pszReportString,
|
|
(LPSTR) "Init Count (tics) %lu Current Count (tics) %lu Returned Time %lu ",
|
|
pTimer [hTimerHandle].ulLo,
|
|
qwCurrent.ulLo,
|
|
ulElapsedTime);
|
|
#endif
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
Added this routine TimerQueryPerformanceCounter to report
|
|
current tic count at behest of NT GDI folks.
|
|
|
|
|
|
Accepts - PQWORD - a pointer to a 64 bit struct. that will
|
|
contain tic count on return.
|
|
|
|
PQWORD [OPTIONAL) - a pointer to a 64 bit struct. that will
|
|
contain frequency on return.
|
|
|
|
Returns - None.
|
|
|
|
*******************************************************************/
|
|
|
|
VOID FAR PASCAL TimerQueryPerformanceCounter (PQWORD pqTic, PQWORD pqFreq OPTIONAL)
|
|
{
|
|
|
|
#ifdef WIN32
|
|
|
|
// call the NT API to do the needful and return.
|
|
#if defined (OLD)
|
|
NtQueryPerformanceCounter ((PQWORD) pqTic, (PQWORD) pqFreq);
|
|
#else
|
|
QueryPerformanceCounter( pqTic ) ;
|
|
QueryPerformanceFrequency( pqFreq);
|
|
#endif
|
|
return;
|
|
|
|
#elif (defined (WIN16))
|
|
|
|
// If freq. is desired, give it, else just give the tic count back.
|
|
|
|
if (pqFreq != NULL) {
|
|
pqFreq->ulLo = TIMER_FREQ;
|
|
pqFreq->ulHi = 0L;
|
|
}
|
|
WinVTDTime (pqTic);
|
|
return;
|
|
|
|
#elif (defined(OS2286) || defined(OS2386))
|
|
|
|
// If freq. is desired, give it, else just give the tic count back.
|
|
|
|
if (pqFreq != NULL) {
|
|
DosTmrQueryFreq (&(pqFreq->ulLo));
|
|
pqFreq->ulHi = 0L;
|
|
}
|
|
|
|
DosTmrQueryTime (pqTic);
|
|
return;
|
|
|
|
#elif (defined (SLOOP))
|
|
|
|
// If freq. is desired, give it, else just give the tic count back.
|
|
|
|
if (pqFreq != NULL) {
|
|
pqFreq->ulLo = TIMER_FREQ;
|
|
pqFreq->ulHi = 0L;
|
|
}
|
|
|
|
DosDevIOCtl (pqTic, pqTic, 0x40, 0x88, hDevHandle);
|
|
return;
|
|
|
|
#endif
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
Added this routine TimerConvertTicsToUSec to return
|
|
time in usecs. for a given elapsed tic count and freq.
|
|
|
|
|
|
Accepts - ULONG - Elapsed Tic Count.
|
|
|
|
ULONG - Frequency.
|
|
|
|
Returns - Elapsed Time in usecs. as a ULONG.
|
|
- Zero if input freq. is zero.
|
|
|
|
*******************************************************************/
|
|
|
|
ULONG FAR PASCAL TimerConvertTicsToUSec (
|
|
ULONG ulElapsedTics,
|
|
ULONG ulInputFreq
|
|
)
|
|
{
|
|
|
|
#ifdef WIN32
|
|
|
|
LARGE_INTEGER ElapsedTime;
|
|
LARGE_INTEGER Temp ;
|
|
ULONG ulRemainder = 0L;
|
|
|
|
// if the bozo gives me a zero freq, return him a zero.
|
|
// Let him tear his hair.
|
|
|
|
if (!ulInputFreq)
|
|
return 0L;
|
|
|
|
// multiply tics by a million and divide by the frequency.
|
|
|
|
ElapsedTime.QuadPart = ulElapsedTics * MICROSEC_FACTOR ;
|
|
|
|
Temp = ElapsedTime ;
|
|
|
|
ElapsedTime.QuadPart /= ulInputFreq ;
|
|
|
|
ulRemainder = (ULONG)(Temp.QuadPart - (ElapsedTime.QuadPart * ulInputFreq)) ;
|
|
|
|
ElapsedTime.LowPart += (ulRemainder > (ulInputFreq / 2L));
|
|
|
|
return (ElapsedTime.LowPart) ; /* get the result into a ULONG */
|
|
|
|
|
|
#elif (defined (WIN16) || defined (OS2386))
|
|
|
|
QWORD qwOld, qwNew, qwElapsed;
|
|
ULONG ulDummy = 0L;
|
|
|
|
qwOld.ulLo = qwOld.ulHi = 0L; // for use with ReturnElaosedTime.
|
|
qwNew.ulHi = 0L; // - do -
|
|
qwNew.ulLo = ulElapsedTics; // - do -
|
|
|
|
// the following routine will do everything.
|
|
|
|
ReturnElapsedTime (&qwOld, &qwNew, &ulInputFreq,
|
|
MICROSECOND_INDEX,
|
|
&ulDummy, &qwElapsed);
|
|
|
|
return (qwElapsed.ulLo);
|
|
|
|
#elif (defined(OS2286) || defined(SLOOP))
|
|
|
|
QWORD qwDiff, qwElapsed;
|
|
|
|
qwDiff.ulLo = ulElapsedTics;
|
|
qwDiff.ulHi = 0L;
|
|
|
|
// multiply tic diff. by a million.
|
|
mult64_32 (qwDiff, ONE_MILLION, &qwResult);
|
|
|
|
// divide by frequency.
|
|
Div64By32 (qwResult, ulInputFreq, &qwElapsed);
|
|
|
|
/* the result of this division should fit into 32 bits */
|
|
|
|
return (qwElapsed.ulLo + (qwElapsed.ulLo > (ulInputFreq /2L));
|
|
|
|
#endif
|
|
}
|
|
|
|
/**************** ROUTINES NOT EXPORTED, FOLLOW ************************/
|
|
|
|
/*
|
|
* Function - CalibrateTimerForOverhead (NOT EXPORTED)
|
|
* Arguments - None
|
|
* Returns - ULONG
|
|
*
|
|
* Calls TimerElapsedTime a few times to compute the expected
|
|
* mean. Calls TimerElapsedTime more number of times and
|
|
* averages the mean out of those calls that did not exceed
|
|
* the expected mean by 15%.
|
|
*/
|
|
|
|
ULONG CalibrateTimerForOverhead (VOID)
|
|
{
|
|
ULONG ulOverhead [ITER_FOR_OVERHEAD];
|
|
ULONG ulTempTotal = 0L;
|
|
ULONG ulExpectedValue = 0L;
|
|
SHORT csIter;
|
|
SHORT csNoOfSamples = ITER_FOR_OVERHEAD;
|
|
SHORT hTimerHandle;
|
|
|
|
if (TimerOpen (&hTimerHandle, MICROSECONDS)) /* open failed. Return 0 */
|
|
return (0L);
|
|
|
|
for (csIter = 0; csIter < 5; csIter++) {
|
|
TimerInit (hTimerHandle);
|
|
ulOverhead [csIter] = TimerRead (hTimerHandle);
|
|
/* if negative, make zero */
|
|
if (((LONG) ulOverhead [csIter]) < 0)
|
|
ulOverhead [csIter] = 0L;
|
|
}
|
|
/* The get elapsed time function has been called 6 times.
|
|
The idea is to calculate the expected mean, then call
|
|
TimerElapsedTime a bunch of times and throw away all times
|
|
that are 15% larger than this mean. This would give a
|
|
really good overhead time */
|
|
|
|
for (csIter = 0; csIter < 5; csIter++ )
|
|
ulTempTotal += ulOverhead [csIter];
|
|
|
|
ulExpectedValue = ulTempTotal / 5;
|
|
|
|
for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++) {
|
|
TimerInit (hTimerHandle);
|
|
ulOverhead [csIter] = TimerRead (hTimerHandle);
|
|
/* if negative, make zero */
|
|
if (((LONG) ulOverhead [csIter]) < 0)
|
|
ulOverhead [csIter] = 0L;
|
|
}
|
|
|
|
ulTempTotal = 0L; /* reset this value */
|
|
for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++ ) {
|
|
if (ulOverhead [csIter] <= (ULONG) (115L * ulExpectedValue/100L))
|
|
/* include all samples that is < 115% of ulExpectedValue */
|
|
ulTempTotal += ulOverhead [csIter];
|
|
else
|
|
/* ignore this sample and dec. sample count */
|
|
csNoOfSamples--;
|
|
}
|
|
TimerClose (hTimerHandle);
|
|
|
|
if (csNoOfSamples == 0) /* no valid times. Return a 0 for overhead */
|
|
return (0L);
|
|
|
|
return (ulTempTotal/csNoOfSamples);
|
|
}
|
|
|
|
#if (defined (OS2286) || defined (OS2386) || defined (OS2SS) || defined SLOOP)
|
|
/* timing init for OS2 only */
|
|
/*
|
|
* Function - TIMING_INIT (NOT EXPORTED)
|
|
* Arguments - None
|
|
* Return - None
|
|
*
|
|
* TIMING_INIT is called from the entry point in TimerInit.ASM
|
|
* when a client process dynamically links to the library.
|
|
*
|
|
*/
|
|
|
|
int TIMING_INIT (VOID)
|
|
{
|
|
SHORT csTempCtr; /* a local counter */
|
|
USHORT usAction;
|
|
|
|
for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
|
|
pTimer [csTempCtr].ulLo = 0L;
|
|
pTimer [csTempCtr].ulHi = 0L;
|
|
pTimer [csTempCtr].TUnits = TIMER_FREE;
|
|
}
|
|
bTimerInit = TRUE;
|
|
#if (defined (OS2286) || defined (OS2386) || defined (OS2SS))
|
|
GetTimerFreq (); /* get the timer freq. into ulFreq */
|
|
#endif
|
|
|
|
#ifdef SLOOP
|
|
DosOpen((char far *) "TIME_16$",
|
|
&hDevHandle,
|
|
&usAction,
|
|
0L,
|
|
FILE_SYSTEM,
|
|
FILE_OPEN,
|
|
OPEN_ACCESS_READONLY+OPEN_SHARE_DENYNONE,
|
|
0L
|
|
);
|
|
#endif
|
|
ulTimerOverhead = CalibrateTimerForOverhead ();
|
|
/* the timer overhead will be placed in a global variable */
|
|
bCalibrated = TRUE;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#ifndef WIN16 /* get freq for all other platforms */
|
|
|
|
/*
|
|
* Function - GetTimerFreq (NOT EXPORTED)
|
|
*
|
|
* Arguments - None
|
|
*
|
|
*
|
|
* Return - 0 if successful or an error code if timer not aailable
|
|
*
|
|
* Calls the function to return freq
|
|
*
|
|
*/
|
|
SHORT GetTimerFreq (VOID)
|
|
{
|
|
#if (defined (OS2286) || defined (OS2386))
|
|
DosTmrQueryFreq (&ulFreq);
|
|
#elif (defined (NTNAT) || defined (WIN32) || defined (OS2SS))
|
|
LARGE_INTEGER PerfCount, Freq;
|
|
NTSTATUS NtStatus;
|
|
|
|
#if defined (OLD)
|
|
NtStatus = NtQueryPerformanceCounter (&PerfCount, &Freq);
|
|
#else
|
|
QueryPerformanceCounter( &PerfCount ) ;
|
|
QueryPerformanceFrequency( &Freq);
|
|
#endif
|
|
|
|
if ((Freq.LowPart == 0L) && (Freq.HighPart == 0L))
|
|
/* frequency of zero implies timer not available */
|
|
return (TIMERERR_NOT_AVAILABLE);
|
|
|
|
PerfFreq.LowPart = Freq.LowPart;
|
|
PerfFreq.HighPart = (LONG) Freq.HighPart;
|
|
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WIN16
|
|
|
|
/*********************************************************************
|
|
|
|
PURPOSE: Contains library routines for the performance timer
|
|
|
|
FUNCTION: LibMain (HANDLE, WORD, WORD, LPSTR)
|
|
|
|
PURPOSE: Is called by LibEntry. LibEntry is called by Windows when
|
|
the DLL is loaded. The LibEntry routine is provided in
|
|
the LIBENTRY.OBJ in the SDK Link Libraries disk. (The
|
|
source LIBENTRY.ASM is also provided.)
|
|
|
|
LibEntry initializes the DLL's heap, if a HEAPSIZE value is
|
|
specified in the DLL's DEF file. Then LibEntry calls
|
|
LibMain. The LibMain function below satisfies that call.
|
|
|
|
The LibMain function should perform additional initialization
|
|
tasks required by the DLL. In this example, no initialization
|
|
tasks are required. LibMain should return a value of 1 if
|
|
the initialization is successful.
|
|
*/
|
|
/*********************************************************************/
|
|
|
|
int FAR PASCAL LibMain(hModule, wDataSeg, cbHeapSize, lpszCmdLine)
|
|
HANDLE hModule;
|
|
WORD wDataSeg;
|
|
WORD cbHeapSize;
|
|
LPSTR lpszCmdLine;
|
|
|
|
{
|
|
/*
|
|
No initialization is done in this routine. Any required
|
|
initialization is done by ApfInitDll() which will be called
|
|
only once during profiling of the first API. A global flag,
|
|
fWinDllInitDone, is used to control calling the init routine.
|
|
This is done to prevent any problems caused by some Microsoft
|
|
applications such as Excel and WinWord due to their self loading
|
|
capability. These apps use kernel calls before DLLs LibMains
|
|
are called.
|
|
|
|
This feature was added by RezaB when faced with a problem
|
|
during API profiling */
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
PURPOSE: Contains DLLs initialization for WINDOWs 3.x.
|
|
|
|
FUNCTION: WinDllInit ()
|
|
|
|
PURPOSE: Is called only once.
|
|
Since no initialization is done by LibMain() routine,
|
|
Any required initialization is done this routine which
|
|
will be called only once during the first reference to any
|
|
of this DLLs exported routines. A global flag, fWinDllInitDone,
|
|
is used to control calling this init routine.
|
|
This is done to prevent any problems caused by some Microsoft
|
|
applications such as Excel and WinWord due to their self loading
|
|
capability. These apps use kernel calls before DLLs LibMains
|
|
are called.
|
|
*/
|
|
/*********************************************************************/
|
|
|
|
void WinDllInit ()
|
|
{
|
|
SHORT csTempCtr; // a local counter
|
|
|
|
|
|
fWinDllInitDone = TRUE; // has to be done here since
|
|
// CalibrateTimerForOverhead() makes calls
|
|
// to TimerInit() and TimerRead() which
|
|
// both check this flag.
|
|
|
|
// allocate storage for all MAX_TIMER objects
|
|
//
|
|
for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
|
|
pTimer [csTempCtr].ulLo = 0L;
|
|
pTimer [csTempCtr].ulHi = 0L;
|
|
pTimer [csTempCtr].TUnits = TIMER_FREE;
|
|
}
|
|
|
|
/* if not enhanced mode or if the VTDAddr routine returns a non-zero
|
|
do not use the VTD.386 */
|
|
|
|
if (!(GetWinFlags() & WF_ENHANCED) || (WinVTDAddr ())) {
|
|
fUseApiTimer = TRUE;
|
|
PerfStartTime (); /* start the timer */
|
|
}
|
|
bTimerInit = TRUE;
|
|
ulTimerOverhead = CalibrateTimerForOverhead ();
|
|
bCalibrated = TRUE;
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/*
|
|
FUNCTION: WEP (int)
|
|
|
|
PURPOSE: Performs cleanup tasks when the DLL is unloaded. WEP() is
|
|
called automatically by Windows when the DLL is unloaded
|
|
(no remaining tasks still have the DLL loaded). It is
|
|
strongly recommended that a DLL have a WEP() function,
|
|
even if it does nothing but return, as in this example.
|
|
*/
|
|
/*********************************************************************/
|
|
|
|
VOID FAR PASCAL WEP(int bSystemExit)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/**********************************************************************/
|
|
#ifdef WIN16
|
|
/**********************************************************************/
|
|
|
|
/* Perftime
|
|
*
|
|
* This is a set of timing routines to be used by any machine. No special
|
|
* hardware is necessary. Call PerfStartTime( ) when you want to begin
|
|
* timing, and PerfGetTime( ) to get the amount of time passed since
|
|
* PerfStartTime was called. PerfGetTime( ) may be called any number of
|
|
* times after PerfStartTime( ), and it will always return the amount of
|
|
* time since PerfStartTime. Times greater than an hour, however, return
|
|
* as an error. Overhead is automatically subtracted.
|
|
*
|
|
* Notes: This timer routine has overhead of 60 microseconds on an
|
|
* 6 MHz AT and 23 microseconds on a 16 MHz Compaq386.
|
|
*
|
|
* Restrictions: Don't use with the dos box. This timer uses the
|
|
* system timer used by Dos. If a Dos session is entered
|
|
* during the timing session, timing is only meaningful
|
|
* above 54 milliseconds. Anything below 54 milliseconds
|
|
* is uncertain.
|
|
*
|
|
* Exports: PerfStartTime( )
|
|
* PerfGetTIme( )
|
|
*
|
|
* Imports: None
|
|
*
|
|
*
|
|
* Algorithm: These routines synchronize two asynchronous clocks: The
|
|
* OS/2 system clock, and the 8253 timer chip, counter 0.
|
|
* It uses the 8253 to time the microsecond level, and the
|
|
* system timer to time milliseconds and above. The two
|
|
* timers are synchronized by first converting the time
|
|
* returned by the system clock to an even multiple of the
|
|
* 8253's max count(54 mSecs), an then comparing the 8253
|
|
* count to the remainder of the system count to compensate
|
|
* for differneces between the two counters. If the system
|
|
* timer is within TIMER_LAG behind the 8253 it is consedered
|
|
* to be lagging, otherwise, it is leading. There is reason
|
|
* to expect consederable lag in the system clock, because
|
|
* it is only updated every 32 mSecs.
|
|
*
|
|
* History: 13-Jan-1989 waynej created
|
|
* 30-Nov-1989 vaidy modified - see comment in body of code
|
|
* 27-Dec-1989 russbl fix error of adding two many WRAP8253's
|
|
* check sync of clocks at start
|
|
* generally cleanup code format
|
|
*
|
|
* 26-Feb-1990 c-bobch modified OS/2 version for Windows 3.0
|
|
*/
|
|
|
|
/**********************************************************************/
|
|
/*
|
|
* PerfStartTime( )
|
|
*
|
|
* This starts the performance timer.
|
|
*
|
|
*
|
|
* Side effects: This gets a pointer to the Global Information Segment,
|
|
* gets the current value of milliseconds from the system
|
|
* clock, and resets the 8253 on the system board.
|
|
*
|
|
* Arguments: none
|
|
*
|
|
* Returns: OK (0) if timers started successfully.
|
|
* SYSTEM_TIMER_ERROR (FFFF) if the system timer is updated
|
|
* less frequently than every 44 milliseconds. This
|
|
* shouldn't be a problem, as IBM machines running
|
|
* OS/2 1.0, 1.1, and 1.2 are updated evry 31 msec.
|
|
*
|
|
* Algorithm: This routine finds out when the system clock is updated
|
|
* by continually poling the Global Information Segment,
|
|
* looking for a change in milliseconds (the system clock
|
|
* is updated at 32hz or about every 31 milliseconds).
|
|
*
|
|
* History: 13-Jan-1989 waynej created
|
|
*
|
|
*/
|
|
|
|
USHORT FAR PASCAL PerfStartTime( ){
|
|
ULONG ulLastMilliSec;
|
|
ULONG ulInitOverhead = 0;
|
|
|
|
if (bInitialized == FALSE) {
|
|
bInitialized = TRUE;
|
|
|
|
/* Synchronize the two clocks: first to get overhead */
|
|
/* We'll do it more carefully later on */
|
|
|
|
ulOverhead = 0;
|
|
ulPrevSysTime = 0; /* reset previous system time */
|
|
ulPrevRetTime = 0; /* reset previous returned time */
|
|
/* get current system time, milliseconds*/
|
|
ulMilliSecStart = GetCurrentTime ();
|
|
ulLastMilliSec = ulMilliSecStart;
|
|
|
|
/* wait for system time to change */
|
|
|
|
/* ChrishSh of Win 3.0 had made some modifications to the
|
|
original code so that the do loop had been commented.
|
|
The micro and millisecond timer synch was causing
|
|
some problems and it appears that the milliseconds
|
|
times never changed. As a result, we never used to
|
|
come out of the loop. This is interesting, since
|
|
this seems to work perfectly well under SLOOP. */
|
|
IntOff();
|
|
Start8253(); /* restart microsecond timer */
|
|
IntOn();
|
|
/* Compute PerfGetTime overhead */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 5 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 10 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 15 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 20 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 25 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 30 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 35 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 40 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
PerfGetTime(); /* 45 */
|
|
PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
|
|
ulInitOverhead = PerfGetTime( ); /* 50 */
|
|
ulOverhead = ulInitOverhead / INITCOUNT;
|
|
|
|
/* Now the overhead has been computed: we can really synchronize */
|
|
|
|
ulPrevSysTime = 0; /* reset previous system time */
|
|
ulPrevRetTime = 0; /* reset previous returned time */
|
|
} /* end of if bInitialized condition */
|
|
|
|
return(SUCCESS_OK);
|
|
|
|
}
|
|
|
|
/*
|
|
* PerfGetTime( )
|
|
*
|
|
* This gets the current time since the PerfStartTime.
|
|
*
|
|
* Arguments: none
|
|
*
|
|
* Returns: time in microseconds
|
|
* TIMER_ERROR (-1) in case of time longer than one hour
|
|
*
|
|
*
|
|
* History: 13-Jan-1989 waynej created
|
|
*
|
|
*/
|
|
|
|
ULONG FAR PASCAL PerfGetTime( ){
|
|
|
|
USHORT u8253Time;
|
|
ULONG ulSystemTime;
|
|
ULONG ulMicroSecs;
|
|
|
|
IntOff();
|
|
ulSystemTime = GetCurrentTime ();
|
|
u8253Time = Stop8253( );
|
|
IntOn();
|
|
if (bInitialized == FALSE)
|
|
return TIMER_ERROR;
|
|
|
|
/* Convert 8253 ticks to microseconds */
|
|
u8253Time = (USHORT)(((ULONG)u8253Time * WRAP_8253 ) / 0xFFFF);
|
|
|
|
/* subtract off starting time */
|
|
ulSystemTime -= ulMilliSecStart;
|
|
|
|
/* now make sure we have not gone backwards */
|
|
if (ulSystemTime < ulPrevSysTime)
|
|
ulSystemTime = ulPrevSysTime; /* use latest time */
|
|
else
|
|
ulPrevSysTime = ulSystemTime; /* use latest time */
|
|
|
|
/*
|
|
The following "if" statement eliminates
|
|
the one hour limit on perftime. Originally, the body of the if
|
|
statement just contained a return statement, returning a TIMER_ERROR.
|
|
Now, if the ulSystemTime happens to exceed one half hour, we restart
|
|
perfstarttime. However, after starting the clock again, perfgettime
|
|
still returns the correct time for that particular call to perfgettime.
|
|
*/
|
|
if (ulSystemTime > MAX_MILLISECS) {
|
|
ReSynchronize();
|
|
return( PerfGetTime() );
|
|
}
|
|
|
|
/* Convert to microseconds */
|
|
ulSystemTime *= 1000;
|
|
|
|
ulMicroSecs = (ulSystemTime / WRAP_8253) * WRAP_8253 + u8253Time;
|
|
|
|
if(ulMicroSecs < ulSystemTime - MICROSYNCERR){
|
|
ulMicroSecs += WRAP_8253;
|
|
if (ulMicroSecs > ulSystemTime + MICROSYNCERR /* +
|
|
(pgisInfo->cusecTimerInterval * 100) */ ){
|
|
/* didn't really wrap, use actual clock time */
|
|
ulMicroSecs -= WRAP_8253;
|
|
}
|
|
}
|
|
|
|
/* Compensate for overhead */
|
|
if (ulMicroSecs >= ulOverhead)
|
|
ulMicroSecs - ulOverhead;
|
|
|
|
/* Now let's try hard to be sure we are not falling back in time */
|
|
if (ulMicroSecs < ulPrevRetTime)
|
|
/* we must have missed a wrap, that's the only way this can happen */
|
|
ulMicroSecs += WRAP_8253;
|
|
|
|
ulPrevRetTime = ulMicroSecs;
|
|
|
|
return(ulMicroSecs);
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
VOID FAR PASCAL ReSynchronize( ) {
|
|
ULONG ulSystemTime;
|
|
/*
|
|
Assume that exactly an hour after timing was started, 2 routines
|
|
A and B make call to perfgettime. If we do not take care of the
|
|
multiple calls, A and B will find that it is an hour since
|
|
intialization and both of them would try to reinitialize. This is
|
|
taken care of by the DosEnterCritSec. So, only one of A or B will
|
|
be allowed to reset the clock. The other one would have to wait.
|
|
Say, A reinitializes while B waits. B then enters the critsec and
|
|
obtains the millisec count on the system clock. But it finds that
|
|
the ulSystemtime is now less than the MAX_MILLISECS and hence does
|
|
not execute the inner if-loop. As a result, both A and B now return
|
|
valid times with the call to perfgettime.
|
|
*/
|
|
ulSystemTime = GetCurrentTime ();
|
|
ulSystemTime -= ulMilliSecStart;
|
|
/* now make sure we have not gone backwards */
|
|
if (ulSystemTime < ulPrevSysTime)
|
|
ulSystemTime = ulPrevSysTime; /* use latest time */
|
|
else
|
|
ulPrevSysTime = ulSystemTime; /* use latest time */
|
|
if( ulSystemTime > MAX_MILLISECS ) {
|
|
bInitialized = FALSE;
|
|
PerfStartTime();
|
|
ulNumResyncs++;
|
|
}
|
|
}
|
|
|
|
/*********END OF APITIMER CODE FOR Windows*********************/
|
|
|
|
#endif
|
|
|
|
#if (defined (OS2286) || defined (SLOOP))
|
|
/*********************************************************************
|
|
* Divide QWORD by ULONG
|
|
* Input:
|
|
* qwDividend -- 64bit dividend in QWORD struct
|
|
* ulDivisor -- 32bit divisor
|
|
* pqwQuot -- ptr to QWORD storage for quotient
|
|
* Output:
|
|
* puts values in Quotient and Remainder
|
|
* returns 0 if completed successfully, 1 if overflow,
|
|
* 2 if unable to complete division (see WARNING)
|
|
*
|
|
* Algorithm:
|
|
* Divide high ulong by divisor, to get high quotient. Then
|
|
* shift remainder down (saving out shifted bits) until highest bit
|
|
* is in low dividend. Do "shifted" divide. Shift this quotient
|
|
* and remainder (with out shifted bits coming back in) back up.
|
|
* Do final low divide, adding quotient to shifted quotient and
|
|
* returning this real remainder.
|
|
*
|
|
* !!! WARNING !!!
|
|
* The divide routine is not strictly a full QWORD / ULONG routine
|
|
* rather it is a QWORD / positive LONG routine. That is, if the
|
|
* highest bit in the divisor is set, the routine can not necessarily
|
|
* complete the division using dividend shift only (that's its method).
|
|
* This condition is checked and causes a return = 2.
|
|
*/
|
|
|
|
|
|
int Div64By32 (QWORD qwDividend, ULONG ulDivisor, PQWORD pqwQuot)
|
|
{
|
|
ULONG ulBitsOut = 0L; /* store bits shifted out of dividend */
|
|
int cShiftCount = 0; /* # bits shifted out of low dividend */
|
|
|
|
/* check overflow */
|
|
if ( ! ulDivisor )
|
|
return 1;
|
|
|
|
/* do high divide
|
|
* remainder is left in dividend to continue division
|
|
*/
|
|
pqwQuot->ulHi = qwDividend.ulHi / ulDivisor;
|
|
qwDividend.ulHi = qwDividend.ulHi % ulDivisor;
|
|
|
|
|
|
/* shift dividend left, until all in low dividend
|
|
* save low bits shifted out */
|
|
while (qwDividend.ulHi) {
|
|
ulBitsOut >>= 1;
|
|
if (qwDividend.ulLo & BIT0)
|
|
ulBitsOut |= BIT31;
|
|
qwDividend.ulLo >>= 1;
|
|
if (qwDividend.ulHi & BIT0)
|
|
qwDividend.ulLo |= BIT31;
|
|
qwDividend.ulHi >>= 1;
|
|
cShiftCount++;
|
|
}
|
|
/* check if shifted division is possible */
|
|
if (qwDividend.ulLo && ulDivisor > qwDividend.ulLo)
|
|
return 2;
|
|
|
|
/* do "shifted" divide
|
|
* again remainder stays in dividend for next divide */
|
|
pqwQuot->ulLo = qwDividend.ulLo / ulDivisor;
|
|
qwDividend.ulLo = qwDividend.ulLo % ulDivisor;
|
|
|
|
/* shift back
|
|
* - low bits back into dividend
|
|
* - quotient up */
|
|
qwDividend.ulLo <<= cShiftCount;
|
|
qwDividend.ulLo += (ulBitsOut >> (32 - cShiftCount));
|
|
pqwQuot->ulLo <<= cShiftCount;
|
|
|
|
|
|
/* do "regular" (non-shifted) low division
|
|
* add quotient to previous "shifted" quotient
|
|
*/
|
|
pqwQuot->ulLo += qwDividend.ulLo / ulDivisor;
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* Multiply 64bit by 32bit producing 64bit result.
|
|
* Input:
|
|
* qwFact64 -- 64bit factor in QWORD struct
|
|
* ulFact32 -- 32bit factor
|
|
* pqwResult -- ptr to QWORD struct for result
|
|
* Output:
|
|
* puts result *pqwResult qword
|
|
* returns 0 if completed successfully, 1 if overflow
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
#define BIT0 0x1L
|
|
#define BIT31 0x80000000L
|
|
|
|
|
|
int mult64_32 (QWORD qwFact64, ULONG ulFact32, PQWORD pqwResult)
|
|
{
|
|
pqwResult->ulLo = 0;
|
|
pqwResult->ulHi = 0;
|
|
|
|
while (1) {
|
|
/* if lowest bit in Fact32 set, add Fact64 to result */
|
|
if (ulFact32 & BIT0) {
|
|
/* add high bits, and check for overflow */
|
|
pqwResult->ulHi += qwFact64.ulHi;
|
|
if (qwFact64.ulHi > pqwResult->ulHi)
|
|
return 1;
|
|
|
|
/* add low bits, if overflow increment hi */
|
|
pqwResult->ulLo += qwFact64.ulLo;
|
|
if (qwFact64.ulLo > pqwResult->ulLo) {
|
|
/* check if inc will cause overflow */
|
|
if (pqwResult->ulHi == 0xfffffff)
|
|
return 1;
|
|
pqwResult->ulHi++;
|
|
}
|
|
}
|
|
/* shift 32 factor right, done when zero */
|
|
if (!(ulFact32 >>= 1))
|
|
return 0;
|
|
|
|
/* shift 64 factor left, return if overflow */
|
|
if (qwFact64.ulHi & BIT31)
|
|
return 1;
|
|
qwFact64.ulHi <<= 1;
|
|
|
|
/* move ulLo bit 32 to ulHi */
|
|
if (qwFact64.ulLo & BIT31)
|
|
qwFact64.ulHi |= 1L;
|
|
qwFact64.ulLo <<=1;
|
|
|
|
} /* end while until return */
|
|
}
|
|
|
|
/*********************************************************************
|
|
* Subtract two signed 64bit values
|
|
* Input:
|
|
* pqwResult -- ptr to QWORD struct for result
|
|
* qwSub1 -- 64bit to be subtracted from
|
|
* qwSub2 -- 64bit subtrahend
|
|
* Output:
|
|
* puts result *pqwResult
|
|
* returns 0 if completed successfully, 1 if overflow
|
|
*/
|
|
|
|
|
|
int Sub64_64 (PQWORD pqwResult, QWORD qwFrom, QWORD qwSub)
|
|
{
|
|
/* subtract */
|
|
pqwResult->ulHi = qwFrom.ulHi - qwSub.ulHi;
|
|
pqwResult->ulLo = qwFrom.ulLo - qwSub.ulLo;
|
|
if (pqwResult->ulLo > qwFrom.ulLo) /* borrow from high? */
|
|
pqwResult->ulHi--;
|
|
|
|
/* overflow check */
|
|
if (pqwResult->ulHi < qwFrom.ulHi)
|
|
return 0; /* valid */
|
|
else if (pqwResult->ulHi > qwFrom.ulHi)
|
|
return 1; /* overflow, found in hi */
|
|
else if (pqwResult->ulLo > qwFrom.ulLo)
|
|
return 1; /* hi equal, overflow found in lo */
|
|
return 0; /* hi equal, no overflow in lo */
|
|
}
|
|
|
|
#endif
|
|
|
|
#if (defined (NTNAT) || defined (WIN32))
|
|
/***************************************************
|
|
|
|
NT native dll init routine
|
|
|
|
****************************************************/
|
|
SHORT csTempCtr; /* a counter - had to make this global..compile fails */
|
|
ULONG culTemp; /* - do - */
|
|
|
|
NTSTATUS
|
|
TimerDllInitialize (
|
|
IN PVOID DllHandle,
|
|
ULONG Reason,
|
|
IN PCONTEXT Context OPTIONAL
|
|
)
|
|
{
|
|
DllHandle, Context; // avoid compiler warnings
|
|
|
|
if (Reason != DLL_PROCESS_ATTACH) { // if detaching return immediately
|
|
return TRUE;
|
|
}
|
|
|
|
for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
|
|
pTimer [csTempCtr].ulLo = 0L;
|
|
pTimer [csTempCtr].ulHi = 0L;
|
|
pTimer [csTempCtr].TUnits = TIMER_FREE;
|
|
}
|
|
|
|
bTimerInit = TRUE;
|
|
GetTimerFreq ();
|
|
ulTimerOverhead = CalibrateTimerForOverhead ();
|
|
/* the timer overhead will be placed in a global variable */
|
|
bCalibrated = TRUE;
|
|
return TRUE;
|
|
|
|
}
|
|
#endif
|