Source code of Windows XP (NT5)
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.

633 lines
22 KiB

  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-1998";
  80. #ifdef SLOOP
  81. #define INCL_DOSINFOSEG
  82. #define INCL_DOSDEVICES
  83. #define INCL_DOSPROCESS
  84. #endif
  85. #include <nt.h>
  86. #include <ntrtl.h>
  87. #include <nturtl.h>
  88. #include <windows.h>
  89. #include "timing.h"
  90. /*****************************END OF INCLUDES*************************/
  91. #define ITER_FOR_OVERHEAD 250
  92. #define SUCCESS_OK 0
  93. #define ONE_MILLION 1000000L
  94. #define MICROSEC_FACTOR 1000000
  95. #define TIMER_FREQ 1193167L /* clock frequency - Hz */
  96. /*********************************************************************/
  97. Timer pTimer [MAX_TIMERS]; /* array of timer struct */
  98. BOOL bTimerInit = FALSE; /* TRUE indicates low level timer exists */
  99. ULONG ulTimerOverhead = 50000L; /* timer overhead stored here */
  100. BOOL bCalibrated = FALSE; /* TRUE subtracts overhead also */
  101. ULONG ulFreq; /* timer frequency */
  102. LONG aScaleValues[] = {1000000000L, 1000000L, 1000L, 1L, 10L, 1000L};
  103. /* this is the table for scaling the units */
  104. ULONG ulElapsedTime = 0L;
  105. /********************* internal unexported routines ***************/
  106. ULONG CalibrateTimerForOverhead (VOID);
  107. /*****************DEFINE VARIBLES AND PROTOTYPE FNS. FOR PLATFORMS*****/
  108. NTSYSAPI
  109. NTSTATUS
  110. NTAPI
  111. NtQueryPerformanceCounter (
  112. OUT PLARGE_INTEGER PerformanceCount,
  113. OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  114. );
  115. LARGE_INTEGER PerfFreq;
  116. LARGE_INTEGER CountCurrent;
  117. SHORT GetTimerFreq (VOID);
  118. /****************** internal unexported routines end***************/
  119. /*
  120. * Function - TimerOpen (EXPORTED)
  121. * Arguments -
  122. * (a) SHORT far * - address to which to return handle of
  123. * the timer object.
  124. * (b) TimerUnits - units in which to return time from
  125. * TimerRead. It is one of the enum
  126. * types defined in the header file.
  127. *
  128. * Returns - SHORT - 0 if handle was returned successfully
  129. * Else, an error code which may be one of:
  130. *
  131. * TIMERERR_NOT_AVAILABLE
  132. * TIMERERR_NO_MORE_HANDLES
  133. * TIMERERR_INVALID_UNITS
  134. *
  135. * Obtains the handle to a timer object after opening it.
  136. * Should precede any calls to timer manipulation. Checks
  137. * for validity of timer units.
  138. */
  139. SHORT
  140. TimerOpen (
  141. SHORT * phTimerHandle,
  142. _TimerUnits TimerUnits
  143. )
  144. {
  145. SHORT csTemp;
  146. if ((TimerUnits < KILOSECONDS)
  147. || (TimerUnits > NANOSECONDS)) /* out of the enum range */
  148. return (TIMERERR_INVALID_UNITS);
  149. if (!bTimerInit) /* set during dll initialization */
  150. return (TIMERERR_NOT_AVAILABLE);
  151. /* else check if any timers are not in use and return the first
  152. available timer handle....actually the index into the
  153. timer object array */
  154. for (csTemp = 0; csTemp < MAX_TIMERS; csTemp++) {
  155. if (pTimer [csTemp].TUnits == TIMER_FREE) {
  156. *phTimerHandle = csTemp; /* found a free timer. Return
  157. the handle */
  158. pTimer [csTemp].ulHi = pTimer[csTemp].ulLo = 0L;
  159. pTimer [csTemp].TUnits =
  160. TimerUnits; /* set the units for timer */
  161. return (SUCCESS_OK);
  162. }
  163. }
  164. /* if exec reached here, all timers are being used */
  165. return (TIMERERR_NO_MORE_HANDLES);
  166. }
  167. /*
  168. * Function - TimerInit (EXPORTED)
  169. * Arguments -
  170. * (a) SHORT - hTimerHandle
  171. *
  172. * Returns - SHORT - 0 if call successful
  173. * Else, an error code if handle invalid:
  174. *
  175. * TIMERERR_INVALID_HANDLE
  176. *
  177. * Calls the low-level timer and sets the ulHi and ulLo of the
  178. * chosen timer to the time returned by the timer. Should be
  179. * called after opening the timer with TimerOpen.
  180. */
  181. SHORT
  182. TimerInit (
  183. SHORT hTimerHandle
  184. )
  185. {
  186. NTSTATUS NtStatus;
  187. if ((hTimerHandle > MAX_TIMERS - 1) ||
  188. (pTimer [hTimerHandle].TUnits == TIMER_FREE))
  189. /* this timer has not been opened or does not exist. Return error */
  190. return (TIMERERR_INVALID_HANDLE);
  191. /* otherwise get the time from the low-level timer into
  192. the structure */
  193. NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
  194. pTimer [hTimerHandle].ulLo = CountCurrent.LowPart;
  195. pTimer [hTimerHandle].ulHi = CountCurrent.HighPart;
  196. /* this timer structure has all the information needed to compute
  197. the elapsed time. So return success, if there was no problem */
  198. return (SUCCESS_OK);
  199. }
  200. /*
  201. * Function - TimerRead (EXPORTED)
  202. * Arguments -
  203. * (a) SHORT - hTimerHandle
  204. *
  205. * Returns - ULONG - elapsed time since last call to TimerInit
  206. * if call successful.
  207. *
  208. * Else, an error code if handle invalid or output
  209. * overflow. The error code will be the same:
  210. *
  211. * TIMERERR_OVERFLOW (max possible ULONG)
  212. *
  213. * Calls the low-level timer. Uses the ulLo and ulHi from the
  214. * timer's structure and subtracts the current time from the
  215. * saved time. Uses ReturnElapsedTime (an external ASM proc)
  216. * to return the elapsed time taking into account the clock
  217. * frequency and the units for this timer. Each call to this
  218. * returns the time from the previous TimerInit.
  219. *
  220. * The user should interpret the return value sensibly to check
  221. * if the result is an error or a real value.
  222. */
  223. ULONG
  224. TimerRead (
  225. SHORT hTimerHandle
  226. )
  227. {
  228. NTSTATUS NtStatus;
  229. LARGE_INTEGER ElapsedTime, CountPrev, LargeOverhead;
  230. if ((hTimerHandle > MAX_TIMERS - 1)
  231. || (pTimer [hTimerHandle].TUnits == TIMER_FREE))
  232. /* this timer has not been opened or does not exist.
  233. Return TIMERERR_OVERFLOW ie. 0xffffffff, the max. possible
  234. ULONG. The user should interpret such a result sensibly */
  235. return (TIMERERR_OVERFLOW);
  236. NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
  237. CountPrev.LowPart = pTimer [hTimerHandle].ulLo;
  238. CountPrev.HighPart = (LONG) pTimer [hTimerHandle].ulHi;
  239. ElapsedTime.LowPart = CountCurrent.LowPart;
  240. ElapsedTime.HighPart = (LONG) CountCurrent.HighPart;
  241. /* everything is just fine, convert to double, subtract the times,
  242. divide by the frequency, convert to MICROSECONDS and return
  243. the elapsed time as a ULONG */
  244. /* convert to us., divide the count by the clock frequency that
  245. has already been obtained */
  246. ElapsedTime = RtlLargeIntegerSubtract (ElapsedTime, CountPrev);
  247. ElapsedTime = RtlExtendedIntegerMultiply (ElapsedTime, MICROSEC_FACTOR);
  248. ElapsedTime = RtlExtendedLargeIntegerDivide (ElapsedTime,
  249. PerfFreq.LowPart,
  250. NULL);
  251. // if the timer is not calibrated, set ulElapsedTime to be the
  252. // low part of ElapsedTime. This is because, we do not have to
  253. // do to any arithmetic to this before returning the value.
  254. if (!bCalibrated)
  255. ulElapsedTime = ElapsedTime.LowPart;
  256. /* this code is common for all platforms but OS2386. For Win3.x
  257. if VTD.386 has been installed, the code below should not matter,
  258. since we should have returned the time by now.
  259. The elapsed time will be scaled, overhead subtracted
  260. and the time returned */
  261. /* we have ulElapsedTime. Scale it and do the needful */
  262. /* divide or multiply by the scale factor */
  263. if (bCalibrated) {
  264. // Applications like the PROBE call TimerRead repeatedly
  265. // without calling TimerInit, for more than 70 minutes. This
  266. // screws up things. So treat everything as 64 bit numbers
  267. // until the very end.
  268. if ((ElapsedTime.LowPart < ulTimerOverhead) &&
  269. (!ElapsedTime.HighPart)) { // low part is lower than overhead
  270. // and high part is zero..then make
  271. // elapsed time 0. We don't want
  272. // negative numbers.
  273. ElapsedTime.HighPart = 0L;
  274. ElapsedTime.LowPart = 0L;
  275. }
  276. else { // subtract the overhead in tics before converting
  277. // to time units
  278. LargeOverhead.HighPart = 0L;
  279. LargeOverhead.LowPart = ulTimerOverhead;
  280. ElapsedTime = RtlLargeIntegerSubtract (ElapsedTime,
  281. LargeOverhead);
  282. }
  283. if (pTimer [hTimerHandle].TUnits <= MICROSECONDS) {
  284. ElapsedTime = RtlExtendedLargeIntegerDivide (
  285. ElapsedTime,
  286. aScaleValues [pTimer [hTimerHandle].TUnits],
  287. NULL
  288. );
  289. } else {
  290. ElapsedTime = RtlExtendedIntegerMultiply (
  291. ElapsedTime,
  292. aScaleValues [pTimer [hTimerHandle].TUnits]
  293. );
  294. }
  295. // scaling is done. Now get the time back into 32 bits. This
  296. // should fit.
  297. ulElapsedTime = ElapsedTime.LowPart;
  298. }
  299. if ((LONG) ulElapsedTime < 0L) /* if this guy is -ve, return a 0 */
  300. return (0L);
  301. return (ulElapsedTime);
  302. }
  303. /*
  304. * Function - TimerClose (EXPORTED)
  305. * Arguments -
  306. * (a) SHORT - hTimerHandle
  307. *
  308. * Returns - SHORT - 0 if call successful
  309. * Else, an error code if handle invalid:
  310. *
  311. * TIMERERR_INVALID_HANDLE
  312. *
  313. * Releases the timer for use by future TimerOpen calls.
  314. * Resets the elements of the timer structure, setting the
  315. * Timer's Units element to TIMER_FREE.
  316. */
  317. SHORT
  318. TimerClose (
  319. SHORT hTimerHandle
  320. )
  321. {
  322. if ((hTimerHandle > MAX_TIMERS - 1) ||
  323. (pTimer [hTimerHandle].TUnits == TIMER_FREE))
  324. /* error condition, wrong handle */
  325. return (TIMERERR_INVALID_HANDLE);
  326. /* otherwise, set the TimerUnits of this timer to TIMER_FREE,
  327. reset the other elements to zero and return */
  328. pTimer [hTimerHandle].TUnits = TIMER_FREE;
  329. pTimer [hTimerHandle].ulLo = 0L;
  330. pTimer [hTimerHandle].ulHi = 0L;
  331. return (SUCCESS_OK);
  332. }
  333. /*******************************************************************
  334. Added this routine TimerReport to report individual
  335. times. Bob Day requested that such a routine be
  336. created. It just maintains the time from the last
  337. TimerInit and TimerRead and also the last time returned.
  338. This routine copies this to a user specified buffer.
  339. Accepts - PSZ - a pointer to a buffer to print the data out
  340. SHORT - timer handle
  341. Returns - TRUE if Timer exists and is open
  342. - FALSE if Timer not opened
  343. *******************************************************************/
  344. BOOL
  345. FAR
  346. PASCAL
  347. TimerReport (
  348. PSZ pszReportString,
  349. SHORT hTimerHandle
  350. )
  351. {
  352. if (pTimer [hTimerHandle].TUnits == TIMER_FREE)
  353. return (FALSE);
  354. /* stored value is in pTimer[hTimerHandle].ulLo and .ulHi */
  355. /*
  356. wsprintf (pszReportString,
  357. "Init Count (tics) %lu:%lu Current Count (tics) %lu:%lu Returned Time %lu ",
  358. pTimer [hTimerHandle].ulHi,
  359. pTimer [hTimerHandle].ulLo, CountCurrent.HighPart,
  360. CountCurrent.LowPart,
  361. ulElapsedTime);
  362. */
  363. return (TRUE);
  364. }
  365. /*******************************************************************
  366. Added this routine TimerQueryPerformanceCounter to report
  367. current tic count at behest of NT GDI folks.
  368. Accepts - PQWORD - a pointer to a 64 bit struct. that will
  369. contain tic count on return.
  370. PQWORD [OPTIONAL) - a pointer to a 64 bit struct. that will
  371. contain frequency on return.
  372. Returns - None.
  373. *******************************************************************/
  374. VOID
  375. FAR
  376. PASCAL
  377. TimerQueryPerformanceCounter (
  378. PQWORD pqTic,
  379. PQWORD pqFreq OPTIONAL
  380. )
  381. {
  382. LARGE_INTEGER TempTic, TempFreq;
  383. // call the NT API to do the needful and return.
  384. NtQueryPerformanceCounter (&TempTic, &TempFreq);
  385. pqTic->LowPart = TempTic.LowPart;
  386. pqTic->HighPart = TempTic.HighPart;
  387. pqFreq->LowPart = TempFreq.LowPart;
  388. pqFreq->HighPart = TempFreq.HighPart;
  389. return;
  390. }
  391. /*******************************************************************
  392. Added this routine TimerConvertTicsToUSec to return
  393. time in usecs. for a given elapsed tic count and freq.
  394. Accepts - ULONG - Elapsed Tic Count.
  395. ULONG - Frequency.
  396. Returns - Elapsed Time in usecs. as a ULONG.
  397. - Zero if input freq. is zero.
  398. *******************************************************************/
  399. ULONG
  400. TimerConvertTicsToUSec (
  401. ULONG ulElapsedTics,
  402. ULONG ulInputFreq
  403. )
  404. {
  405. LARGE_INTEGER ElapsedTime;
  406. ULONG ulRemainder = 0L;
  407. // if the person gives me a zero freq, return him a zero.
  408. // Let him tear his hair.
  409. if (!ulInputFreq)
  410. return 0L;
  411. // multiply tics by a million and divide by the frequency.
  412. ElapsedTime = RtlEnlargedIntegerMultiply (ulElapsedTics, MICROSEC_FACTOR);
  413. ElapsedTime = RtlExtendedLargeIntegerDivide (ElapsedTime,
  414. ulInputFreq,
  415. &ulRemainder);
  416. ElapsedTime.LowPart += (ulRemainder > (ulInputFreq / 2L));
  417. return (ElapsedTime.LowPart) ; /* get the result into a ULONG */
  418. }
  419. /**************** ROUTINES NOT EXPORTED, FOLLOW ************************/
  420. /*
  421. * Function - CalibrateTimerForOverhead (NOT EXPORTED)
  422. * Arguments - None
  423. * Returns - ULONG
  424. *
  425. * Calls TimerElapsedTime a few times to compute the expected
  426. * mean. Calls TimerElapsedTime more number of times and
  427. * averages the mean out of those calls that did not exceed
  428. * the expected mean by 15%.
  429. */
  430. ULONG
  431. CalibrateTimerForOverhead (VOID)
  432. {
  433. ULONG ulOverhead [ITER_FOR_OVERHEAD];
  434. ULONG ulTempTotal = 0L;
  435. ULONG ulExpectedValue = 0L;
  436. SHORT csIter;
  437. SHORT csNoOfSamples = ITER_FOR_OVERHEAD;
  438. SHORT hTimerHandle;
  439. if (TimerOpen (&hTimerHandle, MICROSECONDS)) /* open failed. Return 0 */
  440. return (0L);
  441. for (csIter = 0; csIter < 5; csIter++) {
  442. TimerInit (hTimerHandle);
  443. ulOverhead [csIter] = TimerRead (hTimerHandle);
  444. /* if negative, make zero */
  445. if (((LONG) ulOverhead [csIter]) < 0)
  446. ulOverhead [csIter] = 0L;
  447. }
  448. /* The get elapsed time function has been called 6 times.
  449. The idea is to calculate the expected mean, then call
  450. TimerElapsedTime a bunch of times and throw away all times
  451. that are 15% larger than this mean. This would give a
  452. really good overhead time */
  453. for (csIter = 0; csIter < 5; csIter++ )
  454. ulTempTotal += ulOverhead [csIter];
  455. ulExpectedValue = ulTempTotal / 5;
  456. for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++) {
  457. TimerInit (hTimerHandle);
  458. ulOverhead [csIter] = TimerRead (hTimerHandle);
  459. /* if negative, make zero */
  460. if (((LONG) ulOverhead [csIter]) < 0)
  461. ulOverhead [csIter] = 0L;
  462. }
  463. ulTempTotal = 0L; /* reset this value */
  464. for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++ ) {
  465. if (ulOverhead [csIter] <= (ULONG) (115L * ulExpectedValue/100L))
  466. /* include all samples that is < 115% of ulExpectedValue */
  467. ulTempTotal += ulOverhead [csIter];
  468. else
  469. /* ignore this sample and dec. sample count */
  470. csNoOfSamples--;
  471. }
  472. TimerClose (hTimerHandle);
  473. if (csNoOfSamples == 0) /* no valid times. Return a 0 for overhead */
  474. return (0L);
  475. return (ulTempTotal/csNoOfSamples);
  476. }
  477. /*
  478. * Function - GetTimerFreq (NOT EXPORTED)
  479. *
  480. * Arguments - None
  481. *
  482. *
  483. * Return - 0 if successful or an error code if timer not aailable
  484. *
  485. * Calls the function to return freq
  486. *
  487. */
  488. SHORT
  489. GetTimerFreq (VOID)
  490. {
  491. LARGE_INTEGER PerfCount, Freq;
  492. NTSTATUS NtStatus;
  493. NtStatus = NtQueryPerformanceCounter (&PerfCount, &Freq);
  494. if ((Freq.LowPart == 0L) && (Freq.HighPart == 0L))
  495. /* frequency of zero implies timer not available */
  496. return (TIMERERR_NOT_AVAILABLE);
  497. PerfFreq.LowPart = Freq.LowPart;
  498. PerfFreq.HighPart = (LONG) Freq.HighPart;
  499. return 0;
  500. }
  501. /***************************************************
  502. NT native dll init routine
  503. ****************************************************/
  504. SHORT csTempCtr; /* a counter - had to make this global..compile fails */
  505. ULONG culTemp; /* - do - */
  506. NTSTATUS
  507. TimerDllInitialize (
  508. IN PVOID DllHandle,
  509. ULONG Reason,
  510. IN PCONTEXT Context OPTIONAL
  511. )
  512. {
  513. DllHandle, Context; // avoid compiler warnings
  514. if (Reason != DLL_PROCESS_ATTACH) { // if detaching return immediately
  515. return TRUE;
  516. }
  517. for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
  518. pTimer [csTempCtr].ulLo = 0L;
  519. pTimer [csTempCtr].ulHi = 0L;
  520. pTimer [csTempCtr].TUnits = TIMER_FREE;
  521. }
  522. bTimerInit = TRUE;
  523. GetTimerFreq ();
  524. ulTimerOverhead = CalibrateTimerForOverhead ();
  525. /* the timer overhead will be placed in a global variable */
  526. bCalibrated = TRUE;
  527. return TRUE;
  528. }