Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1758 lines
58 KiB

4 years ago
  1. /* OS/2 Version
  2. * Timer.c - Source file for a statistical
  3. * dll package that exports four
  4. * entry points:
  5. * a) TimerOpen
  6. * b) TimerInit
  7. * c) TimerRead
  8. * d) TimerClose
  9. * e) TimerReport
  10. * f) TimerQueryPerformanceCounter
  11. * g) TimerConvertTicsToUSec
  12. *
  13. * Entry point a) opens a timer object
  14. * and returns a handle to the timer. This
  15. * handle is an index into an array of timer
  16. * objects (structs) that are allocated at
  17. * the time of the initialization of the dll.
  18. * This ensures that allocation is done once only.
  19. * Each application program will call this
  20. * this function so that it has its own set
  21. * of timers to use with TimerInit and TimerRead.
  22. * The units of the time returned by TimerRead
  23. * is also made available as a parameter to
  24. * this call.
  25. *
  26. * Entry point b) is called by the application
  27. * before commencing a timing operation. This
  28. * function is called with a handle to a timer
  29. * object that was opened. This function has to
  30. * to be called before a call to TimerRead. The
  31. * current time is stored in the timer object.
  32. *
  33. * Entry point c) is called each time the time
  34. * since the previous call to TimerInit is
  35. * desired. This call also uses the handle to
  36. * a timer that has been previosly opened. The
  37. * current time is obtained form the lowlevel
  38. * timer and this and the time at TimerInit time
  39. * are used, along with the clock frequency and
  40. * the return time units and the elapsed time
  41. * is obtained and returned as a ULONG.
  42. *
  43. * Entry point d) is called whenever an opened
  44. * timer is not needed any longer. This call
  45. * resets the timer and makes this timer as
  46. * available to future calls to TimerOpen.
  47. *
  48. * Entry point e) returns the time obtained during
  49. * the last call to TimerInit, TimerRead and the
  50. * last returned time.
  51. *
  52. * Entry point f) accepts pointers to 2 64 bit
  53. * vars. Upon return, the first will contain the
  54. * the current timer tic count and the second,
  55. * if not NULL, will point to the timer freq.
  56. *
  57. * Entry point g) accepts Elapsed Tics as ULONG,
  58. * Frequency as a ULONG and returns the time in
  59. * microsecs. as a ULONG.
  60. *
  61. * The dll initialization routine does the
  62. * following:
  63. * a) Obtains the timer overhead for calibration
  64. * purposes.
  65. * b) Allocates memory for a large number of
  66. * timer objects (this will be system dep).
  67. * c) Initializes each timer objects "Units'
  68. * element to a "TIMER_FREE" indicator.
  69. * d) Determines the lowlevel timer's frequency.
  70. *
  71. * TimerRead uses an external asm routine to perform
  72. * its computation for elapsed time.
  73. *
  74. * Created - Paramesh Vaidyanathan (vaidy)
  75. * Initial Version - October 18, '90
  76. *
  77. * Modified to include f). - Feb. 14, 1992. (vaidy).
  78. */
  79. char *COPYRIGHT = "Copyright Microsoft Corporation, 1991";
  80. #ifdef SLOOP
  81. #define INCL_DOSINFOSEG
  82. #define INCL_DOSDEVICES
  83. #define INCL_DOSPROCESS
  84. #endif
  85. #if (defined (OS2286) || defined (OS2386) || defined (SLOOP))
  86. #include <os2.h>
  87. #include <stdio.h>
  88. #define INCL_DOS
  89. #define INCL_DOSPROFILE
  90. #endif
  91. #if (defined (OS2286) || defined (SLOOP))
  92. #include <math.h>
  93. #endif
  94. #if (defined (NTNAT) || defined (WIN32) || defined (OS2SS))
  95. #include <nt.h>
  96. #endif
  97. #ifdef WIN32
  98. #include <ntrtl.h>
  99. #include <nturtl.h>
  100. #include <windows.h>
  101. #endif
  102. #ifdef WIN16
  103. #include <windows.h>
  104. #endif
  105. #include "timing.h"
  106. /*****************************END OF INCLUDES*************************/
  107. #define ITER_FOR_OVERHEAD 250
  108. #define SUCCESS_OK 0
  109. #define ONE_MILLION 1000000L
  110. #define MICROSEC_FACTOR 1000000
  111. #define TIMER_FREQ 1193167L /* clock frequency - Hz */
  112. /*********************************************************************/
  113. Timer pTimer [MAX_TIMERS]; /* array of timer struct */
  114. BOOL bTimerInit = FALSE; /* TRUE indicates low level timer exists */
  115. ULONG ulTimerOverhead = 50000L; /* timer overhead stored here */
  116. BOOL bCalibrated = FALSE; /* TRUE subtracts overhead also */
  117. ULONG ulFreq; /* timer frequency */
  118. LONG aScaleValues[] = {1000000000L, 1000000L, 1000L, 1L, 10L, 1000L};
  119. /* this is the table for scaling the units */
  120. ULONG ulElapsedTime = 0L;
  121. /********************* internal unexported routines ***************/
  122. ULONG CalibrateTimerForOverhead (VOID);
  123. /*****************DEFINE VARIBLES AND PROTOTYPE FNS. FOR PLATFORMS*****/
  124. #if (defined (OS2386) || defined (WIN16)) /* use for Win with new timer */
  125. extern VOID FAR PASCAL ReturnElapsedTime (PQWORD,
  126. PQWORD,
  127. ULONG far *,
  128. _TimerUnits,
  129. ULONG far *,
  130. PQWORD);
  131. #define MICROSECOND_INDEX 3 /* used as a hack. VTD 386 has introduced
  132. some wierd problem if a unit that isn't
  133. MICROSECONDS, is used */
  134. #endif
  135. #if (defined (OS2286) || defined (OS2386))
  136. APIRET APIENTRY DosTmrQueryFreq(PULONG pulTmrFreq);
  137. APIRET APIENTRY DosTmrQueryTime(PQWORD pqwTmrTime);
  138. SHORT GetTimerFreq (VOID);
  139. int TIMING_INIT (VOID);
  140. int mult64_32 (QWORD, ULONG, PQWORD);
  141. #endif
  142. #if (defined (OS2286) || defined (SLOOP))
  143. SHORT GetTimerFreq (VOID);
  144. #define BIT0 0x1L /* for the division of 64 by 32 */
  145. #define BIT31 0x80000000L /* - do - */
  146. int Div64By32 (QWORD, ULONG, PQWORD);
  147. int Sub64_64 (PQWORD, QWORD, QWORD);
  148. #endif
  149. #ifdef W32S
  150. #define BIT0 0x1L /* for the division of 64 by 32 */
  151. #define BIT31 0x80000000L /* - do - */
  152. int Div64By32 (QWORD, ULONG, PQWORD);
  153. int Sub64_64 (PQWORD, QWORD, QWORD);
  154. int mult64_32 (QWORD, ULONG, PQWORD);
  155. #endif
  156. #if (defined (OS2286) || defined (OS2386) || defined (WIN16) || defined (SLOOP))
  157. /* QWORD defined in the timing.h file for WIN platform */
  158. QWORD qwCurrent,
  159. qwPrev,
  160. qwResult,
  161. qwTemp;
  162. #endif
  163. #ifdef WIN16
  164. USHORT FAR PASCAL PerfStartTime (VOID);
  165. ULONG FAR PASCAL PerfGetTime (VOID);
  166. #define RESERVED 0
  167. #define INITCOUNT 50
  168. #define SYNCERR 4 /* Max error between clocks, millisec */
  169. #define MICROSYNCERR SYNCERR * 1000 /* Max error in microseconds */
  170. #define SYNCCOUNT 50 /* Number of times to try to sync clks */
  171. #define TIMER_SCALE .83810588 /* = 12 / 14.318 MHz */
  172. #define MAX_MILLISECS 1800000L /* One Half Hour: 1800000L */
  173. #define WRAP_8253 (ULONG)(0X10000 * TIMER_SCALE)
  174. /* 44 milliseconds is the maximum acceptable period between system timer
  175. updates */
  176. #define MAX_SYSTEM_TIMER_INTERVAL 440 /* doc is wrong: value is
  177. in tenths of milliseconds */
  178. #define TIME_INTERVAL_LOW 40
  179. #define TIME_INTERVAL_HIGH 48
  180. ULONG ulMilliSecStart = 0;
  181. ULONG ulCurrentMilliSec = 0L;
  182. ULONG ulPrevSysTime;
  183. /* previous time, to keep from falling back */
  184. ULONG ulPrevRetTime;
  185. /* previous time, to keep from falling back */
  186. BOOL bInitialized = FALSE;
  187. ULONG ulOverhead;
  188. ULONG ulNumResyncs = 0L;
  189. ULONG ulCurrentTime = 0L;
  190. /* Return Values */
  191. #define TIMER_ERROR (ULONG)~0
  192. #define OK 0
  193. #define SYSTEM_TIMER_ERROR (USHORT)~0
  194. USHORT PASCAL FAR Start8253( void );
  195. PASCAL FAR Stop8253( void );
  196. VOID PASCAL FAR IntOn( void );
  197. VOID PASCAL FAR IntOff( void );
  198. VOID PASCAL FAR Int3( void );
  199. VOID FAR PASCAL ReSynchronize( VOID );
  200. #endif
  201. #if (defined (NTNAT) || defined (OS2SS) || defined (WIN32))
  202. NTSYSAPI
  203. NTSTATUS
  204. NTAPI
  205. NtQueryPerformanceCounter (
  206. OUT PLARGE_INTEGER PerformanceCount,
  207. OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  208. );
  209. LARGE_INTEGER PerfFreq;
  210. LARGE_INTEGER CountCurrent;
  211. SHORT GetTimerFreq (VOID);
  212. #endif
  213. #ifdef WIN16
  214. int FAR PASCAL LibMain(HANDLE, WORD, WORD, LPSTR);
  215. VOID WinDllInit (VOID);
  216. VOID FAR PASCAL WEP (int);
  217. extern BOOL WinVTDAddr (VOID); // returns address of VTD
  218. extern VOID WinVTDTime (PQWORD); // returns the time
  219. BOOL fWinDllInitDone = FALSE; // flag to ensure that init has
  220. // been done.
  221. BOOL fUseApiTimer = FALSE; // if VTD not available
  222. // use API Timer.
  223. #endif
  224. #if (defined (OS2SS) || defined (SLOOP))
  225. int FAR PASCAL TIMING_INIT (VOID);
  226. #endif
  227. #ifdef SLOOP
  228. int mult64_32 (QWORD, ULONG, PQWORD);
  229. HFILE hDevHandle;
  230. #endif
  231. /****************** internal unexported routines end***************/
  232. /*
  233. * Function - TimerOpen (EXPORTED)
  234. * Arguments -
  235. * (a) SHORT far * - address to which to return handle of
  236. * the timer object.
  237. * (b) TimerUnits - units in which to return time from
  238. * TimerRead. It is one of the enum
  239. * types defined in the header file.
  240. *
  241. * Returns - SHORT - 0 if handle was returned successfully
  242. * Else, an error code which may be one of:
  243. *
  244. * TIMERERR_NOT_AVAILABLE
  245. * TIMERERR_NO_MORE_HANDLES
  246. * TIMERERR_INVALID_UNITS
  247. *
  248. * Obtains the handle to a timer object after opening it.
  249. * Should precede any calls to timer manipulation. Checks
  250. * for validity of timer units.
  251. */
  252. SHORT FAR PASCAL TimerOpen (phTimerHandle, TimerUnits)
  253. SHORT far * phTimerHandle;
  254. _TimerUnits TimerUnits;
  255. {
  256. SHORT csTemp;
  257. #ifdef WIN16
  258. /* Perform DLL initialization if hasn't been done */
  259. if (!fWinDllInitDone)
  260. WinDllInit();
  261. #endif
  262. if ((TimerUnits < KILOSECONDS)
  263. || (TimerUnits > NANOSECONDS)) /* out of the enum range */
  264. return (TIMERERR_INVALID_UNITS);
  265. if (!bTimerInit) /* set during dll initialization */
  266. return (TIMERERR_NOT_AVAILABLE);
  267. /* else check if any timers are not in use and return the first
  268. available timer handle....actually the index into the
  269. timer object array */
  270. for (csTemp = 0; csTemp < MAX_TIMERS; csTemp++) {
  271. if (pTimer [csTemp].TUnits == TIMER_FREE) {
  272. *phTimerHandle = csTemp; /* found a free timer. Return
  273. the handle */
  274. pTimer [csTemp].ulHi = pTimer[csTemp].ulLo = 0L;
  275. pTimer [csTemp].TUnits =
  276. TimerUnits; /* set the units for timer */
  277. return (SUCCESS_OK);
  278. }
  279. }
  280. /* if exec reached here, all timers are being used */
  281. return (TIMERERR_NO_MORE_HANDLES);
  282. }
  283. /*
  284. * Function - TimerInit (EXPORTED)
  285. * Arguments -
  286. * (a) SHORT - hTimerHandle
  287. *
  288. * Returns - SHORT - 0 if call successful
  289. * Else, an error code if handle invalid:
  290. *
  291. * TIMERERR_INVALID_HANDLE
  292. *
  293. * Calls the low-level timer and sets the ulHi and ulLo of the
  294. * chosen timer to the time returned by the timer. Should be
  295. * called after opening the timer with TimerOpen.
  296. */
  297. SHORT FAR PASCAL TimerInit (SHORT hTimerHandle)
  298. {
  299. #if (defined (NTNAT) || defined (OS2SS) || defined (WIN32))
  300. NTSTATUS NtStatus;
  301. #endif
  302. #ifdef WIN16
  303. // Perform DLL initialization if hasn't been done
  304. //
  305. if (!fWinDllInitDone) {
  306. WinDllInit();
  307. }
  308. #endif
  309. if ((hTimerHandle > MAX_TIMERS - 1) ||
  310. (pTimer [hTimerHandle].TUnits == TIMER_FREE))
  311. /* this timer has not been opened or does not exist. Return error */
  312. return (TIMERERR_INVALID_HANDLE);
  313. /* otherwise get the time from the low-level timer into
  314. the structure */
  315. #if (defined (OS2286) || defined (OS2386))
  316. DosTmrQueryTime (&qwTemp); /* use the perfview timer */
  317. #elif (defined(WIN16))
  318. if (!fUseApiTimer) /* VTD timer available */
  319. WinVTDTime (&qwTemp);
  320. else {
  321. qwTemp.ulLo = PerfGetTime (); /* use the old timer */
  322. qwTemp.ulHi = 0L; /* clear this fella */
  323. }
  324. #endif
  325. #ifdef SLOOP
  326. /* issue a DosDevIOCtl to read the time */
  327. DosDevIOCtl (&qwTemp, &qwTemp, 0x40, 0x88, hDevHandle);
  328. #endif
  329. #if (defined (OS2286) || defined (OS2386) || defined (WIN16) || defined (SLOOP))
  330. pTimer [hTimerHandle].ulLo = qwTemp.ulLo; // get it into the timer
  331. pTimer [hTimerHandle].ulHi = qwTemp.ulHi; // structure
  332. /* this timer structure has all the information needed to compute
  333. the elapsed time. So return success, if there was no problem */
  334. #endif
  335. #if (defined (OS2SS) || defined (NTNAT) || defined (WIN32))
  336. NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
  337. pTimer [hTimerHandle].ulLo = CountCurrent.LowPart;
  338. pTimer [hTimerHandle].ulHi = CountCurrent.HighPart;
  339. /* this timer structure has all the information needed to compute
  340. the elapsed time. So return success, if there was no problem */
  341. #endif
  342. return (SUCCESS_OK);
  343. }
  344. /*
  345. * Function - TimerRead (EXPORTED)
  346. * Arguments -
  347. * (a) SHORT - hTimerHandle
  348. *
  349. * Returns - ULONG - elapsed time since last call to TimerInit
  350. * if call successful.
  351. *
  352. * Else, an error code if handle invalid or output
  353. * overflow. The error code will be the same:
  354. *
  355. * TIMERERR_OVERFLOW (max possible ULONG)
  356. *
  357. * Calls the low-level timer. Uses the ulLo and ulHi from the
  358. * timer's structure and subtracts the current time from the
  359. * saved time. Uses ReturnElapsedTime (an external ASM proc)
  360. * to return the elapsed time taking into account the clock
  361. * frequency and the units for this timer. Each call to this
  362. * returns the time from the previous TimerInit.
  363. *
  364. * The user should interpret the return value sensibly to check
  365. * if the result is an error or a real value.
  366. */
  367. ULONG FAR PASCAL TimerRead (SHORT hTimerHandle)
  368. {
  369. #if (defined (NTNAT) || defined (OS2SS) || defined (WIN32))
  370. NTSTATUS NtStatus;
  371. LARGE_INTEGER ElapsedTime, CountPrev, LargeOverhead;
  372. #endif
  373. #ifdef W32S
  374. QWORD qwTemp, qwTemp1, qwTemp2;
  375. #endif
  376. #if (defined (OS2286) || defined (SLOOP))
  377. QWORD qwSubtractedTime;
  378. #endif
  379. #ifdef WIN16
  380. // Perform DLL initialization if hasn't been done
  381. //
  382. if (!fWinDllInitDone) {
  383. WinDllInit();
  384. }
  385. #endif
  386. if ((hTimerHandle > MAX_TIMERS - 1)
  387. || (pTimer [hTimerHandle].TUnits == TIMER_FREE))
  388. /* this timer has not been opened or does not exist.
  389. Return TIMERERR_OVERFLOW ie. 0xffffffff, the max. possible
  390. ULONG. The user should interpret such a result sensibly */
  391. return (TIMERERR_OVERFLOW);
  392. /* let us handle a few cases at a time. First let us get the
  393. 286 and 386 times computed first */
  394. #if (defined (OS2286) || defined (OS2386))
  395. DosTmrQueryTime (&qwCurrent); /* use the Perfview timer */
  396. #elif (defined (WIN16))
  397. if (!fUseApiTimer) /* VTD Timer available */
  398. WinVTDTime (&qwCurrent);
  399. else { /* use the old Api Timer */
  400. qwCurrent.ulLo = PerfGetTime ();
  401. qwCurrent.ulHi = 0L;
  402. }
  403. ulFreq = TIMER_FREQ;
  404. #elif (defined (SLOOP))
  405. /* issue a DosDevIOCtl to read the time */
  406. DosDevIOCtl (&qwCurrent, &qwCurrent, 0x40, 0x88, hDevHandle);
  407. ulFreq = TIMER_FREQ; /* hardcode the frequency */
  408. #endif
  409. #if (defined (OS2286) || defined (OS2386) || defined (WIN16) || defined (SLOOP))
  410. /* since we are going to be using separate routines for comptuing the
  411. 64 bit differences, let us store the values into QWORDS. */
  412. qwPrev.ulLo = pTimer [hTimerHandle].ulLo;
  413. qwPrev.ulHi = pTimer [hTimerHandle].ulHi;
  414. /* use the special purposes asm functions to do the 64-32 computation */
  415. #if (defined (OS2386) || defined (WIN16))
  416. /* call the asm routine ReturnElapsedTime */
  417. if (!bCalibrated)
  418. ulTimerOverhead = 0; /* so that overhead will not
  419. be used while calibrating */
  420. #ifdef WIN16
  421. if (fUseApiTimer) /* just subtract the prev. time from the current */
  422. ulElapsedTime = qwCurrent. ulLo - qwPrev.ulLo;
  423. else
  424. #endif
  425. /* MICROSECOND_INDEX = 3, i.e. The units is MICRO_SECONDS */
  426. ReturnElapsedTime (&qwPrev, &qwCurrent, &ulFreq,
  427. MICROSECOND_INDEX,
  428. &ulTimerOverhead, &qwResult);
  429. /* the 386 model allows the difference of the 2 quad words
  430. to be divided by the freq. and scaled approproiately */
  431. if (!bCalibrated)
  432. ulTimerOverhead = 50000L; /* set this back to the old high. This
  433. is basically for OS/2 386. */
  434. /* if the high 32 bits of the returned QWORD is not zero,
  435. there has been an overflow - CHECK */
  436. if (qwResult.ulHi)
  437. return (TIMERERR_OVERFLOW);
  438. #ifdef WIN16
  439. if (!fUseApiTimer) { /* return time here only if VTD.386 installed */
  440. #endif
  441. /* everything is just fine, return the elapsed time as a ULONG after
  442. scaling. Ovrehead calibration has already been done by the
  443. ReturnElapsedTime routine. */
  444. if (pTimer [hTimerHandle].TUnits < 3)
  445. qwResult.ulLo /= aScaleValues [pTimer [hTimerHandle].TUnits];
  446. else
  447. qwResult.ulLo *= aScaleValues [pTimer [hTimerHandle].TUnits];
  448. ulElapsedTime = qwResult.ulLo; // this is used by TimerReport. So stuff
  449. // it in.
  450. if ((ULONG) qwResult.ulLo > 0L)
  451. return (qwResult.ulLo);
  452. else
  453. return (0L);
  454. #ifdef WIN16
  455. } /* close paren. for if (!fUseApiTimer) */
  456. #endif
  457. /* we are done as far as the 32 bit OS/2 platform or Win 3.x (VTD only)
  458. are concerned */
  459. #elif (defined (OS2286) || defined (SLOOP))
  460. /* THIS STUFF IS MESSY. Have to use 16 bit registers and do 64
  461. bit arithmetic */
  462. Sub64_64 (&qwSubtractedTime, qwCurrent, qwPrev);
  463. /* on return, qwResult will contain the difference of the 2
  464. 64 bit numbers */
  465. /* multiply this number by 1000000 to convert to us equivalent...
  466. this is how DosTmrQueryTime works */
  467. mult64_32 (qwSubtractedTime, ONE_MILLION, &qwResult);
  468. /* divide this result by the frequency */
  469. /* in this case, call the Div64by32 routine to do the division */
  470. Div64By32 (qwResult, ulFreq, &qwTemp);
  471. /* the result of this division should fit into 32 bits */
  472. ulElapsedTime = qwTemp.ulLo;
  473. /* OK, we now have the result of dividing the clock tics by
  474. frequency, in ulElapsedTime */
  475. #endif /* end of second if (defined(OS2386) || defined (WIN16)) */
  476. #endif /* end of ifdef OS2386, and elif OS2286 or WIN */
  477. #if (defined (NTNAT) || defined (WIN32) || defined (OS2SS))
  478. NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
  479. CountPrev.LowPart = pTimer [hTimerHandle].ulLo;
  480. CountPrev.HighPart = (LONG) pTimer [hTimerHandle].ulHi;
  481. ElapsedTime.LowPart = CountCurrent.LowPart;
  482. ElapsedTime.HighPart = (LONG) CountCurrent.HighPart;
  483. #ifndef W32S
  484. /* everything is just fine, convert to double, subtract the times,
  485. divide by the frequency, convert to MICROSECONDS and return
  486. the elapsed time as a ULONG */
  487. /* convert to us., divide the count by the clock frequency that
  488. has already been obtained */
  489. ElapsedTime = RtlLargeIntegerSubtract (ElapsedTime, CountPrev);
  490. ElapsedTime = RtlExtendedIntegerMultiply (ElapsedTime, MICROSEC_FACTOR);
  491. ElapsedTime = RtlExtendedLargeIntegerDivide (ElapsedTime,
  492. PerfFreq.LowPart,
  493. NULL);
  494. #else // use the local math routines for calculation on W32S
  495. qwTemp1.ulLo = ElapsedTime.LowPart;
  496. qwTemp1.ulHi = ElapsedTime.HighPart;
  497. qwTemp2.ulLo = CountPrev.LowPart;
  498. qwTemp2.ulHi = CountPrev.HighPart;
  499. Sub64_64(&qwTemp, qwTemp1, qwTemp2);
  500. mult64_32 (qwTemp, (ULONG) MICROSEC_FACTOR, &qwTemp1);
  501. Div64By32 (qwTemp1, PerfFreq.LowPart, &qwTemp);
  502. ElapsedTime.LowPart = qwTemp.ulLo;
  503. ElapsedTime.HighPart = qwTemp.ulHi;
  504. #endif
  505. // if the timer is not calibrated, set ulElapsedTime to be the
  506. // low part of ElapsedTime. This is because, we do not have to
  507. // do to any arithmetic to this before returning the value.
  508. if (!bCalibrated)
  509. ulElapsedTime = ElapsedTime.LowPart;
  510. #endif
  511. #ifndef OS2386
  512. /* this code is common for all platforms but OS2386. For Win3.x
  513. if VTD.386 has been installed, the code below should not matter,
  514. since we should have returned the time by now.
  515. The elapsed time will be scaled, overhead subtracted
  516. and the time returned */
  517. /* we have ulElapsedTime. Scale it and do the needful */
  518. /* divide or multiply by the scale factor */
  519. #ifdef WIN16
  520. if (fUseApiTimer) {
  521. #endif
  522. if (bCalibrated) {
  523. #ifdef WIN32
  524. // Applications like the PROBE call TimerRead repeatedly
  525. // without calling TimerInit, for more than 70 minutes. This
  526. // screws up things. So treat everything as 64 bit numbers
  527. // until the very end.
  528. if ((ElapsedTime.LowPart < ulTimerOverhead) &&
  529. (!ElapsedTime.HighPart)) { // low part is lower than overhead
  530. // and high part is zero..then make
  531. // elapsed time 0. We don't want
  532. // negative numbers.
  533. ElapsedTime.HighPart = 0L;
  534. ElapsedTime.LowPart = 0L;
  535. }
  536. else { // subtract the overhead in tics before converting
  537. // to time units
  538. LargeOverhead.HighPart = 0L;
  539. LargeOverhead.LowPart = ulTimerOverhead;
  540. #ifdef W32S
  541. // use temp vars. to convert LargeInts to QWORDS
  542. qwTemp1.ulLo = ElapsedTime.LowPart;
  543. qwTemp1.ulHi = ElapsedTime.HighPart;
  544. qwTemp2.ulLo = LargeOverhead.LowPart;
  545. qwTemp2.ulHi = LargeOverhead.HighPart;
  546. Sub64_64(&qwTemp, qwTemp1, qwTemp2);
  547. ElapsedTime.LowPart = qwTemp.ulLo;
  548. ElapsedTime.HighPart = qwTemp.ulHi;
  549. #else // For Win32
  550. ElapsedTime = RtlLargeIntegerSubtract (ElapsedTime,
  551. LargeOverhead);
  552. #endif
  553. }
  554. if (pTimer [hTimerHandle].TUnits <= MICROSECONDS) {
  555. #ifdef W32S
  556. qwTemp1.ulLo = ElapsedTime.LowPart;
  557. qwTemp1.ulHi = ElapsedTime.HighPart;
  558. Div64By32 (qwTemp1,
  559. (ULONG) aScaleValues [pTimer [hTimerHandle].TUnits],
  560. &qwTemp);
  561. ElapsedTime.LowPart = qwTemp.ulLo;
  562. ElapsedTime.HighPart = qwTemp.ulHi;
  563. #else
  564. ElapsedTime = RtlExtendedLargeIntegerDivide (
  565. ElapsedTime,
  566. aScaleValues [pTimer [hTimerHandle].TUnits],
  567. NULL
  568. );
  569. #endif
  570. }
  571. else {
  572. #ifdef W32S
  573. qwTemp1.ulLo = ElapsedTime.LowPart;
  574. qwTemp1.ulHi = ElapsedTime.HighPart;
  575. mult64_32(qwTemp1,
  576. (ULONG)aScaleValues [pTimer [hTimerHandle].TUnits],
  577. &qwTemp);
  578. ElapsedTime.LowPart = qwTemp.ulLo;
  579. ElapsedTime.HighPart = qwTemp.ulHi;
  580. #else
  581. ElapsedTime = RtlExtendedIntegerMultiply (
  582. ElapsedTime,
  583. aScaleValues [pTimer [hTimerHandle].TUnits]
  584. );
  585. #endif
  586. }
  587. // scaling is done. Now get the time back into 32 bits. This
  588. // should fit.
  589. ulElapsedTime = ElapsedTime.LowPart;
  590. #else // for non WIN32 platforms and non OS2386 platforms
  591. // subtract the overhead before converting tics to time units.
  592. if (ulElapsedTime > ulTimerOverhead)
  593. ulElapsedTime -= ulTimerOverhead;
  594. else
  595. ulElapsedTime = 0L;
  596. if (pTimer [hTimerHandle].TUnits <= MICROSECONDS)
  597. ulElapsedTime /= aScaleValues [pTimer [hTimerHandle].TUnits];
  598. else
  599. ulElapsedTime *= aScaleValues [pTimer [hTimerHandle].TUnits];
  600. #endif
  601. }
  602. if ((LONG) ulElapsedTime < 0L) /* if this guy is -ve, return a 0 */
  603. return (0L);
  604. return (ulElapsedTime);
  605. #ifdef WIN16
  606. }
  607. #endif
  608. #endif /* for all platforms except OS2386 and WIN30 */
  609. }
  610. /*
  611. * Function - TimerClose (EXPORTED)
  612. * Arguments -
  613. * (a) SHORT - hTimerHandle
  614. *
  615. * Returns - SHORT - 0 if call successful
  616. * Else, an error code if handle invalid:
  617. *
  618. * TIMERERR_INVALID_HANDLE
  619. *
  620. * Releases the timer for use by future TimerOpen calls.
  621. * Resets the elements of the timer structure, setting the
  622. * Timer's Units element to TIMER_FREE.
  623. */
  624. SHORT FAR PASCAL TimerClose (SHORT hTimerHandle)
  625. {
  626. #ifdef WIN16
  627. // Perform DLL initialization if hasn't been done
  628. //
  629. if (!fWinDllInitDone) {
  630. WinDllInit();
  631. }
  632. #endif
  633. if ((hTimerHandle > MAX_TIMERS - 1) ||
  634. (pTimer [hTimerHandle].TUnits == TIMER_FREE))
  635. /* error condition, wrong handle */
  636. return (TIMERERR_INVALID_HANDLE);
  637. /* otherwise, set the TimerUnits of this timer to TIMER_FREE,
  638. reset the other elements to zero and return */
  639. pTimer [hTimerHandle].TUnits = TIMER_FREE;
  640. pTimer [hTimerHandle].ulLo = 0L;
  641. pTimer [hTimerHandle].ulHi = 0L;
  642. return (SUCCESS_OK);
  643. }
  644. /*******************************************************************
  645. Added this routine TimerReport to report individual
  646. times. Bob Day requested that such a routine be
  647. created. It just maintains the time from the last
  648. TimerInit and TimerRead and also the last time returned.
  649. This routine copies this to a user specified buffer.
  650. Accepts - PSZ - a pointer to a buffer to print the data out
  651. SHORT - timer handle
  652. Returns - TRUE if Timer exists and is open
  653. - FALSE if Timer not opened
  654. *******************************************************************/
  655. BOOL FAR PASCAL TimerReport (PSZ pszReportString, SHORT hTimerHandle)
  656. {
  657. #ifdef WIN16
  658. // Perform DLL initialization if hasn't been done
  659. //
  660. if (!fWinDllInitDone) {
  661. WinDllInit();
  662. }
  663. #endif
  664. if (pTimer [hTimerHandle].TUnits == TIMER_FREE)
  665. return (FALSE);
  666. /* stored value is in pTimer[hTimerHandle].ulLo and .ulHi */
  667. #if (defined (OS2286) || defined (SLOOP))
  668. sprintf (pszReportString,
  669. "Init Count (tics) %lx:%lx Current Count (tics) %lx:%lx Returned Time %lu ",
  670. pTimer [hTimerHandle].ulHi,
  671. pTimer [hTimerHandle].ulLo, qwCurrent.ulLo, qwCurrent.ulHi,
  672. ulElapsedTime);
  673. #elif (defined (OS2386))
  674. sprintf (pszReportString,
  675. "Init Count (tics) %lu:%lu Current Count (tics) %lu:%lu Returned Time %lu ",
  676. pTimer [hTimerHandle].ulHi,
  677. pTimer [hTimerHandle].ulLo, qwResult.ulLo, qwResult.ulHi,
  678. qwResult.ulLo);
  679. #elif (defined (WIN32))
  680. /*
  681. wsprintf (pszReportString,
  682. "Init Count (tics) %lu:%lu Current Count (tics) %lu:%lu Returned Time %lu ",
  683. pTimer [hTimerHandle].ulHi,
  684. pTimer [hTimerHandle].ulLo, CountCurrent.HighPart,
  685. CountCurrent.LowPart,
  686. ulElapsedTime);
  687. */
  688. #elif (defined (WIN16))
  689. wsprintf ((LPSTR) pszReportString,
  690. (LPSTR) "Init Count (tics) %lu Current Count (tics) %lu Returned Time %lu ",
  691. pTimer [hTimerHandle].ulLo,
  692. qwCurrent.ulLo,
  693. ulElapsedTime);
  694. #endif
  695. return (TRUE);
  696. }
  697. /*******************************************************************
  698. Added this routine TimerQueryPerformanceCounter to report
  699. current tic count at behest of NT GDI folks.
  700. Accepts - PQWORD - a pointer to a 64 bit struct. that will
  701. contain tic count on return.
  702. PQWORD [OPTIONAL) - a pointer to a 64 bit struct. that will
  703. contain frequency on return.
  704. Returns - None.
  705. *******************************************************************/
  706. VOID FAR PASCAL TimerQueryPerformanceCounter (PQWORD pqTic, PQWORD pqFreq OPTIONAL)
  707. {
  708. #if (defined (WIN32))
  709. LARGE_INTEGER TempTic, TempFreq;
  710. // call the NT API to do the needful and return.
  711. NtQueryPerformanceCounter (&TempTic, &TempFreq);
  712. #ifdef W32S
  713. pqTic->ulLo = TempTic.LowPart;
  714. pqTic->ulHi = TempTic.HighPart;
  715. pqFreq->ulLo = TempFreq.LowPart;
  716. pqFreq->ulHi = TempFreq.HighPart;
  717. #else
  718. pqTic->LowPart = TempTic.LowPart;
  719. pqTic->HighPart = TempTic.HighPart;
  720. pqFreq->LowPart = TempFreq.LowPart;
  721. pqFreq->HighPart = TempFreq.HighPart;
  722. #endif
  723. return;
  724. #elif (defined (WIN16))
  725. // If freq. is desired, give it, else just give the tic count back.
  726. if (pqFreq != NULL) {
  727. pqFreq->ulLo = TIMER_FREQ;
  728. pqFreq->ulHi = 0L;
  729. }
  730. if (!fWinDllInitDone)
  731. WinDllInit();
  732. WinVTDTime (pqTic);
  733. return;
  734. #elif (defined(OS2286) || defined(OS2386))
  735. // If freq. is desired, give it, else just give the tic count back.
  736. if (pqFreq != NULL) {
  737. DosTmrQueryFreq (&(pqFreq->ulLo));
  738. pqFreq->ulHi = 0L;
  739. }
  740. DosTmrQueryTime (pqTic);
  741. return;
  742. #elif (defined (SLOOP))
  743. // If freq. is desired, give it, else just give the tic count back.
  744. if (pqFreq != NULL) {
  745. pqFreq->ulLo = TIMER_FREQ;
  746. pqFreq->ulHi = 0L;
  747. }
  748. DosDevIOCtl (pqTic, pqTic, 0x40, 0x88, hDevHandle);
  749. return;
  750. #endif
  751. }
  752. /*******************************************************************
  753. Added this routine TimerConvertTicsToUSec to return
  754. time in usecs. for a given elapsed tic count and freq.
  755. Accepts - ULONG - Elapsed Tic Count.
  756. ULONG - Frequency.
  757. Returns - Elapsed Time in usecs. as a ULONG.
  758. - Zero if input freq. is zero.
  759. *******************************************************************/
  760. ULONG FAR PASCAL TimerConvertTicsToUSec (
  761. ULONG ulElapsedTics,
  762. ULONG ulInputFreq
  763. )
  764. {
  765. #if (defined (WIN32))
  766. #ifdef W32S
  767. QWORD qwTemp, qwTemp1;
  768. #endif
  769. LARGE_INTEGER ElapsedTime;
  770. ULONG ulRemainder = 0L;
  771. // if the bozo gives me a zero freq, return him a zero.
  772. // Let him tear his hair.
  773. if (!ulInputFreq)
  774. return 0L;
  775. // multiply tics by a million and divide by the frequency.
  776. #ifdef W32S
  777. qwTemp.ulHi = 0L;
  778. qwTemp.ulHi = ulElapsedTics;
  779. qwTemp1.ulLo = ElapsedTime.LowPart;
  780. qwTemp1.ulHi = ElapsedTime.HighPart;
  781. mult64_32 (qwTemp, (ULONG) MICROSEC_FACTOR, (PQWORD)&qwTemp1);
  782. Div64By32 (qwTemp1, ulInputFreq, &qwTemp);
  783. ElapsedTime.LowPart = qwTemp.ulLo;
  784. ElapsedTime.HighPart = qwTemp.ulHi;
  785. // grab the remainder also
  786. ulRemainder = qwTemp.ulLo;
  787. #else
  788. ElapsedTime = RtlEnlargedIntegerMultiply (ulElapsedTics, MICROSEC_FACTOR);
  789. ElapsedTime = RtlExtendedLargeIntegerDivide (ElapsedTime,
  790. ulInputFreq,
  791. &ulRemainder);
  792. #endif
  793. ElapsedTime.LowPart += (ulRemainder > (ulInputFreq / 2L));
  794. return (ElapsedTime.LowPart) ; /* get the result into a ULONG */
  795. #elif (defined (WIN16) || defined (OS2386))
  796. QWORD qwOld, qwNew, qwElapsed;
  797. ULONG ulDummy = 0L;
  798. qwOld.ulLo = qwOld.ulHi = 0L; // for use with ReturnElaosedTime.
  799. qwNew.ulHi = 0L; // - do -
  800. qwNew.ulLo = ulElapsedTics; // - do -
  801. // the following routine will do everything.
  802. ReturnElapsedTime (&qwOld, &qwNew, &ulInputFreq,
  803. MICROSECOND_INDEX,
  804. &ulDummy, &qwElapsed);
  805. return (qwElapsed.ulLo);
  806. #elif (defined(OS2286) || defined(SLOOP))
  807. QWORD qwDiff, qwElapsed;
  808. qwDiff.ulLo = ulElapsedTics;
  809. qwDiff.ulHi = 0L;
  810. // multiply tic diff. by a million.
  811. mult64_32 (qwDiff, ONE_MILLION, &qwResult);
  812. // divide by frequency.
  813. Div64By32 (qwResult, ulInputFreq, &qwElapsed);
  814. /* the result of this division should fit into 32 bits */
  815. return (qwElapsed.ulLo + (qwElapsed.ulLo > (ulInputFreq /2L));
  816. #endif
  817. }
  818. /**************** ROUTINES NOT EXPORTED, FOLLOW ************************/
  819. /*
  820. * Function - CalibrateTimerForOverhead (NOT EXPORTED)
  821. * Arguments - None
  822. * Returns - ULONG
  823. *
  824. * Calls TimerElapsedTime a few times to compute the expected
  825. * mean. Calls TimerElapsedTime more number of times and
  826. * averages the mean out of those calls that did not exceed
  827. * the expected mean by 15%.
  828. */
  829. ULONG CalibrateTimerForOverhead (VOID)
  830. {
  831. ULONG ulOverhead [ITER_FOR_OVERHEAD];
  832. ULONG ulTempTotal = 0L;
  833. ULONG ulExpectedValue = 0L;
  834. SHORT csIter;
  835. SHORT csNoOfSamples = ITER_FOR_OVERHEAD;
  836. SHORT hTimerHandle;
  837. if (TimerOpen (&hTimerHandle, MICROSECONDS)) /* open failed. Return 0 */
  838. return (0L);
  839. for (csIter = 0; csIter < 5; csIter++) {
  840. TimerInit (hTimerHandle);
  841. ulOverhead [csIter] = TimerRead (hTimerHandle);
  842. /* if negative, make zero */
  843. if (((LONG) ulOverhead [csIter]) < 0)
  844. ulOverhead [csIter] = 0L;
  845. }
  846. /* The get elapsed time function has been called 6 times.
  847. The idea is to calculate the expected mean, then call
  848. TimerElapsedTime a bunch of times and throw away all times
  849. that are 15% larger than this mean. This would give a
  850. really good overhead time */
  851. for (csIter = 0; csIter < 5; csIter++ )
  852. ulTempTotal += ulOverhead [csIter];
  853. ulExpectedValue = ulTempTotal / 5;
  854. for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++) {
  855. TimerInit (hTimerHandle);
  856. ulOverhead [csIter] = TimerRead (hTimerHandle);
  857. /* if negative, make zero */
  858. if (((LONG) ulOverhead [csIter]) < 0)
  859. ulOverhead [csIter] = 0L;
  860. }
  861. ulTempTotal = 0L; /* reset this value */
  862. for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++ ) {
  863. if (ulOverhead [csIter] <= (ULONG) (115L * ulExpectedValue/100L))
  864. /* include all samples that is < 115% of ulExpectedValue */
  865. ulTempTotal += ulOverhead [csIter];
  866. else
  867. /* ignore this sample and dec. sample count */
  868. csNoOfSamples--;
  869. }
  870. TimerClose (hTimerHandle);
  871. if (csNoOfSamples == 0) /* no valid times. Return a 0 for overhead */
  872. return (0L);
  873. return (ulTempTotal/csNoOfSamples);
  874. }
  875. #if (defined (OS2286) || defined (OS2386) || defined (OS2SS) || defined SLOOP)
  876. /* timing init for OS2 only */
  877. /*
  878. * Function - TIMING_INIT (NOT EXPORTED)
  879. * Arguments - None
  880. * Return - None
  881. *
  882. * TIMING_INIT is called from the entry point in TimerInit.ASM
  883. * when a client process dynamically links to the library.
  884. *
  885. */
  886. int TIMING_INIT (VOID)
  887. {
  888. SHORT csTempCtr; /* a local counter */
  889. USHORT usAction;
  890. for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
  891. pTimer [csTempCtr].ulLo = 0L;
  892. pTimer [csTempCtr].ulHi = 0L;
  893. pTimer [csTempCtr].TUnits = TIMER_FREE;
  894. }
  895. bTimerInit = TRUE;
  896. #if (defined (OS2286) || defined (OS2386) || defined (OS2SS))
  897. GetTimerFreq (); /* get the timer freq. into ulFreq */
  898. #endif
  899. #ifdef SLOOP
  900. DosOpen((char far *) "TIME_16$",
  901. &hDevHandle,
  902. &usAction,
  903. 0L,
  904. FILE_SYSTEM,
  905. FILE_OPEN,
  906. OPEN_ACCESS_READONLY+OPEN_SHARE_DENYNONE,
  907. 0L
  908. );
  909. #endif
  910. ulTimerOverhead = CalibrateTimerForOverhead ();
  911. /* the timer overhead will be placed in a global variable */
  912. bCalibrated = TRUE;
  913. return 1;
  914. }
  915. #endif
  916. #ifndef WIN16 /* get freq for all other platforms */
  917. /*
  918. * Function - GetTimerFreq (NOT EXPORTED)
  919. *
  920. * Arguments - None
  921. *
  922. *
  923. * Return - 0 if successful or an error code if timer not aailable
  924. *
  925. * Calls the function to return freq
  926. *
  927. */
  928. SHORT GetTimerFreq (VOID)
  929. {
  930. #if (defined (OS2286) || defined (OS2386))
  931. DosTmrQueryFreq (&ulFreq);
  932. #elif (defined (NTNAT) || defined (WIN32) || defined (OS2SS))
  933. LARGE_INTEGER PerfCount, Freq;
  934. NTSTATUS NtStatus;
  935. NtStatus = NtQueryPerformanceCounter (&PerfCount, &Freq);
  936. if ((Freq.LowPart == 0L) && (Freq.HighPart == 0L))
  937. /* frequency of zero implies timer not available */
  938. return (TIMERERR_NOT_AVAILABLE);
  939. PerfFreq.LowPart = Freq.LowPart;
  940. PerfFreq.HighPart = (LONG) Freq.HighPart;
  941. #endif
  942. return 0;
  943. }
  944. #endif
  945. #ifdef WIN16
  946. /*********************************************************************
  947. PURPOSE: Contains library routines for the performance timer
  948. FUNCTION: LibMain (HANDLE, WORD, WORD, LPSTR)
  949. PURPOSE: Is called by LibEntry. LibEntry is called by Windows when
  950. the DLL is loaded. The LibEntry routine is provided in
  951. the LIBENTRY.OBJ in the SDK Link Libraries disk. (The
  952. source LIBENTRY.ASM is also provided.)
  953. LibEntry initializes the DLL's heap, if a HEAPSIZE value is
  954. specified in the DLL's DEF file. Then LibEntry calls
  955. LibMain. The LibMain function below satisfies that call.
  956. The LibMain function should perform additional initialization
  957. tasks required by the DLL. In this example, no initialization
  958. tasks are required. LibMain should return a value of 1 if
  959. the initialization is successful.
  960. */
  961. /*********************************************************************/
  962. int FAR PASCAL LibMain(hModule, wDataSeg, cbHeapSize, lpszCmdLine)
  963. HANDLE hModule;
  964. WORD wDataSeg;
  965. WORD cbHeapSize;
  966. LPSTR lpszCmdLine;
  967. {
  968. /*
  969. No initialization is done in this routine. Any required
  970. initialization is done by ApfInitDll() which will be called
  971. only once during profiling of the first API. A global flag,
  972. fWinDllInitDone, is used to control calling the init routine.
  973. This is done to prevent any problems caused by some Microsoft
  974. applications such as Excel and WinWord due to their self loading
  975. capability. These apps use kernel calls before DLLs LibMains
  976. are called.
  977. This feature was added by RezaB when faced with a problem
  978. during API profiling */
  979. return(1);
  980. }
  981. /*********************************************************************
  982. PURPOSE: Contains DLLs initialization for WINDOWs 3.x.
  983. FUNCTION: WinDllInit ()
  984. PURPOSE: Is called only once.
  985. Since no initialization is done by LibMain() routine,
  986. Any required initialization is done this routine which
  987. will be called only once during the first reference to any
  988. of this DLLs exported routines. A global flag, fWinDllInitDone,
  989. is used to control calling this init routine.
  990. This is done to prevent any problems caused by some Microsoft
  991. applications such as Excel and WinWord due to their self loading
  992. capability. These apps use kernel calls before DLLs LibMains
  993. are called.
  994. */
  995. /*********************************************************************/
  996. void WinDllInit ()
  997. {
  998. SHORT csTempCtr; // a local counter
  999. fWinDllInitDone = TRUE; // has to be done here since
  1000. // CalibrateTimerForOverhead() makes calls
  1001. // to TimerInit() and TimerRead() which
  1002. // both check this flag.
  1003. // allocate storage for all MAX_TIMER objects
  1004. //
  1005. for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
  1006. pTimer [csTempCtr].ulLo = 0L;
  1007. pTimer [csTempCtr].ulHi = 0L;
  1008. pTimer [csTempCtr].TUnits = TIMER_FREE;
  1009. }
  1010. /* if not enhanced mode or if the VTDAddr routine returns a non-zero
  1011. do not use the VTD.386 */
  1012. if (!(GetWinFlags() & WF_ENHANCED) || (WinVTDAddr ())) {
  1013. fUseApiTimer = TRUE;
  1014. PerfStartTime (); /* start the timer */
  1015. }
  1016. bTimerInit = TRUE;
  1017. ulTimerOverhead = CalibrateTimerForOverhead ();
  1018. bCalibrated = TRUE;
  1019. }
  1020. /*********************************************************************/
  1021. /*
  1022. FUNCTION: WEP (int)
  1023. PURPOSE: Performs cleanup tasks when the DLL is unloaded. WEP() is
  1024. called automatically by Windows when the DLL is unloaded
  1025. (no remaining tasks still have the DLL loaded). It is
  1026. strongly recommended that a DLL have a WEP() function,
  1027. even if it does nothing but return, as in this example.
  1028. */
  1029. /*********************************************************************/
  1030. VOID FAR PASCAL WEP(int bSystemExit)
  1031. {
  1032. return;
  1033. }
  1034. #endif
  1035. /**********************************************************************/
  1036. #ifdef WIN16
  1037. /**********************************************************************/
  1038. /* Perftime
  1039. *
  1040. * This is a set of timing routines to be used by any machine. No special
  1041. * hardware is necessary. Call PerfStartTime( ) when you want to begin
  1042. * timing, and PerfGetTime( ) to get the amount of time passed since
  1043. * PerfStartTime was called. PerfGetTime( ) may be called any number of
  1044. * times after PerfStartTime( ), and it will always return the amount of
  1045. * time since PerfStartTime. Times greater than an hour, however, return
  1046. * as an error. Overhead is automatically subtracted.
  1047. *
  1048. * Notes: This timer routine has overhead of 60 microseconds on an
  1049. * 6 MHz AT and 23 microseconds on a 16 MHz Compaq386.
  1050. *
  1051. * Restrictions: Don't use with the dos box. This timer uses the
  1052. * system timer used by Dos. If a Dos session is entered
  1053. * during the timing session, timing is only meaningful
  1054. * above 54 milliseconds. Anything below 54 milliseconds
  1055. * is uncertain.
  1056. *
  1057. * Exports: PerfStartTime( )
  1058. * PerfGetTIme( )
  1059. *
  1060. * Imports: None
  1061. *
  1062. *
  1063. * Algorithm: These routines synchronize two asynchronous clocks: The
  1064. * OS/2 system clock, and the 8253 timer chip, counter 0.
  1065. * It uses the 8253 to time the microsecond level, and the
  1066. * system timer to time milliseconds and above. The two
  1067. * timers are synchronized by first converting the time
  1068. * returned by the system clock to an even multiple of the
  1069. * 8253's max count(54 mSecs), an then comparing the 8253
  1070. * count to the remainder of the system count to compensate
  1071. * for differneces between the two counters. If the system
  1072. * timer is within TIMER_LAG behind the 8253 it is consedered
  1073. * to be lagging, otherwise, it is leading. There is reason
  1074. * to expect consederable lag in the system clock, because
  1075. * it is only updated every 32 mSecs.
  1076. *
  1077. * History: 13-Jan-1989 waynej created
  1078. * 30-Nov-1989 vaidy modified - see comment in body of code
  1079. * 27-Dec-1989 russbl fix error of adding two many WRAP8253's
  1080. * check sync of clocks at start
  1081. * generally cleanup code format
  1082. *
  1083. * 26-Feb-1990 c-bobch modified OS/2 version for Windows 3.0
  1084. */
  1085. /**********************************************************************/
  1086. /*
  1087. * PerfStartTime( )
  1088. *
  1089. * This starts the performance timer.
  1090. *
  1091. *
  1092. * Side effects: This gets a pointer to the Global Information Segment,
  1093. * gets the current value of milliseconds from the system
  1094. * clock, and resets the 8253 on the system board.
  1095. *
  1096. * Arguments: none
  1097. *
  1098. * Returns: OK (0) if timers started successfully.
  1099. * SYSTEM_TIMER_ERROR (FFFF) if the system timer is updated
  1100. * less frequently than every 44 milliseconds. This
  1101. * shouldn't be a problem, as IBM machines running
  1102. * OS/2 1.0, 1.1, and 1.2 are updated evry 31 msec.
  1103. *
  1104. * Algorithm: This routine finds out when the system clock is updated
  1105. * by continually poling the Global Information Segment,
  1106. * looking for a change in milliseconds (the system clock
  1107. * is updated at 32hz or about every 31 milliseconds).
  1108. *
  1109. * History: 13-Jan-1989 waynej created
  1110. *
  1111. */
  1112. USHORT FAR PASCAL PerfStartTime( ){
  1113. ULONG ulLastMilliSec;
  1114. ULONG ulInitOverhead = 0;
  1115. if (bInitialized == FALSE) {
  1116. bInitialized = TRUE;
  1117. /* Synchronize the two clocks: first to get overhead */
  1118. /* We'll do it more carefully later on */
  1119. ulOverhead = 0;
  1120. ulPrevSysTime = 0; /* reset previous system time */
  1121. ulPrevRetTime = 0; /* reset previous returned time */
  1122. /* get current system time, milliseconds*/
  1123. ulMilliSecStart = GetCurrentTime ();
  1124. ulLastMilliSec = ulMilliSecStart;
  1125. /* wait for system time to change */
  1126. /* ChrishSh of Win 3.0 had made some modifications to the
  1127. original code so that the do loop had been commented.
  1128. The micro and millisecond timer synch was causing
  1129. some problems and it appears that the milliseconds
  1130. times never changed. As a result, we never used to
  1131. come out of the loop. This is interesting, since
  1132. this seems to work perfectly well under SLOOP. */
  1133. IntOff();
  1134. Start8253(); /* restart microsecond timer */
  1135. IntOn();
  1136. /* Compute PerfGetTime overhead */
  1137. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1138. PerfGetTime(); /* 5 */
  1139. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1140. PerfGetTime(); /* 10 */
  1141. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1142. PerfGetTime(); /* 15 */
  1143. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1144. PerfGetTime(); /* 20 */
  1145. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1146. PerfGetTime(); /* 25 */
  1147. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1148. PerfGetTime(); /* 30 */
  1149. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1150. PerfGetTime(); /* 35 */
  1151. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1152. PerfGetTime(); /* 40 */
  1153. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1154. PerfGetTime(); /* 45 */
  1155. PerfGetTime(); PerfGetTime(); PerfGetTime(); PerfGetTime();
  1156. ulInitOverhead = PerfGetTime( ); /* 50 */
  1157. ulOverhead = ulInitOverhead / INITCOUNT;
  1158. /* Now the overhead has been computed: we can really synchronize */
  1159. ulPrevSysTime = 0; /* reset previous system time */
  1160. ulPrevRetTime = 0; /* reset previous returned time */
  1161. } /* end of if bInitialized condition */
  1162. return(SUCCESS_OK);
  1163. }
  1164. /*
  1165. * PerfGetTime( )
  1166. *
  1167. * This gets the current time since the PerfStartTime.
  1168. *
  1169. * Arguments: none
  1170. *
  1171. * Returns: time in microseconds
  1172. * TIMER_ERROR (-1) in case of time longer than one hour
  1173. *
  1174. *
  1175. * History: 13-Jan-1989 waynej created
  1176. *
  1177. */
  1178. ULONG FAR PASCAL PerfGetTime( ){
  1179. USHORT u8253Time;
  1180. ULONG ulSystemTime;
  1181. ULONG ulMicroSecs;
  1182. IntOff();
  1183. ulSystemTime = GetCurrentTime ();
  1184. u8253Time = Stop8253( );
  1185. IntOn();
  1186. if (bInitialized == FALSE)
  1187. return TIMER_ERROR;
  1188. /* Convert 8253 ticks to microseconds */
  1189. u8253Time = (USHORT)(((ULONG)u8253Time * WRAP_8253 ) / 0xFFFF);
  1190. /* subtract off starting time */
  1191. ulSystemTime -= ulMilliSecStart;
  1192. /* now make sure we have not gone backwards */
  1193. if (ulSystemTime < ulPrevSysTime)
  1194. ulSystemTime = ulPrevSysTime; /* use latest time */
  1195. else
  1196. ulPrevSysTime = ulSystemTime; /* use latest time */
  1197. /*
  1198. The following "if" statement eliminates
  1199. the one hour limit on perftime. Originally, the body of the if
  1200. statement just contained a return statement, returning a TIMER_ERROR.
  1201. Now, if the ulSystemTime happens to exceed one half hour, we restart
  1202. perfstarttime. However, after starting the clock again, perfgettime
  1203. still returns the correct time for that particular call to perfgettime.
  1204. */
  1205. if (ulSystemTime > MAX_MILLISECS) {
  1206. ReSynchronize();
  1207. return( PerfGetTime() );
  1208. }
  1209. /* Convert to microseconds */
  1210. ulSystemTime *= 1000;
  1211. ulMicroSecs = (ulSystemTime / WRAP_8253) * WRAP_8253 + u8253Time;
  1212. if(ulMicroSecs < ulSystemTime - MICROSYNCERR){
  1213. ulMicroSecs += WRAP_8253;
  1214. if (ulMicroSecs > ulSystemTime + MICROSYNCERR /* +
  1215. (pgisInfo->cusecTimerInterval * 100) */ ){
  1216. /* didn't really wrap, use actual clock time */
  1217. ulMicroSecs -= WRAP_8253;
  1218. }
  1219. }
  1220. /* Compensate for overhead */
  1221. if (ulMicroSecs >= ulOverhead)
  1222. ulMicroSecs - ulOverhead;
  1223. /* Now let's try hard to be sure we are not falling back in time */
  1224. if (ulMicroSecs < ulPrevRetTime)
  1225. /* we must have missed a wrap, that's the only way this can happen */
  1226. ulMicroSecs += WRAP_8253;
  1227. ulPrevRetTime = ulMicroSecs;
  1228. return(ulMicroSecs);
  1229. }
  1230. /**********************************************************************/
  1231. VOID FAR PASCAL ReSynchronize( ) {
  1232. ULONG ulSystemTime;
  1233. /*
  1234. Assume that exactly an hour after timing was started, 2 routines
  1235. A and B make call to perfgettime. If we do not take care of the
  1236. multiple calls, A and B will find that it is an hour since
  1237. intialization and both of them would try to reinitialize. This is
  1238. taken care of by the DosEnterCritSec. So, only one of A or B will
  1239. be allowed to reset the clock. The other one would have to wait.
  1240. Say, A reinitializes while B waits. B then enters the critsec and
  1241. obtains the millisec count on the system clock. But it finds that
  1242. the ulSystemtime is now less than the MAX_MILLISECS and hence does
  1243. not execute the inner if-loop. As a result, both A and B now return
  1244. valid times with the call to perfgettime.
  1245. */
  1246. ulSystemTime = GetCurrentTime ();
  1247. ulSystemTime -= ulMilliSecStart;
  1248. /* now make sure we have not gone backwards */
  1249. if (ulSystemTime < ulPrevSysTime)
  1250. ulSystemTime = ulPrevSysTime; /* use latest time */
  1251. else
  1252. ulPrevSysTime = ulSystemTime; /* use latest time */
  1253. if( ulSystemTime > MAX_MILLISECS ) {
  1254. bInitialized = FALSE;
  1255. PerfStartTime();
  1256. ulNumResyncs++;
  1257. }
  1258. }
  1259. /*********END OF APITIMER CODE FOR Windows*********************/
  1260. #endif
  1261. #if (defined (OS2286) || defined (SLOOP) || defined (W32S))
  1262. /*********************************************************************
  1263. * Divide QWORD by ULONG
  1264. * Input:
  1265. * qwDividend -- 64bit dividend in QWORD struct
  1266. * ulDivisor -- 32bit divisor
  1267. * pqwQuot -- ptr to QWORD storage for quotient
  1268. * Output:
  1269. * puts values in Quotient and Remainder
  1270. * returns 0 if completed successfully, 1 if overflow,
  1271. * 2 if unable to complete division (see WARNING)
  1272. *
  1273. * Algorithm:
  1274. * Divide high ulong by divisor, to get high quotient. Then
  1275. * shift remainder down (saving out shifted bits) until highest bit
  1276. * is in low dividend. Do "shifted" divide. Shift this quotient
  1277. * and remainder (with out shifted bits coming back in) back up.
  1278. * Do final low divide, adding quotient to shifted quotient and
  1279. * returning this real remainder.
  1280. *
  1281. * !!! WARNING !!!
  1282. * The divide routine is not strictly a full QWORD / ULONG routine
  1283. * rather it is a QWORD / positive LONG routine. That is, if the
  1284. * highest bit in the divisor is set, the routine can not necessarily
  1285. * complete the division using dividend shift only (that's its method).
  1286. * This condition is checked and causes a return = 2.
  1287. */
  1288. int Div64By32 (QWORD qwDividend, ULONG ulDivisor, PQWORD pqwQuot)
  1289. {
  1290. ULONG ulBitsOut = 0L; /* store bits shifted out of dividend */
  1291. int cShiftCount = 0; /* # bits shifted out of low dividend */
  1292. /* check overflow */
  1293. if ( ! ulDivisor )
  1294. return 1;
  1295. /* do high divide
  1296. * remainder is left in dividend to continue division
  1297. */
  1298. pqwQuot->ulHi = qwDividend.ulHi / ulDivisor;
  1299. qwDividend.ulHi = qwDividend.ulHi % ulDivisor;
  1300. /* shift dividend left, until all in low dividend
  1301. * save low bits shifted out */
  1302. while (qwDividend.ulHi) {
  1303. ulBitsOut >>= 1;
  1304. if (qwDividend.ulLo & BIT0)
  1305. ulBitsOut |= BIT31;
  1306. qwDividend.ulLo >>= 1;
  1307. if (qwDividend.ulHi & BIT0)
  1308. qwDividend.ulLo |= BIT31;
  1309. qwDividend.ulHi >>= 1;
  1310. cShiftCount++;
  1311. }
  1312. /* check if shifted division is possible */
  1313. if (qwDividend.ulLo && ulDivisor > qwDividend.ulLo)
  1314. return 2;
  1315. /* do "shifted" divide
  1316. * again remainder stays in dividend for next divide */
  1317. pqwQuot->ulLo = qwDividend.ulLo / ulDivisor;
  1318. qwDividend.ulLo = qwDividend.ulLo % ulDivisor;
  1319. /* shift back
  1320. * - low bits back into dividend
  1321. * - quotient up */
  1322. qwDividend.ulLo <<= cShiftCount;
  1323. qwDividend.ulLo += (ulBitsOut >> (32 - cShiftCount));
  1324. pqwQuot->ulLo <<= cShiftCount;
  1325. /* do "regular" (non-shifted) low division
  1326. * add quotient to previous "shifted" quotient
  1327. */
  1328. pqwQuot->ulLo += qwDividend.ulLo / ulDivisor;
  1329. return 0;
  1330. }
  1331. /*********************************************************************
  1332. * Multiply 64bit by 32bit producing 64bit result.
  1333. * Input:
  1334. * qwFact64 -- 64bit factor in QWORD struct
  1335. * ulFact32 -- 32bit factor
  1336. * pqwResult -- ptr to QWORD struct for result
  1337. * Output:
  1338. * puts result *pqwResult qword
  1339. * returns 0 if completed successfully, 1 if overflow
  1340. *
  1341. *
  1342. */
  1343. #define BIT0 0x1L
  1344. #define BIT31 0x80000000L
  1345. int mult64_32 (QWORD qwFact64, ULONG ulFact32, PQWORD pqwResult)
  1346. {
  1347. pqwResult->ulLo = 0;
  1348. pqwResult->ulHi = 0;
  1349. while (1) {
  1350. /* if lowest bit in Fact32 set, add Fact64 to result */
  1351. if (ulFact32 & BIT0) {
  1352. /* add high bits, and check for overflow */
  1353. pqwResult->ulHi += qwFact64.ulHi;
  1354. if (qwFact64.ulHi > pqwResult->ulHi)
  1355. return 1;
  1356. /* add low bits, if overflow increment hi */
  1357. pqwResult->ulLo += qwFact64.ulLo;
  1358. if (qwFact64.ulLo > pqwResult->ulLo) {
  1359. /* check if inc will cause overflow */
  1360. if (pqwResult->ulHi == 0xfffffff)
  1361. return 1;
  1362. pqwResult->ulHi++;
  1363. }
  1364. }
  1365. /* shift 32 factor right, done when zero */
  1366. if (!(ulFact32 >>= 1))
  1367. return 0;
  1368. /* shift 64 factor left, return if overflow */
  1369. if (qwFact64.ulHi & BIT31)
  1370. return 1;
  1371. qwFact64.ulHi <<= 1;
  1372. /* move ulLo bit 32 to ulHi */
  1373. if (qwFact64.ulLo & BIT31)
  1374. qwFact64.ulHi |= 1L;
  1375. qwFact64.ulLo <<=1;
  1376. } /* end while until return */
  1377. }
  1378. /*********************************************************************
  1379. * Subtract two signed 64bit values
  1380. * Input:
  1381. * pqwResult -- ptr to QWORD struct for result
  1382. * qwSub1 -- 64bit to be subtracted from
  1383. * qwSub2 -- 64bit subtrahend
  1384. * Output:
  1385. * puts result *pqwResult
  1386. * returns 0 if completed successfully, 1 if overflow
  1387. */
  1388. int Sub64_64 (PQWORD pqwResult, QWORD qwFrom, QWORD qwSub)
  1389. {
  1390. /* subtract */
  1391. pqwResult->ulHi = qwFrom.ulHi - qwSub.ulHi;
  1392. pqwResult->ulLo = qwFrom.ulLo - qwSub.ulLo;
  1393. if (pqwResult->ulLo > qwFrom.ulLo) /* borrow from high? */
  1394. pqwResult->ulHi--;
  1395. /* overflow check */
  1396. if (pqwResult->ulHi < qwFrom.ulHi)
  1397. return 0; /* valid */
  1398. else if (pqwResult->ulHi > qwFrom.ulHi)
  1399. return 1; /* overflow, found in hi */
  1400. else if (pqwResult->ulLo > qwFrom.ulLo)
  1401. return 1; /* hi equal, overflow found in lo */
  1402. return 0; /* hi equal, no overflow in lo */
  1403. }
  1404. #endif
  1405. #if (defined (NTNAT) || defined (WIN32))
  1406. /***************************************************
  1407. NT native dll init routine
  1408. ****************************************************/
  1409. SHORT csTempCtr; /* a counter - had to make this global..compile fails */
  1410. ULONG culTemp; /* - do - */
  1411. NTSTATUS
  1412. TimerDllInitialize (
  1413. IN PVOID DllHandle,
  1414. ULONG Reason,
  1415. IN PCONTEXT Context OPTIONAL
  1416. )
  1417. {
  1418. DllHandle, Context; // avoid compiler warnings
  1419. if (Reason != DLL_PROCESS_ATTACH) { // if detaching return immediately
  1420. return TRUE;
  1421. }
  1422. for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
  1423. pTimer [csTempCtr].ulLo = 0L;
  1424. pTimer [csTempCtr].ulHi = 0L;
  1425. pTimer [csTempCtr].TUnits = TIMER_FREE;
  1426. }
  1427. bTimerInit = TRUE;
  1428. GetTimerFreq ();
  1429. ulTimerOverhead = CalibrateTimerForOverhead ();
  1430. /* the timer overhead will be placed in a global variable */
  1431. bCalibrated = TRUE;
  1432. return TRUE;
  1433. }
  1434. #endif