VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/nt/nttimesources.cpp@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.5 KB
Line 
1/* $Id: nttimesources.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * Check the various time sources on Windows NT.
4 */
5
6/*
7 * Copyright (C) 2009-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/win/windows.h>
32
33#include <iprt/asm.h>
34#include <iprt/asm-amd64-x86.h>
35#include <iprt/errcore.h>
36#include <iprt/string.h>
37#include <iprt/test.h>
38
39
40/*********************************************************************************************************************************
41* Structures and Typedefs *
42*********************************************************************************************************************************/
43typedef struct _MY_KSYSTEM_TIME
44{
45 ULONG LowPart;
46 LONG High1Time;
47 LONG High2Time;
48} MY_KSYSTEM_TIME;
49
50typedef struct _MY_KUSER_SHARED_DATA
51{
52 ULONG TickCountLowDeprecated;
53 ULONG TickCountMultiplier;
54 volatile MY_KSYSTEM_TIME InterruptTime;
55 volatile MY_KSYSTEM_TIME SystemTime;
56 volatile MY_KSYSTEM_TIME TimeZoneBias;
57 /* The rest is not relevant. */
58} MY_KUSER_SHARED_DATA;
59
60/** The fixed pointer to the user shared data. */
61#define MY_USER_SHARED_DATA ((MY_KUSER_SHARED_DATA *)0x7ffe0000)
62
63/** Spins until GetTickCount() changes. */
64static void SpinUntilTick(void)
65{
66 /* spin till GetTickCount changes. */
67 DWORD dwMsTick = GetTickCount();
68 while (GetTickCount() == dwMsTick)
69 /* nothing */;
70}
71
72/** Delay function that tries to return right after GetTickCount changed. */
73static void DelayMillies(DWORD dwMsStart, DWORD cMillies)
74{
75 /* Delay cMillies - 1. */
76 Sleep(cMillies - 1);
77 while (GetTickCount() - dwMsStart < cMillies - 1U)
78 Sleep(1);
79
80 SpinUntilTick();
81}
82
83
84int main(int argc, char **argv)
85{
86 RT_NOREF1(argv);
87
88 /*
89 * Init, create a test instance and "parse" arguments.
90 */
91 RTTEST hTest;
92 int rc = RTTestInitAndCreate("nttimesources", &hTest);
93 if (rc)
94 return rc;
95 if (argc > 1)
96 {
97 RTTestFailed(hTest, "Syntax error! no arguments expected");
98 return RTTestSummaryAndDestroy(hTest);
99 }
100
101 /*
102 * Guess MHz using GetTickCount.
103 */
104 RTTestSub(hTest, "Guess MHz");
105 DWORD dwTickStart, dwTickEnd, cMsTicks;
106 uint64_t u64TscStart, u64TscEnd, cTscTicks;
107
108 /* get a good start time. */
109 SpinUntilTick();
110 do
111 {
112 dwTickStart = GetTickCount();
113 ASMCompilerBarrier();
114 ASMSerializeInstruction();
115 u64TscStart = ASMReadTSC();
116 ASMCompilerBarrier();
117 } while (GetTickCount() != dwTickStart);
118
119 /* delay a good while. */
120 DelayMillies(dwTickStart, 256);
121
122 /* get a good end time. */
123 do
124 {
125 dwTickEnd = GetTickCount();
126 ASMCompilerBarrier();
127 ASMSerializeInstruction();
128 u64TscEnd = ASMReadTSC();
129 ASMCompilerBarrier();
130 } while (GetTickCount() != dwTickEnd);
131 cMsTicks = dwTickEnd - dwTickStart;
132 cTscTicks = u64TscEnd - u64TscStart;
133
134 /* Calc an approximate TSC frequency:
135 cTscTicks / uTscHz = cMsTicks / 1000
136 1 / uTscHz = (cMsTicks / 1000) / cTscTicks
137 uTscHz = cTscTicks / (cMsTicks / 1000) */
138 uint64_t u64TscHz = (long double)cTscTicks / ((long double)cMsTicks / 1000.0);
139 if ( u64TscHz > _1M*3
140 && u64TscHz < _1T)
141 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "u64TscHz=%'llu", u64TscHz);
142 else
143 {
144 RTTestFailed(hTest, "u64TscHz=%'llu - out of range", u64TscHz);
145 u64TscHz = 0;
146 }
147
148
149 /*
150 * Pit GetTickCount, InterruptTime, Performance Counters and TSC against each other.
151 */
152 LARGE_INTEGER PrfHz;
153 LARGE_INTEGER PrfStart, PrfEnd, cPrfTicks;
154 LARGE_INTEGER IntStart, IntEnd, cIntTicks;
155 for (uint32_t i = 0; i < 7; i++)
156 {
157 RTTestSubF(hTest, "The whole bunch - pass #%u", i + 1);
158
159 if (!QueryPerformanceFrequency(&PrfHz))
160 {
161 RTTestFailed(hTest, "QueryPerformanceFrequency failed (%u)", GetLastError());
162 return RTTestSummaryAndDestroy(hTest);
163 }
164
165 /* get a good start time. */
166 SpinUntilTick();
167 do
168 {
169 IntStart.HighPart = MY_USER_SHARED_DATA->InterruptTime.High1Time;
170 IntStart.LowPart = MY_USER_SHARED_DATA->InterruptTime.LowPart;
171 dwTickStart = GetTickCount();
172 if (!QueryPerformanceCounter(&PrfStart))
173 {
174 RTTestFailed(hTest, "QueryPerformanceCounter failed (%u)", GetLastError());
175 return RTTestSummaryAndDestroy(hTest);
176 }
177 ASMCompilerBarrier();
178 ASMSerializeInstruction();
179 u64TscStart = ASMReadTSC();
180 ASMCompilerBarrier();
181 } while ( MY_USER_SHARED_DATA->InterruptTime.High2Time != IntStart.HighPart
182 || MY_USER_SHARED_DATA->InterruptTime.LowPart != IntStart.LowPart
183 || GetTickCount() != dwTickStart);
184
185 /* delay a good while. */
186 DelayMillies(dwTickStart, 256);
187
188 /* get a good end time. */
189 do
190 {
191 IntEnd.HighPart = MY_USER_SHARED_DATA->InterruptTime.High1Time;
192 IntEnd.LowPart = MY_USER_SHARED_DATA->InterruptTime.LowPart;
193 dwTickEnd = GetTickCount();
194 if (!QueryPerformanceCounter(&PrfEnd))
195 {
196 RTTestFailed(hTest, "QueryPerformanceCounter failed (%u)", GetLastError());
197 return RTTestSummaryAndDestroy(hTest);
198 }
199 ASMCompilerBarrier();
200 ASMSerializeInstruction();
201 u64TscEnd = ASMReadTSC();
202 ASMCompilerBarrier();
203 } while ( MY_USER_SHARED_DATA->InterruptTime.High2Time != IntEnd.HighPart
204 || MY_USER_SHARED_DATA->InterruptTime.LowPart != IntEnd.LowPart
205 || GetTickCount() != dwTickEnd);
206
207 cMsTicks = dwTickEnd - dwTickStart;
208 cTscTicks = u64TscEnd - u64TscStart;
209 cIntTicks.QuadPart = IntEnd.QuadPart - IntStart.QuadPart;
210 cPrfTicks.QuadPart = PrfEnd.QuadPart - PrfStart.QuadPart;
211
212 /* Recalc to micro seconds. */
213 uint64_t u64MicroSecMs = (uint64_t)cMsTicks * 1000;
214 uint64_t u64MicroSecTsc = u64TscHz ? (long double)cTscTicks / u64TscHz * 1000000 : u64MicroSecMs;
215 uint64_t u64MicroSecInt = cIntTicks.QuadPart / 10; /* 100ns units*/
216 uint64_t u64MicroSecPrf = (long double)cPrfTicks.QuadPart / PrfHz.QuadPart * 1000000;
217
218 /* check how much they differ using the millisecond tick count as the standard candle. */
219 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - GetTickCount\n", u64MicroSecMs, 0);
220
221 int64_t off = u64MicroSecTsc - u64MicroSecMs;
222 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - TSC\n", u64MicroSecTsc, off);
223 RTTEST_CHECK(hTest, RT_ABS(off) < 50000 /*us*/); /* some extra uncertainty with TSC. */
224
225 off = u64MicroSecInt - u64MicroSecMs;
226 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - InterruptTime\n", u64MicroSecInt, off);
227 RTTEST_CHECK(hTest, RT_ABS(off) < 25000 /*us*/);
228
229 off = u64MicroSecPrf - u64MicroSecMs;
230 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - QueryPerformanceCounter\n", u64MicroSecPrf, off);
231 RTTEST_CHECK(hTest, RT_ABS(off) < 25000 /*us*/);
232 }
233
234 return RTTestSummaryAndDestroy(hTest);
235}
236
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette