VirtualBox

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

Last change on this file since 44882 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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