VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstTSC.cpp@ 86715

Last change on this file since 86715 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.6 KB
Line 
1/* $Id: tstTSC.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * IPRT Testcase - SMP TSC testcase.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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/asm-amd64-x86.h>
32#include <iprt/asm.h>
33#include <iprt/getopt.h>
34#include <iprt/initterm.h>
35#include <iprt/mp.h>
36#include <iprt/stream.h>
37#include <iprt/string.h>
38#include <iprt/thread.h>
39#include <iprt/time.h>
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45typedef struct TSCDATA
46{
47 /** The TSC. */
48 uint64_t volatile TSC;
49 /** The APIC ID. */
50 uint8_t volatile u8ApicId;
51 /** Did it succeed? */
52 bool volatile fRead;
53 /** Did it fail? */
54 bool volatile fFailed;
55 /** The thread handle. */
56 RTTHREAD Thread;
57} TSCDATA, *PTSCDATA;
58
59
60/*********************************************************************************************************************************
61* Global Variables *
62*********************************************************************************************************************************/
63/** The number of CPUs waiting on their user event semaphore. */
64static volatile uint32_t g_cWaiting;
65/** The number of CPUs ready (in spin) to do the TSC read. */
66static volatile uint32_t g_cReady;
67/** The variable the CPUs are spinning on.
68 * 0: Spin.
69 * 1: Go ahead.
70 * 2: You're too late, back to square one. */
71static volatile uint32_t g_u32Go;
72/** The number of CPUs that managed to read the TSC. */
73static volatile uint32_t g_cRead;
74/** The number of CPUs that failed to read the TSC. */
75static volatile uint32_t g_cFailed;
76
77/** Indicator forcing the threads to quit. */
78static volatile bool g_fDone;
79
80
81/*********************************************************************************************************************************
82* Internal Functions *
83*********************************************************************************************************************************/
84static DECLCALLBACK(int) ThreadFunction(RTTHREAD Thread, void *pvUser);
85
86
87/**
88 * Thread function for catching the other cpus.
89 *
90 * @returns VINF_SUCCESS (we don't care).
91 * @param Thread The thread handle.
92 * @param pvUser PTSCDATA.
93 */
94static DECLCALLBACK(int) ThreadFunction(RTTHREAD Thread, void *pvUser)
95{
96 PTSCDATA pTscData = (PTSCDATA)pvUser;
97
98 while (!g_fDone)
99 {
100 /*
101 * Wait.
102 */
103 ASMAtomicIncU32(&g_cWaiting);
104 RTThreadUserWait(Thread, RT_INDEFINITE_WAIT);
105 RTThreadUserReset(Thread);
106 ASMAtomicDecU32(&g_cWaiting);
107 if (g_fDone)
108 break;
109
110 /*
111 * Spin.
112 */
113 ASMAtomicIncU32(&g_cReady);
114 while (!g_fDone)
115 {
116 const uint8_t ApicId1 = ASMGetApicId();
117 const uint64_t TSC1 = ASMReadTSC();
118 const uint32_t u32Go = g_u32Go;
119 if (u32Go == 0)
120 continue;
121
122 if (u32Go == 1)
123 {
124 /* do the reading. */
125 const uint8_t ApicId2 = ASMGetApicId();
126 const uint64_t TSC2 = ASMReadTSC();
127 const uint8_t ApicId3 = ASMGetApicId();
128 const uint64_t TSC3 = ASMReadTSC();
129 const uint8_t ApicId4 = ASMGetApicId();
130
131 if ( ApicId1 == ApicId2
132 && ApicId1 == ApicId3
133 && ApicId1 == ApicId4
134 && TSC3 - TSC1 < 2250 /* WARNING: This is just a guess, increase if it doesn't work for you. */
135 && TSC2 - TSC1 < TSC3 - TSC1
136 )
137 {
138 /* succeeded. */
139 pTscData->TSC = TSC2;
140 pTscData->u8ApicId = ApicId1;
141 pTscData->fFailed = false;
142 pTscData->fRead = true;
143 ASMAtomicIncU32(&g_cRead);
144 break;
145 }
146 }
147
148 /* failed */
149 pTscData->fFailed = true;
150 pTscData->fRead = false;
151 ASMAtomicIncU32(&g_cFailed);
152 break;
153 }
154 }
155
156 return VINF_SUCCESS;
157}
158
159static int tstTSCCalcDrift(void)
160{
161 /*
162 * This is only relevant to on SMP systems.
163 */
164 const unsigned cCpus = RTMpGetOnlineCount();
165 if (cCpus <= 1)
166 {
167 RTPrintf("tstTSC: SKIPPED - Only relevant on SMP systems\n");
168 return 0;
169 }
170
171 /*
172 * Create the threads.
173 */
174 static TSCDATA s_aData[254];
175 uint32_t i;
176 if (cCpus > RT_ELEMENTS(s_aData))
177 {
178 RTPrintf("tstTSC: FAILED - too many CPUs (%u)\n", cCpus);
179 return 1;
180 }
181
182 /* ourselves. */
183 s_aData[0].Thread = RTThreadSelf();
184
185 /* the others */
186 for (i = 1; i < cCpus; i++)
187 {
188 int rc = RTThreadCreate(&s_aData[i].Thread, ThreadFunction, &s_aData[i], 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "OTHERCPU");
189 if (RT_FAILURE(rc))
190 {
191 RTPrintf("tstTSC: FAILURE - RTThreatCreate failed when creating thread #%u, rc=%Rrc!\n", i, rc);
192 ASMAtomicXchgSize(&g_fDone, true);
193 while (i-- > 1)
194 {
195 RTThreadUserSignal(s_aData[i].Thread);
196 RTThreadWait(s_aData[i].Thread, 5000, NULL);
197 }
198 return 1;
199 }
200 }
201
202 /*
203 * Retry until we get lucky (or give up).
204 */
205 for (unsigned cTries = 0; ; cTries++)
206 {
207 if (cTries > 10240)
208 {
209 RTPrintf("tstTSC: FAILURE - %d attempts, giving.\n", cTries);
210 break;
211 }
212
213 /*
214 * Wait for the other threads to get ready (brute force active wait, I'm lazy).
215 */
216 i = 0;
217 while (g_cWaiting < cCpus - 1)
218 {
219 if (i++ > _2G32)
220 break;
221 RTThreadSleep(i & 0xf);
222 }
223 if (g_cWaiting != cCpus - 1)
224 {
225 RTPrintf("tstTSC: FAILURE - threads failed to get waiting (%d != %d (i=%d))\n", g_cWaiting + 1, cCpus, i);
226 break;
227 }
228
229 /*
230 * Send them spinning.
231 */
232 ASMAtomicXchgU32(&g_cReady, 0);
233 ASMAtomicXchgU32(&g_u32Go, 0);
234 ASMAtomicXchgU32(&g_cRead, 0);
235 ASMAtomicXchgU32(&g_cFailed, 0);
236 for (i = 1; i < cCpus; i++)
237 {
238 ASMAtomicXchgSize(&s_aData[i].fFailed, false);
239 ASMAtomicXchgSize(&s_aData[i].fRead, false);
240 ASMAtomicXchgU8(&s_aData[i].u8ApicId, 0xff);
241
242 int rc = RTThreadUserSignal(s_aData[i].Thread);
243 if (RT_FAILURE(rc))
244 RTPrintf("tstTSC: WARNING - RTThreadUserSignal(%#u) -> rc=%Rrc!\n", i, rc);
245 }
246
247 /* wait for them to get ready. */
248 i = 0;
249 while (g_cReady < cCpus - 1)
250 {
251 if (i++ > _2G32)
252 break;
253 }
254 if (g_cReady != cCpus - 1)
255 {
256 RTPrintf("tstTSC: FAILURE - threads failed to get ready (%d != %d, i=%d)\n", g_cWaiting + 1, cCpus, i);
257 break;
258 }
259
260 /*
261 * Flip the "go" switch and do our readings.
262 * We give the other threads the slack it takes to two extra TSC and APIC ID reads.
263 */
264 const uint8_t ApicId1 = ASMGetApicId();
265 const uint64_t TSC1 = ASMReadTSC();
266 ASMAtomicXchgU32(&g_u32Go, 1);
267 const uint8_t ApicId2 = ASMGetApicId();
268 const uint64_t TSC2 = ASMReadTSC();
269 const uint8_t ApicId3 = ASMGetApicId();
270 const uint64_t TSC3 = ASMReadTSC();
271 const uint8_t ApicId4 = ASMGetApicId();
272 const uint64_t TSC4 = ASMReadTSC();
273 ASMAtomicXchgU32(&g_u32Go, 2);
274 const uint8_t ApicId5 = ASMGetApicId();
275 const uint64_t TSC5 = ASMReadTSC();
276 const uint8_t ApicId6 = ASMGetApicId();
277
278 /* Compose our own result. */
279 if ( ApicId1 == ApicId2
280 && ApicId1 == ApicId3
281 && ApicId1 == ApicId4
282 && ApicId1 == ApicId5
283 && ApicId1 == ApicId6
284 && TSC5 - TSC1 < 2750 /* WARNING: This is just a guess, increase if it doesn't work for you. */
285 && TSC4 - TSC1 < TSC5 - TSC1
286 && TSC3 - TSC1 < TSC4 - TSC1
287 && TSC2 - TSC1 < TSC3 - TSC1
288 )
289 {
290 /* succeeded. */
291 s_aData[0].TSC = TSC2;
292 s_aData[0].u8ApicId = ApicId1;
293 s_aData[0].fFailed = false;
294 s_aData[0].fRead = true;
295 ASMAtomicIncU32(&g_cRead);
296 }
297 else
298 {
299 /* failed */
300 s_aData[0].fFailed = true;
301 s_aData[0].fRead = false;
302 ASMAtomicIncU32(&g_cFailed);
303 }
304
305 /*
306 * Wait a little while to let the other ones to finish.
307 */
308 i = 0;
309 while (g_cRead + g_cFailed < cCpus)
310 {
311 if (i++ > _2G32)
312 break;
313 if (i > _1M)
314 RTThreadSleep(i & 0xf);
315 }
316 if (g_cRead + g_cFailed != cCpus)
317 {
318 RTPrintf("tstTSC: FAILURE - threads failed to complete reading (%d + %d != %d)\n", g_cRead, g_cFailed, cCpus);
319 break;
320 }
321
322 /*
323 * If everone succeeded, print the results.
324 */
325 if (!g_cFailed)
326 {
327 /* sort it by apic id first. */
328 bool fDone;
329 do
330 {
331 for (i = 1, fDone = true; i < cCpus; i++)
332 if (s_aData[i - 1].u8ApicId > s_aData[i].u8ApicId)
333 {
334 TSCDATA Tmp = s_aData[i - 1];
335 s_aData[i - 1] = s_aData[i];
336 s_aData[i] = Tmp;
337 fDone = false;
338 }
339 } while (!fDone);
340
341 RTPrintf(" # ID TSC delta0 (decimal)\n"
342 "-----------------------------------------\n");
343 RTPrintf("%2d %02x %RX64\n", 0, s_aData[0].u8ApicId, s_aData[0].TSC);
344 for (i = 1; i < cCpus; i++)
345 RTPrintf("%2d %02x %RX64 %s%lld\n", i, s_aData[i].u8ApicId, s_aData[i].TSC,
346 s_aData[i].TSC > s_aData[0].TSC ? "+" : "", s_aData[i].TSC - s_aData[0].TSC);
347 RTPrintf("(Needed %u attempt%s.)\n", cTries + 1, cTries ? "s" : "");
348 break;
349 }
350 }
351
352 /*
353 * Destroy the threads.
354 */
355 ASMAtomicXchgSize(&g_fDone, true);
356 for (i = 0; i < cCpus; i++)
357 if (s_aData[i].Thread != RTThreadSelf())
358 {
359 int rc = RTThreadUserSignal(s_aData[i].Thread);
360 if (RT_FAILURE(rc))
361 RTPrintf("tstTSC: WARNING - RTThreadUserSignal(%#u) -> rc=%Rrc! (2)\n", i, rc);
362 }
363 for (i = 0; i < cCpus; i++)
364 if (s_aData[i].Thread != RTThreadSelf())
365 {
366 int rc = RTThreadWait(s_aData[i].Thread, 5000, NULL);
367 if (RT_FAILURE(rc))
368 RTPrintf("tstTSC: WARNING - RTThreadWait(%#u) -> rc=%Rrc!\n", i, rc);
369 }
370
371 return g_cFailed != 0 || g_cRead != cCpus;
372}
373
374
375static int tstTSCCalcFrequency(uint32_t cMsDuration)
376{
377 /*
378 * Sample the TSC and time, sleep the requested time and calc the deltas.
379 */
380 uint64_t uNanoTS = RTTimeSystemNanoTS();
381 uint64_t uTSC = ASMReadTSC();
382 RTThreadSleep(cMsDuration);
383 uNanoTS = RTTimeSystemNanoTS() - uNanoTS;
384 uTSC = ASMReadTSC() - uTSC;
385
386 /*
387 * Calc the frequency.
388 */
389 RTPrintf("tstTSC: %RU64 ticks in %RU64 ns\n", uTSC, uNanoTS);
390 uint64_t cHz = (uint64_t)(uTSC / ((long double)uNanoTS / (long double)1000000000));
391 RTPrintf("tstTSC: Frequency %RU64 Hz", cHz);
392 if (cHz > _1G)
393 {
394 cHz += _1G / 20;
395 RTPrintf(" %RU64.%RU64 GHz", cHz / _1G, (cHz % _1G) / (_1G / 10));
396 }
397 else if (cHz > _1M)
398 {
399 cHz += _1M / 20;
400 RTPrintf(" %RU64.%RU64 MHz", cHz / _1M, (cHz % _1M) / (_1M / 10));
401 }
402 RTPrintf("\n");
403 return 0;
404}
405
406
407int main(int argc, char **argv)
408{
409 RTR3InitExe(argc, &argv, 0);
410
411 /*
412 * Parse arguments.
413 */
414 bool fCalcFrequency = false;
415 uint32_t cMsDuration = 1000; /* 1 sec */
416 static const RTGETOPTDEF s_aOptions[] =
417 {
418 { "--duration", 'd', RTGETOPT_REQ_UINT32 },
419 { "--calc-frequency", 'f', RTGETOPT_REQ_NOTHING },
420 };
421 int ch;
422 RTGETOPTUNION Value;
423 RTGETOPTSTATE GetState;
424 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
425 while ((ch = RTGetOpt(&GetState, &Value)))
426 switch (ch)
427 {
428 case 'd': cMsDuration = Value.u32;
429 break;
430
431 case 'f': fCalcFrequency = true;
432 break;
433
434 case 'h':
435 RTPrintf("usage: tstTSC\n"
436 " or: tstTSC <-f|--calc-frequency> [--duration|-d ms]\n");
437 return 1;
438
439 case 'V':
440 RTPrintf("$Revision: 82968 $\n");
441 return 0;
442
443 default:
444 return RTGetOptPrintError(ch, &Value);
445 }
446
447 if (fCalcFrequency)
448 return tstTSCCalcFrequency(cMsDuration);
449 return tstTSCCalcDrift();
450}
451
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