VirtualBox

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

Last change on this file since 16684 was 12548, checked in by vboxsync, 16 years ago

iprt/tstTSC: Extended it with an option (-f) for sampling the frequency instead of calcing the TSC drift.

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